Platform: Code4rena
Start Date: 11/11/2022
Pot Size: $36,500 USDC
Total HM: 5
Participants: 62
Period: 3 days
Judge: berndartmueller
Id: 181
League: ETH
Rank: 16/62
Findings: 2
Award: $596.26
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: rotcivegaf
Also found by: 0x4non, 0xDecorativePineapple, 9svR6w, Trust, V_B, adriro, ajtra, aviggiano, brgltd, carlitox477, chaduke, codexploder, corerouter, joestakey, ladboy233, s3cunda, saian, wait
66.8068 USDC - $66.81
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L216 https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L44
The execute
function refunds the unused ETH
back to the msg.sender
through _returnDust()
. This internal function uses a low-level call to transfer the ETH.
The issue is that the return value of the call is not checked. As per the Solidity documentation
Exceptions to this rule are send and the low-level functions call, delegatecall and staticcall: they return false as their first return value in case of an exception
Two cases here:
the msg.sender
is a smart contract that has a receive()
function performing some logic and can revert in certain conditions.
There is not enough gas for the low-level call to succeed, but enough for the execute()
call to finish - which is possible due to the 64th gas not being passed to sub-calls as per EIP-150
Both cases would lead to the call failing silently, and the msg.sender
would not retrieve the extra ETH
passed during execute()
.
On top of that, because remainingETH
is set to 0
at the end of the call due to the setupExecution
modifier, that amount would be lost forever.
Medium
Manual Analysis
Check the return value of the call, and revert if it is not true
.
function returnDust() private { uint256 remainingETH = remainingETH; assembly { if gt(remainingETH, 0) { let callStatus := call( gas(), caller(), selfbalance(), 0, 0, 0, 0 ) } } + if (!callStatus) revert(); }
#0 - c4-judge
2022-11-16T11:59:58Z
berndartmueller marked the issue as duplicate of #90
#1 - c4-judge
2022-11-16T12:00:02Z
berndartmueller marked the issue as satisfactory
🌟 Selected for report: 0xSmartContract
Also found by: 0x4non, 0xNazgul, 0xhacksmithh, Aymen0909, HE1M, IllIllI, Josiah, RaymondFam, Rolezn, Tricko, brgltd, c3phas, chrisdior4, joestakey, ladboy233, martin, neko_nyaa, rotcivegaf, tnevler, trustindistrust
529.4504 USDC - $529.45
A few logic issues were found. They are mainly centered around the upgradeability of the contracts, and are worth taking a look at to ensure the contracts are future-proof.
EXCHANGE
should be avoidedThe Pool
contract uses a hardcoded constant for the Exchange
address.
As the Exchange
is an upgradeable contract, this means EXCHANGE
can point to an incorrect address if Exchange
is upgraded.
In such cases, the Pool
functionality will be broken as transferFrom
will revert when called by the new Exchange
because of this check: users will not be able to use the Pool
for their orders.
Low
Manual Analysis
Pool.EXCHANGE
should not be a constantinitialize()
function in Pool to set the EXCHANGE
address.EXCHANGE
if it is upgraded.fees
is too large_transferFees
will revert if fees
is too large, meaning the order will never be able to be filled, though valid.
Low
Manual Analysis
Consider adding an upper limit on order.fees
, which can be validated in _validateOrderParameters
.
msg.value
in _transferTo()
._transferTo()
is used during an execute
call to transfer amount
from a buyer to a seller.
If the paymentToken is POOL
or WETH
, the call does not check msg.value == 0
.
This means if a buyer mistakenly passes ETH
during the call, that amount will be stuck in Exchange
, as there is no sweeping function.
Low
Manual Analysis
Consider adding a msg.value == 0
check in the code blocks handling POOL
and WETH
.
Exchange
should be upgradeableExchange
is an upgradeable contract and inherits OwnableUpgradeable.sol
,UUPSUpgradeable.sol
, ReentrancyGuarded
and EIP712
.
The first two are upgradeable but the last two are not.
Low
Manual Analysis
Use upgradeable versions of ReentrancyGuarded
and EIP712
.
Exchange
is not properly initializedExchange
follows the UUPSUpgradeable
pattern, inheriting it from OpenZeppelin's library. But Exchange.initialize
does not call __UUPSUpgradeable_init
, meaning it is actually left uninitialized.
Low
Manual Analysis
Add __UUPSUpgradeable_init()
after __Ownable_init()
to initialize()
.
initialize
Improper initialization by passing zero addresses to key variables would force redeployment. Note that there is already a zero address check in every admin setter.
Low
Manual Analysis
Add zero address checks in initialize()
.
Exchange
is an upgradeable contract and is missing a __gap[50] storage variableA storage gap allows for new storage variables in later versions.
Low
Manual Analysis
Define a storage gap at the end of the storage variable definitions
listingTime
can be set by taker
to use their matching policyThe listingTime
of orders are compared here, determining which policy will be used.
A buyer
(or seller
) can create an order to match an existing one and set a lower listingTime
to become the maker
.
This means the true order maker will become the taker
in the execution of the orders.
This will not grieve them as they have to agree to the policy used anyway, but the behavior is unexpected.
Low
Manual Analysis
Consider requiring the hash of the maker order in the taker signature.
#0 - c4-judge
2022-11-17T12:25:30Z
berndartmueller marked the issue as grade-b
#2 - berndartmueller
2022-12-01T14:42:37Z
Hi @berndartmueller , this report has 8 Low issues and was graded as B, while reports with fewer Lows (54 has 5, 39 has 3) were graded as A. Shouldn't it be graded as A too?
Hey @joestakey! After reviewing your QA report again, I'm on your side that it should be graded as A. For full transparency, I favored comprehensive QA and gas reports with a table of contents section for A graded reports. But as you have reported plenty of low-severity issues, you should get rewarded for that.
#3 - c4-judge
2022-12-01T14:42:47Z
berndartmueller marked the issue as grade-a