DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: medium
Invalid

Gas Limit: Multiple Calls Leading to Out-of-Gas Errors

Summary

The createTradingAccountAndMulticall function may run into out-of-gas errors due to performing multiple calls within a loop, especially if the number of calls is large or if individual calls are gas-intensive.

Vulnerability Details

Description:

The function is designed to create a new trading account and then execute multiple delegate calls based on the provided data payload. Each delegate call consumes gas, and the total gas consumption of all calls combined can exceed the gas limit of a single transaction, resulting in an out-of-gas error.

/// @notice Creates a new trading account and multicalls using the provided data payload.
/// @param data The data payload to be multicalled.
/// @return results The array of results of the multicall.
function createTradingAccountAndMulticall(
bytes[] calldata data,
bytes memory referralCode,
bool isCustomReferralCode
)
external
payable
virtual
returns (bytes[] memory results)
{
uint128 tradingAccountId = createTradingAccount(referralCode, isCustomReferralCode);
results = new bytes[](data.length);
for (uint256 i; i < data.length; i++) {
bytes memory dataWithAccountId = bytes.concat(data[i][0:4], abi.encode(tradingAccountId), data[i][4:]);
(bool success, bytes memory result) = address(this).delegatecall(dataWithAccountId);
if (!success) {
uint256 len = result.length;
assembly {
revert(add(result, 0x20), len)
}
}
results[i] = result;
}
}

Root Cause:

The gas limit for a single transaction is finite. When the function iterates over the data array, it executes multiple delegate calls. If the data array contains a large number of entries or if any individual delegate call requires a significant amount of gas, the cumulative gas consumption can surpass the transaction gas limit.

Proof of concept

copy and paste into createTradingAccountAndMulticall.t.sol and create event log_named_uint and log_named_bytes

function test_GasConsumptionWithVaryingDataArraySizes() external {
uint256[] memory sizes = new uint256[](5);
sizes[0] = 10;
sizes[1] = 100;
sizes[2] = 1000;
sizes[3] = 10000;
sizes[4] = 100000; // Large number to cause out-of-gas error
for (uint256 j = 0; j < sizes.length; j++) {
bytes[] memory data = new bytes[](sizes[j]);
for (uint256 i = 0; i < sizes[j]; i++) {
data[i] = abi.encodeWithSelector(TradingAccountBranch.depositMargin.selector, address(usdc), uint256(1));
}
uint256 gasStart = gasleft();
try perpsEngine.createTradingAccountAndMulticall(data, bytes(""), false) {
uint256 gasUsed = gasStart - gasleft();
emit log_named_uint("Gas used for data array size:", sizes[j]);
emit log_named_uint("Gas used :", gasUsed);
} catch (bytes memory reason) {
uint256 gasUsed = gasStart - gasleft();
emit log_named_uint("Gas used for data array size (failed):", sizes[j]);
emit log_named_uint("Gas used (failed):", gasUsed);
emit log_named_bytes("Revert reason :", reason);
}
}
}
function test_DosAttackUsingLargeDataArray2() external {
// Set up a very large data array
uint256 largeArraySize = 100000; // Large number to cause out-of-gas error
bytes[] memory data = new bytes[](largeArraySize);
for (uint256 i = 0; i < largeArraySize; i++) {
data[i] = abi.encodeWithSelector(TradingAccountBranch.depositMargin.selector, address(usdc), uint256(1));
}
// Expecting out-of-gas error due to large data array
try perpsEngine.createTradingAccountAndMulticall(data, bytes(""), false) {
// Should not reach here
assertTrue(false, "Expected out-of-gas error, but function succeeded");
} catch (bytes memory reason) {
// Ensure the error is due to out-of-gas
assertTrue(keccak256(reason) == keccak256(""), "Expected out-of-gas error");
}
}

Impact

  • Transaction Failure: The entire transaction will fail if the gas limit is exceeded, reverting all state changes made within the function.

  • Service Disruption: Users may experience failures and be unable to create trading accounts or perform multicalls, leading to potential service disruptions and a negative user experience.

  • Increased Costs: Repeatedly encountering out-of-gas errors can increase costs for users, as they have to pay for failed transactions.

Tools Used

Manual Review

Recommendations

Verify the data length by limiting it to certain values or Implement gas estimation for each call and split the calls into multiple transactions if the estimated gas exceeds a safe threshold.

Updates

Lead Judging Commences

inallhonesty Lead Judge
about 1 year ago
inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.