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: 20/30
Findings: 1
Award: $385.23
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: sirhashalot
Also found by: GiveMeTestEther, JMukesh, Ruhum, WatchPug, defsec, robee
Ruhum
According to the ERC20 standard, the approve()
function returns a boolean value indicating whether the call was successful or not, see https://eips.ethereum.org/EIPS/eip-20#methods
The SingleJoin/SingleExit contracts ignore that return value when approving tokens for the uniswap router. It might lead to a failed transaction because the contract proceeded with the swap although the approve()
call failed.
relevant line: https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/singleJoinExit/SingleNativeTokenExit.sol#L44
Slither
Use SafeERC20.safeApprove()
which reverts when the call fails or returns false.
#0 - loki-sama
2022-01-04T11:17:51Z
duplicate #294
Ruhum
The EthSingleTokenJoin
and EthSingleTokenJoinV2
contracts both rely on the INTERMEDIATE_TOKEN
to be WETH. But, both contracts can be deployed with a token other than WETH which would make them unusable. In that case, the contract would have to be redeployed. There's no way of adjusting that state variable after deployment.
To mitigate that, it shouldn't be possible to deploy those two contracts with a token other than WETH through address checks in the constructor.
Relevant constructors: https://github.com/code-423n4/2021-12-amun/blob/main/contracts/basket/contracts/singleJoinExit/EthSingleTokenJoin.sol#L10
none
Verify that the passed _INTERMEDIATE_TOKEN
address is actually the WETH contract.
#0 - 0xleastwood
2022-01-23T04:45:18Z
This adds unnecessary overhead for validating an incorrect deployment.
#1 - 0xleastwood
2022-01-23T05:20:03Z
Duplicate of #190
Ruhum
The SingleJoin & SingleExit contracts use transfer()
to send ether to the caller. If the caller is a smart contract the call might fail reverting the transaction if:
receive()
or payable fallback()
function)Since gas costs might increase in the future, using transfer()
might cause these contracts to potentially break since transfer()
would always fail.
Here's a blogpost about using transfer()
: https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/
I'd rate the issue as medium since a part of the protocol would potentially be unusable.
Usage of transfer()
:
none
Instead, the contracts should use:
(bool success, ) = payable(msg.sender).call{value: intermediateTokenBalance}(""); require(success);
Note that this approach introduces a risk of reentrancy. But, that shouldn't be a problem for these specific contracts. Nevertheless, it might be worth adding reentrancy guards in case the contracts ever get extended.
#0 - loki-sama
2021-12-29T11:56:09Z
duplicate #175