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: 24/62
Findings: 2
Award: $471.30
🌟 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#L212 https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Exchange.sol#L216
When an order is matched, the Buyer has the option to pay in either ETH, WETH or via the Pool contract. The Exchange smart contract implements a function _returnDust
which returns the extra ETH to the user, if (s)he overpays.
The function is implemented in inline assembly(YUL) and takes advantage of the call OPCODE
.
The result of the call
function call isn't checked whether it's true or not.
So, if a smart contracts calls the execute
function, pays in ETH and forgets to implement a fallback function, the execute function succeedsand the remaining ETH are left in the contract and not returned to the user.
fallback
functionexecute
function of the Exchange.sol
with an amountA
higher than the sell amount and with address(0)
as the paymentToken
meaning that (s)he wants to pay in ETH.remainingETH
are remainingETH=startingETH-sellAmount
and need to be transferred to the userA via the _returnDust
functionManual Code Review
It is recommended to check the returned value of the call opcode, callStatus
whether its true or not.
A simple _returnDust
function is given bellow:
function _returnDust() private { (bool success,) = payable(msg.sender).call{value: remainingETH}(""); require(success, "ETH transfer failed"); }
#0 - trust1995
2022-11-14T22:22:18Z
Dup of #185
#1 - c4-judge
2022-11-16T12:00:08Z
berndartmueller marked the issue as duplicate of #90
#2 - c4-judge
2022-11-16T12:00:12Z
berndartmueller marked the issue as satisfactory
404.489 USDC - $404.49
https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Pool.sol#L5 https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Pool.sol#L15
The Pool
smart contract allows a user to predeposit ETH so that it can be used when a seller takes their bid. It uses an ERC1967
proxy pattern and only the exchange contract is permitted to make transfers.
The smart contract inherits the OwnableUpgradeable.sol
smart contract
The smart contract inherits the OwnableUpgradeable.sol
but never initialize it, with a call to the internal __Ownable_init();
function. Due to the fact that the upgradable smart contract initial logic cannot be executed in the constructor, this efficiently leaves the smart contract without an owner, and so, no-one can upgrade it(https://github.com/code-423n4/2022-11-non-fungible/blob/323b7cbf607425dd81da96c0777c8b12e800305d/contracts/Pool.sol#L15)
Manual Code Review
It is recommended to create an initialize()
function, similar to the one that the Exchange.sol
, that makes a call to the __Ownable_init();
internal function. This will make the msg.sender
as the original owner, that is required in order to upgrade the smart contract.
#0 - trust1995
2022-11-14T22:14:55Z
Dup of #186 , but risk should be Med as no risk to user funds.
#1 - c4-judge
2022-11-16T12:15:47Z
berndartmueller marked the issue as duplicate of #186
#2 - c4-judge
2022-11-16T12:15:56Z
berndartmueller changed the severity to 2 (Med Risk)
#3 - c4-judge
2022-11-16T12:16:08Z
berndartmueller marked the issue as satisfactory