Platform: Code4rena
Start Date: 14/06/2022
Pot Size: $50,000 USDC
Total HM: 19
Participants: 99
Period: 5 days
Judge: HardlyDifficult
Total Solo HM: 4
Id: 136
League: ETH
Rank: 23/99
Findings: 3
Award: $446.25
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: shenwilly
Also found by: 0x29A, BowTiedWardens, VAD37, berndartmueller, peritoflores
136.753 USDC - $136.75
Judge has assessed an item in Issue #270 as Medium risk. The relevant finding follows:
ProtocolFee
and gasFee
missing max amount check which can be used to take fund from userWith PROTOCOL_FEE_BPS > 10000
(more than 100%), the exchange can steal user WETH who might approve max WETH allowance for InfinityExchange.sol
To provide safety for user and in case of compromised both admin and MATCH_EXECUTOR
bot server.
Protocol should prevent potential further damage that might steal WETH from all user approve Exchange before.
function setProtocolFee(uint16 _protocolFeeBps) external onlyOwner { require(_protocolFeeBps <= 1000); //max 10% PROTOCOL_FEE_BPS = _protocolFeeBps; emit NewProtocolFee(_protocolFeeBps); }
With maximum amount of WETH_TRANSFER_GAS_UNITS
is 4_294_967_296
and default value is 50_000
, basically exchange can take any amount of WETH from user if gas price set high enough.
function updateWethTranferGas(uint32 _wethTransferGasUnits) external onlyOwner { require(_wethTransferGasUnits <= 200_000); // for deflation token or voting token transfer cannot really be more than 200000 gas per transaction WETH_TRANSFER_GAS_UNITS = _wethTransferGasUnits; emit NewWethTransferGasUnits(_wethTransferGasUnits); }
#0 - HardlyDifficult
2022-07-11T22:58:40Z
🌟 Selected for report: joestakey
Also found by: 0x1f8b, 0x29A, 0x52, 0xDjango, 0xNazgul, 0xNineDec, 0xf15ers, 0xkowloon, 0xmint, 8olidity, BowTiedWardens, Chom, Cityscape, Czar102, ElKu, FSchmoede, Funen, GimelSec, GreyArt, IllIllI, KIntern, Kaiziron, Kenshin, Lambda, MadWookie, MiloTruck, PPrieditis, Picodes, Ruhum, Sm4rty, StErMi, TerrierLover, TomJ, Treasure-Seeker, VAD37, WatchPug, Wayne, _Adam, a12jmx, abhinavmir, antonttc, apostle0x01, asutorufos, berndartmueller, cccz, cloudjunky, codexploder, cryptphi, csanuragjain, defsec, delfin454000, fatherOfBlocks, georgypetrov, hake, hansfriese, horsefacts, hyh, k, kenta, nxrblsrpr, oyc_109, peritoflores, rajatbeladiya, reassor, rfa, robee, sach1r0, saian, samruna, shenwilly, simon135, sorrynotsorry, sseefried, throttle, unforgiven, wagmi, zzzitron
56.2525 USDC - $56.25
totalStaked >= 0
is always trueupdateStakeLevelThreshold()
should call with array input and not spread out into multiple transactionsupdatePenalties()
missing input checkupdateInfinityTreasury()
ProtocolFee
and gasFee
missing max amount check which can be used to take fund from user_transferNFTs()
revert for case like thistotalStaked >= 0
is always trueSlither tautology error here. uint256
always >= 0. I think the dev here mean totalStaked > 0
.
updateStakeLevelThreshold()
should call with array input and not spread out into multiple transactionsSince there is no implementation for user staking power yet. Admin should be aware of user might use protocol when admin changing staking level.
If admin update stake level of Bronze,Silver,Gold,Plat one by one between the average of 4 blocks (assuming using JS script). There is very small chance of user do something not intended as Threshold is changing.
function _updateStakeLevelThreshold(StakeLevel stakeLevel, uint16 threshold) internal { if (stakeLevel == StakeLevel.BRONZE) { BRONZE_STAKE_THRESHOLD = threshold; } else if (stakeLevel == StakeLevel.SILVER) { SILVER_STAKE_THRESHOLD = threshold; } else if (stakeLevel == StakeLevel.GOLD) { GOLD_STAKE_THRESHOLD = threshold; } else if (stakeLevel == StakeLevel.PLATINUM) { PLATINUM_STAKE_THRESHOLD = threshold; //@audit missing input check } } function updateStakeLevelThreshold(StakeLevel[] stakeLevel, uint16[] threshold) external onlyOwner { for (uint8 i = 0; i < stakeLevel.length; i++) { _updateStakeLevelThreshold(stakeLevel[i], threshold[i]); } // emit event } // Or this one is much better as there are fixed amount of enum and threshold function updateStakeLevelThreshold(uint16 bronzeThreshold,uint16 silverThreshold, uint16 goldThresHold, uint16 platThresHold ) external onlyOwner { require (silverThreshold >= bronzeThreshold, "Silver threshold must be greater than Bronze threshold"); require (goldThresHold >= silverThreshold, "Gold threshold must be greater than Silver threshold"); require (platThresHold >= goldThresHold, "Platinum threshold must be greater than Gold threshold"); if(BRONZE_STAKE_THRESHOLD != bronzeThreshold) BRONZE_STAKE_THRESHOLD = bronzeThreshold; if(SILVER_STAKE_THRESHOLD != silverThreshold) SILVER_STAKE_THRESHOLD = silverThreshold; if(GOLD_STAKE_THRESHOLD != goldThresHold) GOLD_STAKE_THRESHOLD = goldThresHold; if(PLATINUM_STAKE_THRESHOLD != platThresHold) PLATINUM_STAKE_THRESHOLD = platThresHold; // emit event }
updatePenalties()
missing input checkSolidity throw error when divided by 0. Admin should prevent this so user wont be locked out of ragequit()
early.
function updatePenalties( uint16 threeMonthPenalty, uint16 sixMonthPenalty, uint16 twelveMonthPenalty ) external onlyOwner { require(threeMonthPenalty >= 1, 'Three month penalty must be greater than 1'); require(sixMonthPenalty >= threeMonthPenalty, 'Six month penalty must be greater than three month penalty'); require(twelveMonthPenalty >= sixMonthPenalty, 'Twelve month penalty must be greater than six month penalty'); THREE_MONTH_PENALTY = threeMonthPenalty; SIX_MONTH_PENALTY = sixMonthPenalty; TWELVE_MONTH_PENALTY = twelveMonthPenalty; // emit event }
updateInfinityTreasury()
Cant have user send token to burn address thanks to javascript.
function updateInfinityTreasury(address _infinityTreasury) external onlyOwner { require(_infinityTreasury != address(0), 'Invalid address'); INFINITY_TREASURY = _infinityTreasury; // emit event }
With admin event, it is for easy tracking from off chain for rare case like compromised admin key. It just simply look much better with it.
Missing event all of these functions:
ProtocolFee
and gasFee
missing max amount check which can be used to take fund from userWith PROTOCOL_FEE_BPS > 10000
(more than 100%), the exchange can steal user WETH who might approve max WETH allowance for InfinityExchange.sol
To provide safety for user and in case of compromised both admin and MATCH_EXECUTOR
bot server.
Protocol should prevent potential further damage that might steal WETH from all user approve Exchange before.
function setProtocolFee(uint16 _protocolFeeBps) external onlyOwner { require(_protocolFeeBps <= 1000); //max 10% PROTOCOL_FEE_BPS = _protocolFeeBps; emit NewProtocolFee(_protocolFeeBps); }
With maximum amount of WETH_TRANSFER_GAS_UNITS
is 4_294_967_296
and default value is 50_000
, basically exchange can take any amount of WETH from user if gas price set high enough.
function updateWethTranferGas(uint32 _wethTransferGasUnits) external onlyOwner { require(_wethTransferGasUnits <= 200_000); // for deflation token or voting token transfer cannot really be more than 200000 gas per transaction WETH_TRANSFER_GAS_UNITS = _wethTransferGasUnits; emit NewWethTransferGasUnits(_wethTransferGasUnits); }
_transferNFTs()
revert for case like thisAll NFT transfer goes through _transferNFTs(). But if-else only check for NFT contract support ERC721 or ERC1155. When nonstandard NFT does not have function to return support interface, the transaction revert.
Either add final else case as fail-safe call ERC721 anyway or throw error to prevent any malicious NFT from entering the exchange.
#0 - HardlyDifficult
2022-07-11T23:01:23Z
#1 - HardlyDifficult
2022-07-12T12:34:24Z