✅RACE #15 - DEX
Last updated
Last updated
All 8 questions in this RACE are based on the below contract. This is the same contract you will see for all the 8 questions in this RACE. The question is below the shown contract.
What is/are the correct implementation(s) of the nonReentrant() modifier?
My comment:
These two correct options are the same as:
Who can claim fees using claimFees()
?
My comment:
The onlyOwner
modifier is vulnerable to phishing attack:
In buyEth()
, we put an unchecked
block on current_eth -= amount
In buyEth()
, are there any reentrancy concerns assuming the nonReentrant
modifier is implemented correctly?
Comment:
While the nonReentrant modifier prevents re-entering the same contract to exploit an "incomplete state", the same cannot be said for other contracts that might make use of the SimpleDEX's state before the state is completely updated.
Specifically state variables involved in determining the price (token_balance & current_eth) are relevant here: current_eth is updated before the call() to the message sender is made. But token_balance is only updated after.
If the msg.sender is actually a contract, it will have a chance to call another protocol that is relying on the SimpleDEX's reported price to be correct (such as the Seller contract). If the malicious contract calls this victim contract while the state of SimpleDEX is incomplete (ie. cross-contract read-only reentrancy) the victim would make use of this incorrect price data which might give the attacker an advantage. (Not in this case though. There's no advantage to exploiting this in Seller since the attacker would actually have to pay a higher price than without exploiting this issue).
What will happen when calling buyEth()
via SimpleDexProxy
?
Comment:
The transaction would be reverted since the SimpleDEX's buyEth() function would attempt transferring the tokens from the msg.sender, which in this case would be a proxy that has no way to give it the appropriate allowance even if the user were to transfer their tokens to the proxy first.
In buyEth()
:
Can getEthPrice()
return zero?
Which of the following invariants (written in propositional logic) hold on a correct implementation of the code?
Comment:
The symbol <=> is called the biconditional operator, meaning that the expressions on either side are logically equivalent. But the actual balance of ether being equal to the balance tracked within the state variable current_eth does not imply that the actual and tracked token balances are equal too. So the biconditional is invalid. But even if it were an AND operator it would not be an invariant that could hold: The invariant would be simple to break by sending unsolicited tokens to the contract (eg. via selfdestruct()).
Option B) includes the fact that the actual token and ether balances may be higher than the tracked balances. In a correct implementation this invariant should always hold.
Option C)'s second part allows the tracked token_balance to be larger than the actual token balance. This should not be the case in a correct implementation.
Option D) seems similar to B) but would allow for there to be either too little ether to match the tracked balance or too little tokens to match the tracked token balance. So it doesn't sound like a good invariant to test for since you'd want both things to hold true and not just one of them. But that wasn't the question - would it hold true in a correct implementation? Yes.