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: 145/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
address(0)
and amount > 0
is not checked in many functions in RootPort.sol
These are functions that use address as a parameter but do not check address(0)
. Although this may not be financially harmful, it is still a top priority to check these parameters.
No check for the _globalAddress
parameter
function setLocalAddress(address _globalAddress, address _localAddress, uint256 _srcChainId) external override requiresCoreRootRouter { if (_localAddress == address(0)) revert InvalidLocalAddress(); getGlobalTokenFromLocal[_localAddress][_srcChainId] = _globalAddress; getLocalTokenFromGlobal[_globalAddress][_srcChainId] = _localAddress; emit GlobalTokenAdded(_localAddress, _globalAddress, _srcChainId); }
Do not check the address in the _from
parameter. In the previous function bridgeToRoot
, I also mentioned this, but here we use safeTransfer
of solady and because it is transferring from outside to this contract, we can check for exceptions in this case. So the previous function I evaluated the risk level as Medium.
function bridgeToRootFromLocalBranch(address _from, address _hToken, uint256 _amount) external override requiresLocalBranchPort { if (!isGlobalAddress[_hToken]) revert UnrecognizedToken(); _hToken.safeTransferFrom(_from, address(this), _amount); }
function burn(address _from, address _hToken, uint256 _amount, uint256 _srcChainId) external override requiresBridgeAgent { if (!isGlobalAddress[_hToken]) revert UnrecognizedToken(); ERC20hTokenRoot(_hToken).burn(_from, _amount, _srcChainId); }
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootPort.sol#L325-L333
No check for the _from
and _amount
parameters
function burnFromLocalBranch(address _from, address _hToken, uint256 _amount) external override requiresLocalBranchPort { if (!isGlobalAddress[_hToken]) revert UnrecognizedToken(); ERC20hTokenRoot(_hToken).burn(_from, _amount, localChainId); }
No check for the _to
and _amount
parameters.
function mintToLocalBranch(address _to, address _hToken, uint256 _amount) external override requiresLocalBranchPort { if (!isGlobalAddress[_hToken]) revert UnrecognizedToken(); if (!ERC20hTokenRoot(_hToken).mint(_to, _amount, localChainId)) revert UnableToMint(); }
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootPort.sol#L382-L390
No check for the _manager
and brigeAgent
parameters
function addBridgeAgent(address _manager, address _bridgeAgent) external override requiresBridgeAgentFactory { if (isBridgeAgent[_bridgeAgent]) revert AlreadyAddedBridgeAgent(); bridgeAgents.push(_bridgeAgent); getBridgeAgentManager[_bridgeAgent] = _manager; isBridgeAgent[_bridgeAgent] = true; emit BridgeAgentAdded(_bridgeAgent, _manager); }
You should check address(0)
before executing any function.
My recommendation is to just add a simple modifier for checking address(0)
and a modifier for checking amount > 0
. And use these modifiers for all functions that need them.
@inheritdoc
for function comments in RootPort.sol
function bridgeToLocalBranchFromRoot(address _to, address _hToken, uint256 _amount) external override requiresLocalBranchPort { }
/// @inheritdoc IRootPort function bridgeToLocalBranchFromRoot(address _to, address _hToken, uint256 _amount) external override requiresLocalBranchPort { }
In your entire code, I understand that it is necessary to add a hyphen below before the name of the internal function. But here it is not there, you should add that in your code.
function addVirtualAccount(address _user) internal returns (VirtualAccount newAccount) { ... }
function _addVirtualAccount(address _user) internal returns (VirtualAccount newAccount) { ... }
/// @inheritdoc IRootPort function fetchVirtualAccount(address _user) external override returns (VirtualAccount account) { account = getUserAccount[_user]; if (address(account) == address(0)) account = addVirtualAccount(_user); } /** * @notice Creates a new virtual account for a user. * @param _user address of the user to associate a virtual account with. */ function addVirtualAccount(address _user) internal returns (VirtualAccount newAccount) { if (_user == address(0)) revert InvalidUserAddress(); newAccount = new VirtualAccount{salt: keccak256(abi.encode(_user))}(_user, address(this)); getUserAccount[_user] = newAccount; emit VirtualAccountCreated(_user, address(newAccount)); }
/// @inheritdoc IRootPort function fetchVirtualAccount( address _user ) external override returns (VirtualAccount account) { if (_user == address(0)) revert InvalidUserAddress(); account = getUserAccount[_user]; if (address(account) == address(0)) { account = _addVirtualAccount(_user); emit VirtualAccountCreated(_user, address(newAccount)); } } /** * @notice Creates a new virtual account for a user. * @param _user address of the user to associate a virtual account with. */ function _addVirtualAccount( address _user ) internal returns (VirtualAccount newAccount) { newAccount = new VirtualAccount{salt: keccak256(abi.encode(_user))}( _user, address(this) ); getUserAccount[_user] = newAccount; }
RootBridgeAgentExecutor.sol
There is code repetition in parsing _dParams
. This makes the code error-prone and difficult to maintain.
hTokens[i] = address( uint160( bytes20( bytes32( _dParams[ PARAMS_TKN_START + (PARAMS_ENTRY_SIZE * i) + ADDRESS_END_OFFSET: PARAMS_TKN_START + (PARAMS_ENTRY_SIZE * currentIterationOffset) ] ) ) ) ); tokens[i] = address( uint160( bytes20( bytes32( _dParams[ PARAMS_TKN_START + PARAMS_ENTRY_SIZE * (i + numOfAssets) + ADDRESS_END_OFFSET: PARAMS_TKN_START + PARAMS_ENTRY_SIZE * (currentIterationOffset + numOfAssets) ] ) ) ) );
You should consider creating helper functions to avoid repeating code. For example, you could create a separate function to convert addresses from _dParams
data to reduce repetition and make the code more readable and maintainable.
_bridgeInMultiple
in RootBridgeAgentExecutor.sol
In the _bridgeInMultiple
function, overflow or underflow can occur in some variables if the numOfAssets value is not handled properly.
https://github.com/code-423n4/2023-09-maia/blob/f5ba4de628836b2a29f9b5fff59499690008c463/src/RootBridgeAgentExecutor.sol#L273
Here, numOfAssets
is read from the input _dParams
and converted to type uint8
. However, if the value in _dParams
exceeds the limit of uint8
(i.e. between 0 and 255), an overflow may occur.
uint8 numOfAssets = uint8(bytes1(_dParams[0]));
To fix this, you need to add more checking and processing before using the numOfAssets
value.
For example:
if (numOfAssets > 0) revert();
When you use numOfAssets
to iterate over an array or perform other calculations, make sure that you check for and handle any possible cases to avoid overflow or underflow.
#0 - c4-pre-sort
2023-10-15T12:25:07Z
0xA5DF marked the issue as sufficient quality report
#1 - c4-judge
2023-10-20T13:45:13Z
alcueca marked the issue as grade-b