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: 13/30
Findings: 3
Award: $1,141.51
🌟 Selected for report: 5
🚀 Solo Findings: 0
pauliax
Token join contracts check that the final outputAmount is equal to _joinTokenStruct.outputAmount:
uint256 outputAmount = outputToken.balanceOf(address(this)); require( outputAmount == _joinTokenStruct.outputAmount, "FAILED_OUTPUT_AMOUNT" );
While these contracts are only for convenience, a theoretical attack exists here: a malicious actor can monitor the mempool and send the smallest fraction of the output token (basket) directly to the contract thus breaking this check.
Consider replacing == with >= .
#0 - loki-sama
2022-01-03T09:29:18Z
duplicate #81
pauliax
.transfer usage is discouraged and is no longer recommended as recipients with custom fallback functions (smart contracts) will not be able to handle that. You can read more here: https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/
Solution (don't forget re-entrancy protection): https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L53-L59
#0 - loki-sama
2022-01-03T10:01:36Z
#175
🌟 Selected for report: pauliax
Also found by: GiveMeTestEther, itsmeSTYJ, robee
pauliax
Rebalance managers approve max if allowance < quantity. SingleTokenJoin, SingleTokenJoinV2, SingleNativeTokenExit, and SingleNativeTokenExitV2. approve max if allowance < balance.
The problem is that some tokens, e.g. USDT require resetting approval to 0 before approving any value again.
Consider using this approach:
token.approve(spender, 0); token.approve(spender, uint256(-1));
450.6605 USDC - $450.66
pauliax
I am sorry if I incorrectly understood the intentions of contract PolygonERC20Wrapper but the comments and the code do not align here:
Function deposit: "Should handle deposit by minting the required amount for user. Make sure minting is done only by this function". However, the code does not perform any minting and just transfers the underlying tokens to the user.
On the other hand, functions withdraw and withdrawTo state: "Should burn user's tokens" but it actually performs both, minting and burning:
_mint(recipient, amount); _burn(recipient, amount);
I was late to verify this with the sponsor, so make sure this is the intended behavior, and update comments to match the codebase.
#0 - loki-sama
2021-12-30T13:53:51Z
The behavior is as intended. Maybe can change the comments.
14.2759 USDC - $14.28
pauliax
Assigned operations to constant variables are re-evaluated every time:
bytes32 constant BASKET_STORAGE_POSITION = keccak256("diamond.standard.basket.storage"); bytes32 constant CALL_STORAGE_POSITION = keccak256("diamond.standard.call.storage"); bytes32 constant REENTRY_STORAGE_POSITION = keccak256("diamond.standard.reentry.storage"); bytes32 constant ERC_20_STORAGE_POSITION = keccak256( // Compatible with pie-smart-pools "PCToken.storage.location" );
See https://github.com/ethereum/solidity/issues/9232
Change from 'constant' to 'immutable'.
#0 - 0xleastwood
2022-01-24T09:54:29Z
Duplicate of #281
35.2492 USDC - $35.25
pauliax
_amount.add(feeAmount) is re-evaluated again and again inside the loop:
for (uint256 i; i < bs.tokens.length; i++) { ... uint256 tokenAmount = balance(address(token)).mul(_amount.add(feeAmount)).div( totalSupply ); ... }
These values do not change so consider extracting outside the loop to reduce gas costs.
#0 - 0xleastwood
2022-01-24T09:56:06Z
Duplicate of #205
35.2492 USDC - $35.25
pauliax
Contracts SingleTokenJoinV2 and SingleNativeTokenExitV2 initialize uniSwapLikeRouter, but never actually use it, as swap.exchange is used instead. So it basically trusts the user input. Consider removing uniSwapLikeRouter if that was the intention to save some gas.
🌟 Selected for report: pauliax
78.3316 USDC - $78.33
pauliax
structs can be optimized, e.g. inputAmount and outputAmount could be stored in uint112, as that is what Uniswap like exchanges use: https://github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol#L22-L23
'deadline' also usually fits in a reasonable limit of at least uint32 or uint64. 'entryFee', 'exitFee', etc are also capped, so you should consider revisiting structs to find optimal sizes and orders of the variables.
🌟 Selected for report: pauliax
78.3316 USDC - $78.33
pauliax
Not used state variables:
bool private recovered; bool private burned;
Consider removing them to reduce deployment costs.
#0 - 0xleastwood
2022-01-24T09:53:53Z
As far as I can see, the warden is correct. So keeping this open