Platform: Code4rena
Start Date: 28/06/2023
Pot Size: $52,500 USDC
Total HM: 10
Participants: 5
Period: 9 days
Judge: hansfriese
Total Solo HM: 6
Id: 255
League: ETH
Rank: 5/5
Findings: 1
Award: $0.00
π Selected for report: 2
π Solo Findings: 1
π Selected for report: __141345__
Data not available
https://github.com/AngleProtocol/angle-transmuter/blob/9707ee4ed3d221e02dcfcd2ebaa4b4d38d280936/contracts/transmuter/facets/Swapper.sol#L189 https://github.com/AngleProtocol/angle-transmuter/blob/9707ee4ed3d221e02dcfcd2ebaa4b4d38d280936/contracts/transmuter/facets/Swapper.sol#L210
This issue is an edge case, that uint128 changeAmount
could overflow, making the protocol fail for certain amount of swap.
Let's break down the changeAmount
:
amountOut/amountIn
normalizer
File: transmuter/contracts/transmuter/facets/Swapper.sol 176: function _swap() { 189: uint128 changeAmount = (amountOut.mulDiv(BASE_27, ts.normalizer, Math.Rounding.Up)).toUint128(); 210: uint128 changeAmount = ((amountIn * BASE_27) / ts.normalizer).toUint128();
normalizer
could take value ranging from 1e18 to 1e36, we take the worst case, 1e18.File: transmuter/contracts/transmuter/facets/Redeemer.sol 193: if (newNormalizerValue <= BASE_18 || newNormalizerValue >= BASE_36) { 208: newNormalizerValue = BASE_27; 209: }
BASE_27 is constant 1e27.
amountOut/amountIn
is AgToken with 18 decimals.
Combine the above 1, 2, 3, we have the "total decimal": 1e18 * 1e27 / 1e18 = 1e27.
The max value of uint128 is 3.4e38. To overflow, the amountOut/amountIn
need to be around 3.4e38 / 1e27 = 3.4e11.
For nowadays currencies around the world, we can look at Iranian Rial (IRR). 3.4e11 IRR is around 8M USD. 8M USD is a big amount for now. However considering the following, this concern maybe not nonsense:
If after 20 years, 3.4e11 of some currency with high inflation depreciates more than 100 times . In that case, the amount is around 80k USD, a reasonable amount to use (especially for institutions).
Although the possibility is low for current use cases, in the future, things could change. To make the stablecoin protocol universal for all countries in the world, the edge case might be some concern.
Manual analysis.
Use uint256 for changeAmount
, then the likelihood would be negligible small, making the system more robust to special situations. uint256 wont add too much cost, but can avoid much more potential issue.
Use narrower range for normalizer
. 1e9 for both up and down direction might be too big, and not necessary.
Under/Overflow
#0 - c4-judge
2023-07-08T12:18:56Z
hansfriese marked the issue as primary issue
#1 - Picodes
2023-07-10T09:16:12Z
Although the possibility of overflow is real, I don't think the scenario is convincing and this likely to happen. Worst case the code is already using safeCast
#2 - c4-sponsor
2023-07-10T09:16:17Z
Picodes marked the issue as disagree with severity
#3 - hansfriese
2023-07-10T11:21:44Z
Will keep it as medium because it's likely to happen and _swap()
will revert during the safeCast.
#4 - c4-judge
2023-07-10T11:21:53Z
hansfriese marked the issue as satisfactory
#5 - c4-judge
2023-07-10T15:40:05Z
hansfriese marked the issue as selected for report
π Selected for report: __141345__
Also found by: Lambda, auditor0517
Data not available
Here will share some thoughts in 2 aspects. Composability and Penalty Factor.
Composability is a huge advantage of DeFi, on the other side, it also could be a double sword. Considering Angle can work with other stablecoin system, and the sub-collateral is also introduced, the inter dependency could go quite complex later on. Some troublesome situation could be like: stableA use stableB as collateral, stableB use stableC as collateral, stableC again use stableA as collateral, ABC form a cycled collateral. If the cycle involves tens of tokens, it could be hard to spot the issue. But in the time of crisis, such inter dependent system will break quickly.
The idea of sub-collateral can help to expand the available assets for the protocol. On the other hand, more attention will be needed with regards to uncontrollable external contracts. It may need special attention, such as:
The same principal applies to other parts of the contract. Overall, the protocol works great in terms of modular implementation, by which I mean the Angle system has sub-modules, acting like LEGO. One developing suggestion might be not go too far in the compatibility with other protocols, just like the sub-collateral, maybe one layer of integration is good enough, but not too much.
The main suggestion is to add some kind of downside protection as last resort. Below is the reason.
The penalty factor can deter users from bank run, this is a clever design. But depending on the severity of the situation, the outcome might be quite different. Let's divide the users into 2 groups: "run" and "stay", and compare the 2 past big events: USDC and luna.
USDC It depegged to 0.87 and recovered afterwards. The penalty rule will work great. The "run" group will be penalized and the "stay" group will be rewarded. The most important is, USDC recovered at last.
luna It just went down to zero. At the end, everyone loses. At the beginning, the "run" group would be discouraged to exit. In the end, they ended up losing more because missing the earlier exit stop loss opportunity.
My understanding of some main stream stable coin is, it should stay pegged, or just go zero and exit. It's hard to imagine USDC valued at $0.65 after market rally (even if the loss is too big, the admin might try to burn some of the supply to recover the price, otherwise it has nothing to do with USD). Hence, the penalty factor rule can work great in the first situation with recover, but not for collapse case. When depeg comes, the protocol will hope no one get panic and bank run, but can not assure that everything is ok if staying. It will become a difficult trade off for users.
To deal with the worst situation, some protect such as insurance can be added. With the downside protection as last resort, users wont worry that much to join the "stay" group. Worst case, if this stable coin goes to zero, they know they are still covered anyway. The last protection could act as a patch for the penalty rule.
There are some defi insurance providers, for example unslashed, nexusmutual, insurace, bridgemutual. As for the choice of this kind of protection, the cheapest tier can be used, since the collapse situation is some extremely low possible black swan. The alternative could be put option, however the choice could be limited, and the cost might be higher.
One more thing, for such kind of insurance, if try to protect USDC, the payout should not be in USDC. Because if USDC goes to 0, it it pointless to receive huge payout still in USDC.
25 hours
#0 - c4-judge
2023-07-08T12:36:14Z
hansfriese marked the issue as grade-a
#1 - c4-judge
2023-07-10T15:54:02Z
hansfriese marked the issue as selected for report