Platform: Code4rena
Start Date: 13/11/2023
Pot Size: $24,500 USDC
Total HM: 3
Participants: 120
Period: 4 days
Judge: 0xTheC0der
Id: 306
League: ETH
Rank: 7/120
Findings: 3
Award: $739.56
π Selected for report: 0
π Solo Findings: 0
690.3741 USDC - $690.37
The asD.withdrawCarry
function will always throw an error for all normal totalSupply()
values due to inappropriate scale factor. So the creator can withdraw the accrued interest only after all users will burn their asD
tokens.
The scale factor at the line L#76 of the asD
contract may be used with cTokens with 8 decimals but not with the cNOTE
which token has 18 decimals. The exchangeRateCurrent()
for the cNOTE
is scaled by 1 * 10**(18 - 18 + Underlying Token Decimals), i.e. 1e18. So the current scale factor is too big.
uint256 maximumWithdrawable = (CTokenInterface(cNote).balanceOf(address(this)) * exchangeRate) / 1e28 - totalSupply();
This way the underlying amount will be always underestimated and there will be the underflow error here.
Manual review
Consider using 1e18
scale factor instead of 1e28
.
Decimal
#0 - c4-pre-sort
2023-11-18T09:01:20Z
minhquanym marked the issue as duplicate of #227
#1 - c4-judge
2023-11-28T22:58:04Z
MarioPoneder marked the issue as satisfactory
π Selected for report: rvierdiiev
Also found by: 0x175, 0x3b, 0xMango, 0xarno, 0xpiken, Bauchibred, DarkTower, ElCid, Giorgio, HChang26, Kose, KupiaSec, Madalad, PENGUN, Pheonix, RaoulSchaffranek, SpicyMeatball, T1MOH, Tricko, Udsen, Yanchuan, aslanbek, ast3ros, bart1e, bin2chen, chaduke, d3e4, deepkin, developerjordy, glcanvas, inzinko, jasonxiale, jnforja, mahyar, max10afternoon, mojito_auditor, neocrao, nmirchev8, openwide, osmanozdemir1, peanuts, pep7siup, peritoflores, pontifex, rice_cooker, rouhsamad, t0x1c, tnquanghuy0512, turvy_fuzz, twcctop, ustas, vangrim, zhaojie, zhaojohnson
1.3743 USDC - $1.37
The shares price increases/decreases too fast especially for the first 20 shares and the sandwich attack can be really profitable. This way users may lose sufficient value of their investments when buying and selling the shares.
I will consider the option in which users buy one share at a time as a priority strategy for users, but the attack can be carried out equally when users buy several shares in one transaction or in a batch of transactions.
The difference between price + fee
for buying share #n and rewardsSinceLastClaim + price - fee
for selling share #n+1 let perform the sandwich attack. The most profitable is frontrun the first buyer. The attacker can receive approx. 0.77 * priceIncrease
but the user losses approx. a half of investments. Then the attacker can repeat this attack with next users. Some of these users can decide to sell shares some - to keep. So there can be multiple attacks. The max tokenCount
when the attack is possible for any attackers is 20 shares. In case an attacker is controlled by the share creator (the role can be untrusted) the max tokenCount
is 100. In both cases the amount of shares does not reflect the amount of attacks because of different users behavior. The table shows the attackers profit for different tokenCount
values when the LinearBondingCurve
contract is used as a bondingCurve
.
Manual review
Consider using the curve where the next token can't be sold with sufficient profit. For example the price of the first share can be equal 100 * priceIncrease
. This makes frontrunning unprofitable even for the shares creators.
MEV
#0 - c4-pre-sort
2023-11-18T10:05:16Z
minhquanym marked the issue as duplicate of #12
#1 - c4-judge
2023-11-28T23:29:28Z
MarioPoneder marked the issue as satisfactory
π Selected for report: chaduke
Also found by: 0xpiken, Bauchibred, Matin, MohammedRizwan, MrPotatoMagic, OMEN, Pheonix, SandNallani, T1MOH, Topmark, ZanyBonzy, adriro, aslanbek, ayden, bareli, bart1e, bin2chen, btk, cheatc0d3, codynhat, critical-or-high, d3e4, erebus, firmanregar, hunter_w3b, jasonxiale, kaveyjoe, ksk2345, lsaudit, max10afternoon, merlinboii, nailkhalimov, osmanozdemir1, peanuts, pep7siup, pontifex, sbaudh6, shenwilly, sl1, tourist, wisdomn_, young, zhaojie
47.8152 USDC - $47.82
Due to the rounding losses an excess amount of cNOTE
tokens may be burnt. This can cause different problems including that some amount of asD
tokens can not be burnt, the owner of asD
loses a part of the accrued interest and even the draining of the contract assets.
The asD.mint
does not take into account the rounding losses at the cNOTE
contract and mints exactly the _amount
of asD
. It is not a problem when a user deposits n
tokens and then withdraws n
tokens. But if the user deposits several times n
tokens and then withdraws the whole value (k * n
) the rounding error will be in k
times less and the asD
contract will burn an excess amount of cNOTE
.
There is a POC with a real exchange rate for the NOTE/cNOTE
pair here. The test passes when the user mints 5e18 at once but not when the user deposits 5 times 1e18 NOTE
and then withdraws the whole amount.
Running 3 tests for src/test/asDR.t.sol:asDR [PASS] testBurn() (gas: 267387) Logs: cNOTE 4979798570281804344 [PASS] testMint() (gas: 217598) Logs: cNOTE 4979798570281804344 [FAIL. Reason: revert: ERC20: burn amount exceeds balance] testMintThenBurn() (gas: 565204) Logs: cNOTE 4979798570281804340
Manual review
Consider checking the actual minted amount of cNOTE
to mint the appropriate amount of asD
. It is obvious that the amount of asD
can be slightly less than deposited NOTE
but it will guarantee that all asD
tokens can be redeemed.
Other
#0 - c4-pre-sort
2023-11-20T17:58:31Z
minhquanym marked the issue as sufficient quality report
#1 - OpenCoreCH
2023-11-27T11:13:26Z
I think this is indeed a problem. Need to check if it is only in the test setup or also when using the real CLM, but I think they round in the same direction.
Severity seems a bit overinflated to me. Monetary damage would be ~0 (10^(-18) USD), but it is of course not good when a user cannot burn their whole balance and has to deduct a few weis.
#2 - OpenCoreCH
2023-11-28T19:34:51Z
CLM really rounds the same way, so it could happen in practice. However, the probability of it occurring should be very low for multiple reasons:
mint
and burn
, it does not happen.withdrawalCarry
, we also round down when calculating the maximum withdrawable amount, meaning that the owner can potentially withdraw 1 wei less because of the rounding. This could average out the rounding#3 - c4-sponsor
2023-11-28T19:35:00Z
OpenCoreCH marked the issue as disagree with severity
#4 - c4-sponsor
2023-11-28T19:35:05Z
OpenCoreCH (sponsor) confirmed
#5 - MarioPoneder
2023-11-28T22:20:20Z
The given deviation is miniscule and likelihood is low (see sponsor comment).
Nevertheless, the strict asD:NOTE = 1:1 relation is not always conserved, therefore moving forward with Medium
severity.
#6 - c4-judge
2023-11-28T22:20:27Z
MarioPoneder changed the severity to 2 (Med Risk)
#7 - c4-judge
2023-11-28T22:20:37Z
MarioPoneder marked the issue as satisfactory
#8 - c4-judge
2023-11-28T22:20:41Z
MarioPoneder marked the issue as selected for report
#9 - MarioPoneder
2023-11-30T01:22:08Z
Returning to this issue after judging the rest of the contest, it seems inconsistent and unfair to maintain Medium
severity in the present case.
Considering that the peg can only deviate by a miniscule an negligible amount, it's fair to move forward with QA.
#10 - c4-judge
2023-11-30T01:22:13Z
MarioPoneder marked the issue as not selected for report
#11 - c4-judge
2023-11-30T01:22:18Z
MarioPoneder changed the severity to QA (Quality Assurance)
#12 - c4-judge
2023-11-30T01:22:23Z
MarioPoneder marked the issue as grade-a
#13 - SovaSlava
2023-12-02T11:59:15Z
#14 - MarioPoneder
2023-12-04T12:49:49Z
Thank you for your comment!
According to https://docs.compound.finance/v2/ctokens/#exchange-rate:
Each cToken is convertible into an ever increasing quantity of the underlying asset, as interest accrues in the market.
Furthermore, the PoC shows an edge-case where asD
is immediately burned after minting.
Typically, times passes the interest accrued through cNOTE
in the meantime, see also the sponsor's comment:
If the exchange rate has increased (even if only a little bit) between mint and burn, it does not happen.
Therefore, maintaining QA.