Platform: Code4rena
Start Date: 10/02/2022
Pot Size: $30,000 USDC
Total HM: 5
Participants: 24
Period: 3 days
Judge: harleythedog
Total Solo HM: 3
Id: 86
League: ETH
Rank: 1/24
Findings: 3
Award: $7,232.49
π Selected for report: 1
π Solo Findings: 1
π Selected for report: 0xliumin
5623.2427 USDC - $5,623.24
A user can destroy their NFTs and not pay fees on most of their assets.
Alice has an NFT portfolio with 100 gwei dai and 100 gwei uni. Alice calls destroy on this NFT with buy token marked as dai. We would expect after this destroy step that she would pay 1 gwei dai in fees + 1 gwei uni worth of dai in fees, no matter what orders she selects.
Alice selects the following orders:
[{ operator: ZeroEx, token: uni, calldata: performSwap with a very small amount of uni for dai} { operator: Flat, token: dai, calldata: transfer a very small amount dai for dai}]
This set of orders doesn't violate any of the require statements in the destroy function. Each order will complete successfully given the constraints here: https://github.com/code-423n4/2022-02-nested/blob/69cf51d8e4eeb8bce3025db7f4f74cc565c9fad3/contracts/abstracts/MixinOperatorResolver.sol#L100-L101
Fees aren't collected on the leftover amount from the callOperator step, seen here https://github.com/code-423n4/2022-02-nested/blob/69cf51d8e4eeb8bce3025db7f4f74cc565c9fad3/contracts/NestedFactory.sol#L446
this means that Alice will only pay the fees on the dai that was output from the orders (a very small amount), and the rest of the 100 gwei uni and 100 gwei dai are transferred directly back to the attacker.
Replace the safeTransfer with your safeTransferWithFees function. Or, if you don't want users to have to pay fees on slippage, set a maximum "slippage" amount in the safeSubmitOrder function.
#0 - maximebrugel
2022-02-11T11:13:46Z
Good one
#1 - harleythedogC4
2022-02-27T16:22:50Z
The warden has described a way to avoid fees even when large amounts are being withdrawn. An avoidance of fees more closely fits the criteria of a medium severity issue:
2 β Med: Assets not at direct risk, but the function of the protocol or its availability could be impacted, or leak value with a hypothetical attack path with stated assumptions, but external requirements.
This is contrary to the previous audit that awarded a fee avoidance finding as high severity (which I disagree with).
90.9726 USDC - $90.97
Overall the code is high-quality and easy to follow. I did find some low-risk and non-critical findings below.
Right now, this function returns false if the implementation AND the selector don't match. It's possible to provide a destination with either a different implementation or selector, and still get a true response.
https://github.com/code-423n4/2022-02-nested/blob/69cf51d8e4eeb8bce3025db7f4f74cc565c9fad3/contracts/OperatorResolver.sol#L42 Mitigation is to change && to ||.
If a user accidentally submits two withdraw transactions from the frontend, they will withdraw the token at that index and the last token in the list originally. Making sure that the user is unable to do this on the frontend will be important.
#0 - maximebrugel
2022-02-15T17:38:27Z
https://github.com/code-423n4/2022-02-nested-findings/issues/17
https://github.com/code-423n4/2022-02-nested-findings/issues/52
Transfer to address zero event used.
Specific update, the event LockTimestampIncreased
is emitted.
#1 - harleythedogC4
2022-03-03T01:17:36Z
My personal judgements:
_burn
will emit a transfer event which is good enough. Invalid.#2 - harleythedogC4
2022-03-03T02:27:44Z
Now, here is the methodology I used for calculating a score for each QA report. I first assigned each submission to be either non-critical (1 point), very-low-critical (5 points) or low-critical (10 points), depending on how severe/useful the issue is. The score of a QA report is the sum of these points, divided by the maximum number of points achieved by a QA report. This maximum number was 26 points, achieved by https://github.com/code-423n4/2022-02-nested-findings/issues/66.
The number of points achieved by this report is 2 points. Thus the final score of this QA report is (2/26)*100 = 8.