Platform: Code4rena
Start Date: 01/09/2023
Pot Size: $36,500 USDC
Total HM: 4
Participants: 70
Period: 6 days
Judge: kirk-baird
Id: 281
League: ETH
Rank: 30/70
Findings: 2
Award: $130.92
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: c3phas
Also found by: 0x11singh99, 0xhex, 0xta, Eurovickk, K42, MohammedRizwan, SAAJ, SAQ, SY_S, adriro, albahaca, castle_chain, jeffy, kaveyjoe, matrix_0wl, naman1778, petrichor, wahedtalash77, ybansal2403, zabihullahazadzoi
112.0737 USDC - $112.07
Possible Optimization 1 =
immutable
for destChainToContractAddr mapping if the mapping
is not expected to change after contract deployment.Here is the optimized code snippet:
mapping(string => string) public immutable destChainToContractAddr;
SLOAD
opcode savings per access.Possible Optimization 2 =
SSTORE
operations.Here is the optimized code:
function burnAndCallAxelar( uint256 amount, string calldata destinationChain, uint256 newNonce ) external payable whenNotPaused { // ... existing logic nonce = newNonce; }
SSTORE
operations.Possible Optimization 3 =
msg.sender
and msg.value
into a single struct
to reduce stack depth.After Optimization:
struct MsgData { address sender; uint256 value; } function burnAndCallAxelar( uint256 amount, string calldata destinationChain, MsgData calldata msgData ) external payable whenNotPaused { // Then use msgData.sender and msgData.value instead of msg.sender and msg.value here }
Possible Optimization 4 =
bytes32
for destinationChain instead of string
to save gas on storage and comparison.After Optimization:
mapping(bytes32 => string) public destChainToContractAddr; function burnAndCallAxelar( uint256 amount, bytes32 destinationChain ) external payable whenNotPaused { // ... }
SSTORE
costs 20,000 gas for a new value and 5,000 gas for updating, while SLOAD
costs 800 gas).Possible Optimization 5 =
call
opcode directly instead of looping through ExCallData[].After Optimization:
function multiexcall( ExCallData[] calldata exCallData ) external payable override onlyOwner returns (bytes[] memory results) { results = new bytes[](exCallData.length); for (uint256 i = 0; i < exCallData.length; ++i) { (bool success, bytes memory ret) = address(exCallData[i].target).call{ value: exCallData[i].value }(exCallData[i].data); require(success, "Call Failed"); results[i] = ret; } }
Possible Optimization 1 =
staticcall
for read-only external
calls like ALLOWLIST.isAllowed(), as staticcall
is cheaper than a regular call
for read-only operations.After Optimization:
(bool success, bytes memory data) = address(ALLOWLIST).staticcall(abi.encodeWithSignature("isAllowed(address)", txn.sender));
Possible Optimization 2 =
After Optimization:
struct Threshold { uint128 amount; uint128 numberOfApprovalsNeeded; } struct TxnThreshold { uint128 numberOfApprovalsNeeded; address[] approvers; }
SSTORE
operations.Possible Optimization 3 =
bytes32
can save gas on storage and comparisons.After Optimization:
function _execute( bytes32 srcChain, bytes32 srcAddr, bytes calldata payload ) internal override whenNotPaused { // ... }
Possible Optimization 4 =
mapping
with a struct
combining both.After Optimization:
struct TxData { Transaction txn; TxnThreshold threshold; } mapping(bytes32 => TxData) public txnData;
SSTORE
operations.Possible Optimization 5 =
After Optimization:
function _execute( // ... existing parameters ) internal override whenNotPaused { // ... existing logic isSpentNonce[chainToApprovedSender[srcChain]][nonce] = true; txnHashToTransaction[txnHash] = Transaction(srcSender, amt); // ... existing logic }
SSTORE
operations.Possible Optimization 1 =
immutable
to save gas on SLOAD
operations, if these addresses are not expected to change after contract deployment.After Optimization:
IRWADynamicOracle public immutable oracle; IUSDY public immutable usdy;
SLOAD
access.Possible Optimization 2 =
After Optimization:
function _mintShares(address _recipient, uint256 _sharesAmount) internal whenNotPaused returns (uint256) { // ... existing logic totalShares += _sharesAmount; shares[_recipient] += _sharesAmount; // ... existing logic }
SSTORE
operations.Possible Optimization 3 =
staticcall
for read-only external calls like oracle.getPrice() also, as staticcall
is cheaper than a regular call.After Optimization:
(bool success, bytes memory data) = address(oracle).staticcall(abi.encodeWithSignature("getPrice()"));
Possible Optimization 1 =
block.timestamp
is accessed in the getPrice() function.After Optimization:
function getPrice() public view whenNotPaused returns (uint256 price) { uint256 currentTimestamp = block.timestamp; // Cache block.timestamp uint256 length = ranges.length; for (uint256 i = 0; i < length; ++i) { Range storage range = ranges[(length - 1) - i]; if (range.start <= currentTimestamp) { if (range.end <= currentTimestamp) { return derivePrice(range, range.end - 1); } else { return derivePrice(range, currentTimestamp); } } } }
Possible Optimization 2 =
struct
packing to reduce storage costs for the Range struct, assuming the values for start, end, dailyInterestRate, and prevRangeClosePrice fit within 64 bits. However, you must ensure that these fields will never exceed the 64-bit limit during the contract's lifetime.After Optimization:
struct Range { uint64 start; uint64 end; uint64 dailyInterestRate; uint64 prevRangeClosePrice; }
#0 - c4-pre-sort
2023-09-08T14:27:35Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-09-24T06:08:55Z
kirk-baird marked the issue as grade-a
#2 - c4-sponsor
2023-09-27T21:27:43Z
ali2251 (sponsor) acknowledged
🌟 Selected for report: catellatech
Also found by: 0xAsen, 0xE1D, 0xStalin, 0xmystery, Breeje, Bube, DedOhWale, JayShreeRAM, K42, Krace, castle_chain, hals, hunter_w3b, kaveyjoe, m4ttm, mahdikarimi, nirlin, peanuts, sandy
18.8458 USDC - $18.85
Key Contracts:
Inter-Contract Communication:
Security:
Asset Transfer:
Token Management:
Role-Based Access Control:
Specific Risks and Mitigations in Key Contracts:
Here are function interaction graphs for each contract I made to better visualize interactions:
Link to Graph for SourceBridge.sol.
Link to Graph for DestinationBridge.sol.
Link to Graph for rUSDYFactory.sol.
Link to Graph for RWADynamicOracle.sol.
Link to Graph for IRWADynamicOracle.sol.
20 hours
#0 - c4-pre-sort
2023-09-08T14:43:59Z
raymondfam marked the issue as sufficient quality report
#1 - c4-judge
2023-09-24T07:12:35Z
kirk-baird marked the issue as grade-b