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: 3/99
Findings: 5
Award: $5,213.95
π Selected for report: 2
π Solo Findings: 1
π Selected for report: shenwilly
4631.8469 USDC - $4,631.85
An order's type and it's rules are defined in it's Complication
. Not checking it would allow anyone to take any orders regardless of their Complication's rule, causing unexpected execution for order makers.
takeMultipleOneOrders
assumes that all makerOrders
are simple orderbook orders and the Complication check is missing here.
PrivateSaleComplication
, allowing only Bob to take the private sale order.takeMultipleOneOrders
to take Alice's order, despite the Complication only allowing Bob to take it.Add canExecTakeOneOrder
function in IComplication.sol and implement it in InfinityOrderBookComplication
(and future Complications) to support takeMultipleOneOrders
operation, then modify takeMultipleOneOrders
to use the check:
function takeMultipleOneOrders() { ... for (uint256 i = 0; i < numMakerOrders; ) { bytes32 makerOrderHash = _hash(makerOrders[i]); bool makerOrderValid = isOrderValid(makerOrders[i], makerOrderHash); bool executionValid = IComplication(makerOrders[i].execParams[0]).canExecTakeOneOrder(makerOrders[i]); require(makerOrderValid && executionValid, 'order not verified'); require(currency == makerOrders[i].execParams[1], 'cannot mix currencies'); require(isMakerSeller == makerOrders[i].isSellOrder, 'cannot mix order sides'); uint256 execPrice = _getCurrentPrice(makerOrders[i]); totalPrice += execPrice; // @audit-issue missing complication check _execTakeOneOrder(makerOrderHash, makerOrders[i], isMakerSeller, execPrice); unchecked { ++i; } } ... }
#0 - nneverlander
2022-06-22T10:38:55Z
#1 - nneverlander
2022-07-05T11:19:10Z
#2 - HardlyDifficult
2022-07-10T21:50:58Z
takeMultipleOneOrders
does not check restrictions set via the Complication. Agree with the High risk assessment here.
375.1796 USDC - $375.18
https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L231 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L739 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L787 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L893
A malicious or compromised executor can execute matching orders at a very high tx.gasprice
, causing buyers to pay more gasCost
than necessary.
While this griefing attack is costly (unless governance is also malicious and can refund the gasCost), it's better to let users the ability to decide beforehand how much gas price they are willing to pay for auto snipes.
Consider adding maxGasPrice
variable in MakerOrder
and check them before executing matching orders. For example, if we are using execParams[2]
to store the gas price limit:
if (buy.execParams[2] > 0 ) { require(buy.execParams[2] <= tx.gasprice, "gas price too expensive"); }
#0 - nneverlander
2022-06-23T18:27:14Z
#1 - HardlyDifficult
2022-07-12T12:11:55Z
π Selected for report: WatchPug
Also found by: BowTiedWardens, GreyArt, Ruhum, berndartmueller, cccz, csanuragjain, defsec, joestakey, m9800, peritoflores, reassor, shenwilly, throttle, zer0dot
21.1924 USDC - $21.19
A malicious or compromised governance can set protocol fee to an unreasonable amount and steal funds from sellers.
When executing orders, governance can frontrun the transactions by setting protocol fee to 10000
, effectively taking all funds supposed to be received by seller.
takeOrders
to buy Alice's order.Set a sanity check in setProtocolFee
so governance can't set it to unreasonable value. Consider using timelock for setting governance settings.
function setProtocolFee(uint16 _protocolFeeBps) external onlyOwner { require(_protocolFeeBps <= 1500, "fee must not be higher than 15%"); PROTOCOL_FEE_BPS = _protocolFeeBps; emit NewProtocolFee(_protocolFeeBps); }
#0 - nneverlander
2022-06-23T11:20:47Z
Duplicate
#1 - HardlyDifficult
2022-07-11T00:02:37Z
π Selected for report: shenwilly
Also found by: 0x29A, BowTiedWardens, VAD37, berndartmueller, peritoflores
136.753 USDC - $136.75
https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L1260-L1263 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L739-L747
A malicious or compromised governance can set the transfer gas cost to an unreasonable amount and steal approved WETH from buyers.
There are two ways for governance to exploit this:
WETH_TRANSFER_GAS_UNITS
to a very high amount.WETH_TRANSFER_GAS_UNITS
to a very high amount, and execute trades against active buy orders. As long as the value of WETH to steal is higher than the cost to prepare the NFTs to sell, it is profitable to do so.WETH_TRANSFER_GAS_UNITS
is set to 50000
.InfinityExchange
.WETH_TRANSFER_GAS_UNITS
to a very high amount such that the gasCost calculation equals 100 WETH.Set a sanity check in updateWethTranferGas
so governance can't set it to unreasonable value. Consider using timelock for setting governance settings.
function updateWethTranferGas(uint32 _wethTransferGasUnits) external onlyOwner { require(_wethTransferGasUnits <= 100000, "gas unit must not be higher than 100000"); WETH_TRANSFER_GAS_UNITS = _wethTransferGasUnits; emit NewWethTransferGasUnits(_wethTransferGasUnits); }
#0 - nneverlander
2022-06-23T11:20:40Z
Duplicate
#1 - HardlyDifficult
2022-07-11T22:56:09Z
When a transaction is sent by the matching engine, the user pays for the gas costs of their portion of that call. There's overhead in actually getting the money from the user in WETH, which is estimated with WETH_TRANSFER_GAS_UNITS. That value is currently uncapped so the admin could increase it significantly, impacting users who signed orders back when that value was more reasonably assigned.
Agree with Medium risk here.
π 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
48.9813 USDC - $48.98
In InfinityStaker._updateUserStakedAmounts
:
if (amount > vestedTwelveMonths) { userstakedAmounts[user][Duration.TWELVE_MONTHS].amount = 0; userstakedAmounts[user][Duration.TWELVE_MONTHS].timestamp = 0; // @audit if amount == vestedTwelveMonths timestamp not 0, unreachable } else { userstakedAmounts[user][Duration.TWELVE_MONTHS].amount -= amount; }
The amount > vestedTwelveMonths
check will never be satisfied as amount
is guaranteed to be no more than total vested at L123. When amount
equals vestedTwelveMonths
, userstakedAmounts[user][Duration.TWELVE_MONTHS]
will be reduced to zero but the timestamp is not reset.
Use inclusive condition instead:
if (amount >= vestedTwelveMonths) { ... }
updateWethTranferGas
should be updateWethTransferGas
.
It's best practice to use a constant instead of magic number for readability, especially for future maintainers.
Add a constant variable BPS = 10000
.
https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L725 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L775 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L819 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L873 https://github.com/code-423n4/2022-06-infinity/blob/765376fa238bbccd8b1e2e12897c91098c7e5ac6/contracts/core/InfinityExchange.sol#L1135
#0 - nneverlander
2022-06-23T11:21:25Z
Duplicate