Platform: Code4rena
Start Date: 13/12/2021
Pot Size: $75,000 USDC
Total HM: 11
Participants: 30
Period: 7 days
Judge: leastwood
Total Solo HM: 4
Id: 68
League: ETH
Rank: 12/30
Findings: 3
Award: $1,484.76
π Selected for report: 8
π Solo Findings: 0
defsec
It is good to add a require() statement that checks the return value of token transfers or to use something like OpenZeppelinβs safeTransfer/safeTransferFrom unless one is sure the given token reverts in case of a failure. Failure to do so will cause silent failures of transfers and affect token accounting in contract.
Reference: This similar medium-severity finding from Consensys Diligence Audit of Fei Protocol: https://consensys.net/diligence/audits/2021/01/fei-protocol/#unchecked-return-value-for-iweth-transfer-call
https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleNativeTokenExitV2.sol#L104
Code Review
Consider using safeTransfer/safeTransferFrom or require() consistently.
#0 - loki-sama
2022-01-04T10:34:33Z
duplicate #232
#1 - 0xleastwood
2022-01-22T04:09:12Z
Duplicate of #192
π Selected for report: sirhashalot
Also found by: GiveMeTestEther, JMukesh, Ruhum, WatchPug, defsec, robee
defsec
The following contract functions performs an ERC20.approve() call but does not check the success return value. Some tokens do not revert if the approval failed but return false instead.
https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleTokenJoinV2.sol#L53 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleTokenJoin.sol#L43 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleNativeTokenExit.sol#L44 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleNativeTokenExitV2.sol#L55 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/callManagers/RebalanceManager.sol#L131 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/callManagers/RebalanceManagerV2.sol#L70 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/callManagers/RebalanceManagerV3.sol#L75
None
Its recommend to using OpenZeppelinβs SafeERC20 versions with the safeApprove function that handles the return value check as well as non-standard-compliant tokens.
#0 - loki-sama
2022-01-04T11:17:32Z
duplicate #294
defsec
Native fund transfers into the basket contracts are only expected from the wrapped token contract. Hence, it would be good to restrict incoming fund transfers to prevent accidental native fund transfers from other sources.
https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleTokenJoinV2.sol#L19
// WETH or WAVAX ... IERC20 public immutable INTERMEDIATE_TOKEN;
None
receive() external payable { require(msg.sender == address(WETH) | msg.sender == address(WAVAX), 'only wrapped eth'); }
#0 - 0xleastwood
2022-01-23T05:25:13Z
Duplicate of #253
defsec
Amun protocol allows different tokens to be used as output token. The Basket contracts do not appear to support rebasing/deflationary/inflationary tokens whose balance changes during transfers or over time. The necessary checks include at least verifying the amount of tokens transferred to contracts before and after the actual transfer to infer any fees/interest.
Code Review
Make sure output token for any rebasing/inflation/deflation Add support in contracts for such tokens before accepting user-supplied tokens.
uint256 balanceBefore = getOwnBalance(outputToken); require(outputToken.safeTransfer(assetId, router, address(this), amount, "Transfer ERC20_TRANSFER_FAILED"); uint256 receivedAmount = getOwnBalance(outputToken) - balanceBefore;
#0 - 0xleastwood
2022-01-23T05:26:28Z
Duplicate of #220
defsec
joinTokenEth function performs a low-level status code and parses the return data even if the call failed as it does not check the first return value (success). It could be the case that non-zero data is returned even though the call failed, and the function would return true.
https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/EthSingleTokenJoin.sol#L26 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/EthSingleTokenJoinV2.sol#L26
None
Check the return value or perform a high-level call using the call.value function.
(bool success, ) = _target.call{value: _value}(_calldata); require(success, "CALL_FAILED");
#0 - 0xleastwood
2022-01-23T05:20:08Z
Duplicate of #190
defsec
Reading array length at each iteration of the loop takes 6 gas (3 for mload and 3 to place memory_offset) in the stack.
Caching the array length in the stack saves around 3 gas per iteration.
https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/callManagers/RebalanceManager.sol#L218 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/callManagers/RebalanceManager.sol#L234 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/callManagers/RebalanceManagerV2.sol#L155 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/callManagers/RebalanceManagerV3.sol#L166 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/factories/PieFactoryContract.sol#L88 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/facets/Call/CallFacet.sol#L55 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/facets/Basket/BasketFacet.sol#L50 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/facets/Basket/BasketFacet.sol#L160 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/facets/Basket/BasketFacet.sol#L321 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/facets/Basket/BasketFacet.sol#L348 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/facets/Basket/BasketFacet.sol#L381 https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/singleJoinExit/SingleNativeTokenExit.sol#L69
None
Consider to cache array length.
#0 - 0xleastwood
2022-01-23T22:17:47Z
Duplicate of #249
10.2787 USDC - $10.28
defsec
++i is more gas efficient than i++ in loops forwarding.
Code Review
It is recommend to use unchecked{++i} and change i declaration to uint256.
defsec
Using newer compiler versions and the optimizer gives gas optimizations and additional safety checks are available for free.
The advantages of versions 0.8.* over <0.8.0 are:
"All Contracts" - Pragma version 0.7.5 is used in the all contracts. (https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/EthSingleTokenJoinV2.sol)
None
Consider to upgrade pragma to at least 0.8.4.
π Selected for report: defsec
defsec
The use of _msgSender() when there is no implementation of a meta transaction mechanism that uses it, such as EIP-2771, very slightly increases gas consumption.
_msgSender() is utilized three times where msg.sender could have been used in the following function.
None
Replace _msgSender() with msg.sender if there is no mechanism to support meta-transactions like EIP-2771 implemented.
π Selected for report: defsec
78.3316 USDC - $78.33
defsec
From Pragma 0.8.0, ABI coder v2 is activated by default. The pragma abicoder v2 can be deleted from the repository. That will provide gas optimization.
None
After the 0.8.0, ABI coder v2 is activated by default. Upgrade pragma to 0.8.0 version. It is recommended to delete redundant codes.
From Solidity v0.8.0 Breaking Changes https://docs.soliditylang.org/en/v0.8.0/080-breaking-changes.html
#0 - loki-sama
2022-01-17T11:06:40Z
We use x.x.7 but would do it with upgrade..
defsec
Shortening revert strings to fit in 32 bytes will decrease deploy time gas and will decrease runtime gas when the revert condition has been met.
Revert strings that are longer than 32 bytes require at least one additional mstore, along with additional overhead for computing memory offset, etc.
Revert strings > 32 bytes are here:
Manual Review
Shorten the revert strings to fit in 32 bytes. That will affect gas optimization.
π Selected for report: defsec
defsec
When doing swaps with a Uniswap router from within a contract, there's no need to compute any offset from the current block for the deadline parameter. The router just checks if deadline >= block.timestamp.
https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleTokenJoinV2.sol#L109
None
The most efficient way to provide deadlines for a router swap is to use a hardcoded value that is far in the future, for example, 1e10
.
π Selected for report: defsec
defsec
Lower than uint256 size storage instance variables are actually less gas efficient. E.g. using uint16 does not give any efficiency, actually, it is the opposite as EVM operates on default of 256-bit values so uint8 is more expensive in this case as it needs a conversion. It only gives improvements in cases where you can pack variables together, e.g. structs.
https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleNativeTokenExit.sol#L26 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleTokenJoinV2.sol#L37 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleNativeTokenExitV2.sol#L36 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/singleJoinExit/SingleTokenJoin.sol#L27 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/facets/Basket/BasketFacet.sol#L143 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/facets/Basket/BasketFacet.sol#L188 https://github.com/code-423n4/2021-12-amun/blob/cf890dedf2e43ec787e8e5df65726316fda134a1/contracts/basket/contracts/facets/ERC20/ERC20Facet.sol#L63
None
Consider to review all uint types. Change them with uint256 If the integer is not necessary to present with 16.
π Selected for report: defsec
defsec
Checking non-zero transfer values can avoid an external call to save gas. Checking if amount > 0 before making the external call to erc20 transfer can save gas by avoiding the external call in such situations
https://github.com/code-423n4/2021-12-amun/blob/main/contracts/bridge/contracts/amunBasketBridge/PolygonERC20Wrapper.sol#L51
None
Add zero amount validation on the deposit function.