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: 10/99
Findings: 4
Award: $1,293.20
π Selected for report: 1
π Solo Findings: 0
π Selected for report: hyh
Also found by: 0x29A, 0xNineDec, 0xf15ers, 0xkowloon, GreyArt, IllIllI, KIntern, Kenshin, Lambda, WatchPug, Wayne, berndartmueller, byterocket, cccz, codexploder, horsefacts, kenzo, obront, obtarian, oyc_109, peritoflores, rajatbeladiya, rfa, saian, unforgiven, zer0dot
11.084 USDC - $11.08
Function InfinityStaker.rescueETH()
should rescue any ETH accidentally sent to the contract. But because of using msg.value
, instead of transfering ETH of contract, it just transfers ETH of caller to destination
.
InfinityStaker
InfinityStaker
and he try to rescue Aliceβs ETH by calling rescueETH
with destination = address(Alice)
and msg.value = 1 ETH
.InfinityStaker
contract.Manual Review
Add an amount
parameter in rescueETH
function and remove payable
tag.
#0 - nneverlander
2022-06-23T12:15:55Z
Duplicate
#1 - nneverlander
2022-07-05T12:37:49Z
#2 - HardlyDifficult
2022-07-09T17:00:36Z
π Selected for report: KIntern
Also found by: GimelSec, csanuragjain, kenzo, unforgiven
607.7909 USDC - $607.79
Function matchOrders
uses custom constraints to make the matching more flexible, allow seller/buyer to specify maximum/minimum number of NFTs they want to sell/buy. This function first does some checks and then execute matching.
But in function areNumItemsValid()
, there is a wrong checking will lead to wrong logic in matchOrders()
function.
Instead of checking if numConstructedItems <= sell.constraints[0]
or not, function areNumItemsValid()
check if buy.constraints[0] <= sell.constraints[0]
. It will lead to the scenario that numConstructedItems > sell.constraints[0]
and make the seller sell more number of nfts than he/she allow.
Consider the scenario
[1, 2, 3]
[1, 2, 3]
matchOrders()
to match Alice's order and Bob's one with parameter constructs = [1, 2, 3]
matchOrders
will transfer all NFT in construct
list (3 NFTs 1, 2, 3
) from seller to buyer even though seller only want to sell maximum 2 NFTs.For more information, please check this PoC. https://gist.github.com/minhquanym/a95c8652de8431c5d1d24aa4076a1878
hardhat, chai
Replace check buy.constraints[0] <= sell.constraints[0]
with numConstructedItems <= sell.constraints[0]
#0 - nneverlander
2022-06-22T19:12:21Z
Duplicate
#1 - nneverlander
2022-07-05T12:06:48Z
#2 - HardlyDifficult
2022-07-10T15:14:13Z
Seller's may specify a max number of NFTs to sell, but in the scenario outlined by the warden that requirement is not enforced - leading to the sale of more NFTs than authorized.
Accepting this as a High risk report. Making this submission the primary for including PoC code demonstrating the problem.
π Selected for report: csanuragjain
Also found by: KIntern
625.2993 USDC - $625.30
constructedItem
but can't be match.Function canExecMatchOrder()
will check whether match orders with a higher order intent can be executed as in the comment. It can be done by checking buyOrder
and sellOrder
contain constructedItem
or not.
But this function contains a redundant check doItemsIntersect(sell.nfts, buy.nfts)
. This check will make the whole logic of function wrong.
For example:
In example above, sellOrder
and buyOrder
are both contain constructedItem
, but doItemsIntersect(sellOrder, buyOrder)
will return false
and make the whole function return false
.
Manual review
remove check in line 140
#0 - nneverlander
2022-06-23T11:39:00Z
Duplicate
#1 - nneverlander
2022-07-05T13:13: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
49.0265 USDC - $49.03
return false
instead of require
because of function's purpose (InfinityOrderBookComplication.sol)Affected code
Proof of concept
doItemsIntersect
will return true if 2 item is intersected, return false if not. But in line 255, if not tokendIdsIntersect
, it will revert the transaction.Recommend mitigation step
require
statement, we can return false in line 255if (!tokenIdsIntersect) { return false; }
InfinityStaker.unstake
InfinityStaker.unstake
function, variables are following camel case naming convention (e.g vestedThreeMonths
and vestedTwelveMonths
, but vestedsixMonths
is not.Proof of concept
Recommended Mitigation Steps
vestedsixMonths
to vestedSixMonths
InfinityExchange
, variable WETH
is immutable. The compiler does not reserve a storage slot for this variable, and every occurrence is replaced by the respective value.Affected code
#0 - HardlyDifficult
2022-07-10T14:30:31Z