Platform: Code4rena
Start Date: 22/09/2023
Pot Size: $100,000 USDC
Total HM: 15
Participants: 175
Period: 14 days
Judge: alcueca
Total Solo HM: 4
Id: 287
League: ETH
Rank: 140/175
Findings: 1
Award: $11.47
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: MrPotatoMagic
Also found by: 0xAadi, 0xDING99YA, 0xDemon, 0xRstStn, 0xSmartContract, 0xStriker, 0xWaitress, 0xbrett8571, 0xfuje, 0xsagetony, 0xsurena, 33BYTEZZZ, 3docSec, 7ashraf, ABA, ABAIKUNANBAEV, Aamir, Audinarey, Bauchibred, Black_Box_DD, Daniel526, DanielArmstrong, DanielTan_MetaTrust, Dinesh11G, Eurovickk, Franklin, Inspecktor, John, Jorgect, Joshuajee, K42, Kek, Koolex, LokiThe5th, MIQUINHO, Myd, NoTechBG, QiuhaoLi, SanketKogekar, Sathish9098, Sentry, Soul22, SovaSlava, Stormreckson, Tendency, Topmark, Udsen, V1235816, Viktor_Cortess, Viraz, Yanchuan, ZdravkoHr, Zims, albahaca, albertwh1te, alexweb3, alexxander, ast3ros, audityourcontracts, bareli, bin2chen, bronze_pickaxe, c0pp3rscr3w3r, cartlex_, castle_chain, chaduke, debo, ether_sky, gumgumzum, imare, its_basu, jaraxxus, jasonxiale, josephdara, kodyvim, ladboy233, lanrebayode77, lsaudit, mert_eren, minhtrng, n1punp, nadin, niroh, nmirchev8, orion, peakbolt, perseverancesuccess, pfapostol, ptsanev, rvierdiiev, saneryee, shaflow2, te_aut, terrancrypt, twcctop, unsafesol, ustas, versiyonbir, windhustler, yongskiws, zhaojie, ziyou-
11.4657 USDC - $11.47
no | Issue | Instances | |
---|---|---|---|
[L-01] | NO check for contract accounts | 1 |
no | Issue | Instances | |
---|---|---|---|
[N-01] | Misspelled word | 1 | |
[N-02] | if statement syntax not correctly implemented | 2 | |
[N-03] | no implementation of events | 5 |
The function does not include verification of whether the supplied addresses are Externally Owned Accounts (EOAs) or contract addresses. If there is no check for contract accounts the function will accept EOA accounts and continue to work with them as if they were contract addresses which will lead to potential problems.
To resolve this issue, you should add checks to verify whether the supplied addresses are Externally Owned Accounts (EOAs) or contract addresses. This can be done by using the extcodesize
opcode in Solidity. Here is an example of how you can implement this:
function isContract(address _addr) private view returns (bool) { uint32 size; assembly { size := extcodesize(_addr) } return (size > 0); }
Then, you can use this function in your initialize
and initializeCore
functions to check if the supplied addresses are contract addresses:
function initialize(address _bridgeAgentFactory, address _coreRootRouter) external onlyOwner { require(_bridgeAgentFactory != address(0), "Bridge Agent Factory cannot be 0 address."); require(_coreRootRouter != address(0), "Core Root Router cannot be 0 address."); require(_setup, "Setup ended."); require(isContract(_bridgeAgentFactory), "Bridge Agent Factory must be a contract."); require(isContract(_coreRootRouter), "Core Root Router must be a contract."); _setup = false; isBridgeAgentFactory[_bridgeAgentFactory] = true; bridgeAgentFactories.push(_bridgeAgentFactory); coreRootRouterAddress = _coreRootRouter; }
This will ensure that only contracts can be set as the Bridge Agent Factory and Core Root Router, which can prevent potential attacks or misuse of these roles.
Inccorectly spelled word.
mapping(VirtualAccount acount => mapping(address router => bool allowed)) public isRouterApproved;
The "if" statement does not adhere to the correct syntax or layout. In Solidity, an if statement should be written as if (condition) { code }
.
In the functions, the if statement on line 74 is written as if (isContract(_call.target)) (success, returnData[i]) = _call.target.call{value: val}(_call.callData);
.
This is not the correct syntax for an if statement in Solidity. The code that should be executed if the condition is true should be enclosed in curly braces {}
.
Here is the corrected code:
if (isContract(_call.target)) { (success, returnData[i]) = _call.target.call{value: val}(_call.callData); }
This change will ensure that the if statement is correctly formatted and will execute as expected.
There are 2 instances of this
function call(Call[] calldata calls) external override requiresApprovedCaller returns (bytes[] memory returnData) { uint256 length = calls.length; returnData = new bytes[](length); for (uint256 i = 0; i < length;) { bool success; Call calldata _call = calls[i]; if (isContract(_call.target)) (success, returnData[i]) = _call.target.call(_call.callData); if (!success) revert CallFailed(); unchecked { ++i; } } }
function payableCall(PayableCall[] calldata calls) public payable returns (bytes[] memory returnData) { uint256 valAccumulator; uint256 length = calls.length; returnData = new bytes[](length); PayableCall calldata _call; for (uint256 i = 0; i < length;) { _call = calls[i]; uint256 val = _call.value; // Humanity will be a Type V Kardashev Civilization before this overflows - andreas // ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256 unchecked { valAccumulator += val; } bool success; if (isContract(_call.target)) (success, returnData[i]) = _call.target.call{value: val}(_call.callData); if (!success) revert CallFailed(); unchecked { ++i; } } // Finally, make sure the msg.value = SUM(call[0...i].value) if (msg.value != valAccumulator) revert CallFailed(); }
Contract Event Emission Deficiency: The contract under review does not emit any events. Although not a mandatory requirement, the emission of events is instrumental for off-chain tracking of contract activities. To resolve this issue, you should consider adding event emissions at key points in your contract. Events allow light clients and web interfaces to react to on-chain transactions and activities.
For example, you could add an event in the withdrawNative
, withdrawERC20
, and withdrawERC721
functions to track when withdrawals occur. Here's how you could do it for withdrawNative
:
event WithdrawnNative(address indexed to, uint256 amount); function withdrawNative(uint256 _amount) external override requiresApprovedCaller { msg.sender.safeTransferETH(_amount); emit WithdrawnNative(msg.sender, _amount); }
You can do similar for withdrawERC20
and withdrawERC721
. Also consider adding events in the call
and payableCall
functions to track when these functions are called.
function withdrawNative(uint256 _amount) external override requiresApprovedCaller { msg.sender.safeTransferETH(_amount); }
function withdrawERC20(address _token, uint256 _amount) external override requiresApprovedCaller { _token.safeTransfer(msg.sender, _amount); }
function withdrawERC721(address _token, uint256 _tokenId) external override requiresApprovedCaller { ERC721(_token).transferFrom(address(this), msg.sender, _tokenId); }
function call(Call[] calldata calls) external override requiresApprovedCaller returns (bytes[] memory returnData) { uint256 length = calls.length; returnData = new bytes[](length); for (uint256 i = 0; i < length;) { bool success; Call calldata _call = calls[i]; if (isContract(_call.target)) (success, returnData[i]) = _call.target.call(_call.callData); if (!success) revert CallFailed(); unchecked { ++i; } } }
function payableCall(PayableCall[] calldata calls) public payable returns (bytes[] memory returnData) { uint256 valAccumulator; uint256 length = calls.length; returnData = new bytes[](length); PayableCall calldata _call; for (uint256 i = 0; i < length;) { _call = calls[i]; uint256 val = _call.value; // Humanity will be a Type V Kardashev Civilization before this overflows - andreas // ~ 10^25 Wei in existence << ~ 10^76 size uint fits in a uint256 unchecked { valAccumulator += val; } bool success; if (isContract(_call.target)) (success, returnData[i]) = _call.target.call{value: val}(_call.callData); if (!success) revert CallFailed(); unchecked { ++i; } } // Finally, make sure the msg.value = SUM(call[0...i].value) if (msg.value != valAccumulator) revert CallFailed(); }
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/VirtualAccount.sol#L51 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/VirtualAccount.sol#L56 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/VirtualAccount.sol#L61 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/VirtualAccount.sol#L66 https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/VirtualAccount.sol#L85
#0 - c4-pre-sort
2023-10-15T08:51:13Z
0xA5DF marked the issue as sufficient quality report
#1 - c4-judge
2023-10-20T13:25:42Z
alcueca marked the issue as grade-b