Reserve - Versus Mitigation contest

A permissionless platform to launch and govern asset-backed stable currencies.

  • Start date13 Feb 2023
  • End date17 Feb 2023
  • Total awards$45,000 USDC
  • Duration4 days

Reserve - Mitigation contest details

Important note

Each warden must submit a mitigation review for every High and Medium finding from the parent contest. Incomplete mitigation reviews will not be eligible for awards.

Findings being mitigated

High Risk Findings

Medium Risk Findings

Overview of changes

The sponsors have made many, many changes, in response to the thoughtful feedback from the Wardens. In most cases changes were straightforward and of limited scope, but in at least two cases there were significant reductions or simplifications of large portions of the code. These areas are expanded upon below in their own sections. The 3rd section will cover everything else:

1. Removal of non-atomic RToken issuance (M-13, M-15)

PR #571: remove non-atomic issuance

This audit, as in previous audits (ToB; Solidified) problems were found with the RToken issuance queue, a fussy cumulative data structure that exists to support constant-time cancel() and vest() operations for non-atomic issuance. This audit too, another issue was discovered with M-13. This prompted us to look for alternatives that achieve a similar purpose to the issuance queue, leading to removal of non-atomic issuance entirely and creation of the issuance throttle. The issuance throttle is at a low-level mechanistically similar to the redemption battery from before, except it is a net hourly issuance measure. This addresses the problem of ingesting large amounts of bad collateral too quickly in a different way and with less frictions for users, both in terms of time and gas fees.

As wardens will see, large portions of the RToken contract code have been removed. This also freed up contract bytecode real estate that allowed us to take libraries internal that were previously external.

Context: Original purpose of issuance queue

The original purpose of the issuance queue was to prevent MEV searchers and other unspeakables from depositing large amounts of collateral right before the basket becomes IFFY and issuance is turned off. The overall IFFY -> DISABLED basket flow can be frontrun, and even though the depositer does not know yet whether a collateral token will default, acquiring a position in the queue acts like a valuable option that pays off if it does and has only opportunity cost otherwise. From the protocol's perspective, this kind of issuance just introduces bad debt.

The new issunce throttle is purely atomic and serves the same purpose of limiting the loss due to bad debt directly prior to a collateral default.

2. Tightening of the basket range formula (H-02, M-20, M-22)

PR #585: Narrow bu band

H-02 is the other highly consequential change, from a sheer quantity of SLOC point of view. Indeed, the calculation of the top and bottom of the basket range was highly inefficient and would generally result in larger haircuts than desirable. Below are two datapoints from tests that show the severity of a haircut after default in the absence of RSR overcollateralization:

  • 37.5% loss + market-even trades
    • Before: 39.7% haircut
    • After: 37.52% haircut
  • 15% loss + worst-case below market trades
    • Before: 17.87% haircut
    • After: 16.38% haircut

The previous code was more complicated, more costly, and provided worse outcomes. In short this was because it didn't distinguish between capital that needed to be traded vs capital that did not. While the protocol cannot know ahead of time exactly how many BUs it will have after recollateralization, it can use the number of basket units currently held as a bedrock that it knows it will not need to trade, and thus do not differentially contribute to basket.top and basket.bottom.

Related issues

In addition to H-02 this PR also addressed M-20 and M-22, which are related to the calculation of the dust loss and potential overflow during the shortfall calculation. The calculation of the dust loss is now capped appropriately and the shortfall calculation has been eliminated.

3. Everything else

The mitigations for the remaining issues were more narrow in scope. Most do not require further context or description. But there are 2 smaller clusters of changes worth calling out:

Universal Revenue Hiding

PR #620: Universal revenue hiding

As a warden pointed out in H-01, there are subtleties that can cause the compound v2 cToken rate to decrease, albeit by extremely little. Since we have dealt with Compund V2 for so long, and only just discovered this detail, we reason there are probably more like it.

To this end we've implemented universal revenue hiding at the collateral plugin level, for all appreciating collateral. The idea is that even a small amount of revenue hiding such as 1-part-in-1-million may end up protecting the collateral plugin from unexpected default while being basically undetectable to humans.

We mention this change because it can potentially impact other areas of the protocol, such as what prices trades are opened at, or how the basket range is calculated during recollateralization. A warden looking to examine this should focus their attention on contracts/assets/AppreciatingFiatCollateral.sol.

Redemption while DISABLED

PR #575: support redemption while disabled

The final change area to bring attention to is the enabling of RToken redemption while the basket is DISABLED. The motivation for this change is not neatly captured in a single contest issue, though it was something discussed with wardens via DM, and which seems tangentially related to issues like M-03.

Previous behavior: Cannot redeem while DISABLED. BasketHandler.refreshBasket() must be called before first redemption can occur, and even then, the redeemer must wait until trading finishes to receive full redemptions.

Current behavior: Can redeem while DISABLED. Will get full share of collateral until BasketHandler.refreshBasket() is called. Can use revertOnPartialRedemption redemption param to control behavior along this boundary.

We mention this change because functionality under different basket conditions is central to the functioning of our protocol. RToken redemption is how capital primarily exits the system, so any change to this area is fundamentally risky.

A related change is that BasketHandler._switchBasket() now skips over IFFY collateral.

Mitigations to be reviewed

URLMitigation ofPurpose
https://github.com/reserve-protocol/protocol/pull/571M-13, M-15This PR removes the non-atomic issuance mechanism and adds an issuance throttle. The redemption battery is rebranded to a redemption throttle.
https://github.com/reserve-protocol/protocol/pull/585H-02, M-20, M-22This PR simplifies and improves the basket range formula. The new logic should provide much tighter basket range estimates and result in smaller haircuts.
https://github.com/reserve-protocol/protocol/pull/584M-01, M-12, M-23, M-25This PR bundles mitigations for many small issues together. The full list is in the PR description. Each of these items are small and local in scope.
https://github.com/reserve-protocol/protocol/pull/575M-24This PR enables redemption while the basket is DISABLED.
https://github.com/reserve-protocol/protocol/pull/614M-18This PR removes the ability to change StRSR token's name and symbol.
https://github.com/reserve-protocol/protocol/pull/615M-03, M-04This PR allows an RToken redeemer to specify when they require full redemptions vs accept partial (prorata) redemptions.
https://github.com/reserve-protocol/protocol/pull/617M-02This PR prevents paying out StRSR rewards until the StRSR supply is at least 1e18.
https://github.com/reserve-protocol/protocol/pull/619M-05This PR prevents melting RToken until the RToken supply is at least 1e18.
https://github.com/reserve-protocol/protocol/pull/620H-01This PR adds universal revenue hiding to all appreciating collateral.
https://github.com/reserve-protocol/protocol/pull/622M-11This PR adds a Furnace.melt()/StRSR.payoutRewards() step when governance changes the rewardRatio.
https://github.com/reserve-protocol/protocol/pull/623M-16, M-21This PR makes the AssetRegistry more resilient to bad collateral during asset unregistration, and disables staking when frozen.
https://github.com/reserve-protocol/protocol/pull/628M-10This PR makes all dangerous uint192 downcasts truncation-safe.

The sponsors want to emphasize this is not the complete list of changes between the original df7eca commit and the mitigation review 27a347 commit. While it is the vast majority of the changes, we urge wardens to check out the diff between the two commits for themselves, as changes may have been made due to addressing gas/QA findings.