Skip to content

3.0.0

Compare
Choose a tag to compare
@tbrent tbrent released this 02 Oct 23:34
· 866 commits to master since this release
9014df3

Bump solidity version to 0.8.19

Upgrade Steps

Required Steps

Update all component contracts, including Main.

Call the following functions:

  • BackingManager.cacheComponents()
  • RevenueTrader.cacheComponents() (for both rsrTrader and rTokenTrader)
  • Distributor.cacheComponents()

All asset plugins (and their corresponding ERC20s) must be upgraded. The only exception is the StaticATokenLM ERC20s from Aave V2. These can be left the same, however their assets should upgraded.

  • Note: Make sure to use Deployer.deployRTokenAsset() to create new RTokenAsset instances. This asset should be swapped too.

Optional Steps

Call the following functions, once it is desired to turn on the new features:

  • BasketHandler.setWarmupPeriod()
  • StRSR.setWithdrawalLeak()
  • Broker.setDutchAuctionLength()
  • Broker.setDutchTradeImplementation()

It is acceptable to leave these function calls out of the initial upgrade tx and follow up with them later. The protocol will continue to function, just without dutch auctions, RSR unstaking gas-savings, and the warmup period.

Core Protocol Contracts

  • AssetRegistry [+1 slot]
    Summary: Other component contracts need to know when refresh() was last called

    • Add lastRefresh() timestamp getter
    • Add size() getter for number of registered assets
    • Require asset is SOUND on registration
    • Bugfix: Fix gas attack that could result in someone disabling the basket
  • BackingManager [+2 slots]
    Summary: manageTokens was broken out into separate rebalancing and surplus-forwarding functions to allow users to more precisely call the protocol

    • Replace manageTokens(IERC20[] memory erc20s) with:
      • rebalance(TradeKind)
        • Modify trading algorithm to not trade RToken, and instead dissolve it when it has a balance above ~1e6 RToken quanta. "dissolve" = melt() with a basketsNeeded change, similar to redemption but without transfer of RToken collateral.
        • Use lotPrice() to set trade prices instead of price()
        • Add significant caching to save gas
      • forwardRevenue(IERC20[] memory erc20s)
        • Modify backingBuffer logic to keep the backing buffer in collateral tokens only. Fix subtle and inconsequential bug that resulted in not maximizing RToken minting locally, though overall RToken production does not change.
        • Use nonReentrant over CEI pattern for gas improvement. related to discussion of this cross-contract reentrancy risk
      • move nonReentrant up outside tryTrade internal helper
    • Remove manageTokensSortedOrder(IERC20[] memory erc20s)
    • Modify settleTrade(IERC20 sell) to call rebalance() when caller is a trade it deployed.
    • Remove all delegatecall during reward claiming; call claimRewards() directly on ERC20
    • Functions now revert on unproductive executions, instead of no-op
    • Do not trade until a warmupPeriod (last time SOUND was newly attained) has passed
    • Add cacheComponents() refresher to be called on upgrade
    • Add concept of tradeNonce
    • Bugfix: consider maxTradeVolume() from both assets on a trade, not just 1
  • BasketHandler [+5 slots]
    Summary: Introduces a notion of basket warmup to defend against short-term oracle manipulation attacks. Prevent RTokens from changing in value due to governance

    • Add new gov param: warmupPeriod with setter setWarmupPeriod(..) and event WarmupPeriodSet()
    • Add trackStatus() refresher
    • Add isReady() view
    • Extract basket switching logic out into external library BasketLibP1
    • Enforce setPrimeBasket() does not change the net value of a basket in terms of its target units
    • Add quoteCustomRedemption(uint48[] basketNonces, uint192[] memory portions, ..) to quote a linear combination of current-or-previous baskets for redemption
    • Add getHistoricalBasket(uint48 basketNonce) view
    • Bugfix: Protect against high BU price overflow
  • Broker [+2 slot]
    Summary: Add a second trading method for single-lot dutch auctions. Batch auctions via Gnosis EasyAuction are expected to be the backup auction going forward.

    • Add new dutch auction DutchTrade
    • Add minimum auction length of 20 blocks based on network block time
    • Rename variable auctionLength -> batchAuctionLength
    • Rename setter setAuctionLength() -> setBatchAuctionLength()
    • Rename event AuctionLengthSet() -> BatchAuctionLengthSet()
    • Add dutchAuctionLength and setDutchAuctionLength() setter and DutchAuctionLengthSet() event
    • Add dutchTradeImplementation and setDutchTradeImplementation() setter and DutchTradeImplementationSet() event
    • Modify setBatchTradeDisabled(bool) -> enableBatchTrade()
    • Modify setDutchTradeDisabled(IERC20 erc20, bool) -> enableDutchTrade(IERC20 erc20)
      • Unlike batch auctions, dutch auctions can be disabled per-ERC20, and can only be disabled by BackingManager-started trades
    • Modify openTrade(TradeRequest memory reg) -> openTrade(TradeKind kind, TradeRequest memory req, TradePrices memory prices)
      • Allow when paused / frozen, since caller must be in-system
  • Deployer [+0 slots]
    Summary: Support new governance params

    • Modify to handle new gov params: warmupPeriod, dutchAuctionLength, and withdrawalLeak
    • Do not grant OWNER any of the roles other than ownership
    • Add deployRTokenAsset() to allow easy creation of new RTokenAsset instances
  • Distributor [+2 slots]
    Summary: Restrict callers to system components and remove paused/frozen checks

    • Remove notPausedOrFrozen modifier from distribute()
  • Furnace [+0 slots]
    Summary: Allow melting while paused

    • Allow melting while paused
    • Melt during updates to the melting ratio
    • Lower MAX_RATIO from 1e18 to 1e14.
  • Main [+0 slots]
    Summary: Split pausing into two types of pausing: issuance and trading

    • Split paused into issuancePaused and tradingPaused
    • pause() -> pauseTrading() and pauseIssuance()
    • unpause() -> unpauseTrading() and unpauseIssuance()
    • pausedOrFrozen() -> tradingPausedOrFrozen() and issuancePausedOrFrozen()
    • PausedSet() event -> TradingPausedSet() and IssuancePausedSet()
  • RevenueTrader [+4 slots]
    Summary: QoL improvements. Make compatible with new dutch auction trading method

    • Remove delegatecall during reward claiming; call claimRewards() directly on ERC20
    • Add cacheComponents() refresher to be called on upgrade
    • manageToken(IERC20 sell) -> manageTokens(IERC20[] calldata erc20s, TradeKind[] memory kinds)
      • Allow multiple auctions to be launched at once
      • Allow opening dust auctions (i.e ignore minTradeVolume)
      • Revert on 0 balance or collision auction instead of no-op
      • Refresh buy and sell asset before trade
    • settleTrade(IERC20) now distributes tokenToBuy automatically, instead of requiring separate manageToken(IERC20) call
    • Add returnTokens(IERC20[] memory erc20s) to return tokens to the BackingManager when the distribution is set to 0
    • Add concept of tradeNonce
  • RToken [+0 slots]
    Summary: Provide multiple redemption methods for fullyCollateralized vs uncollateralized.

    • Gas: Remove exchangeRateIsValidAfter modifier from all functions except setBasketsNeeded()
    • Modify issuanceto revert beforewarmupPeriod`
    • Modify redeem(uint256 amount, uint48 basketNonce) -> redeem(uint256 amount). Redemptions are always on the current basket nonce and revert under partial redemption
    • Modify redeemTo(address recipient, uint256 amount, uint48 basketNonce) -> redeemTo(address recipient, uint256 amount). Redemptions are on the current basket nonce and revert under partial redemption
    • Add new redeemCustom(.., uint256 amount, uint48[] memory basketNonces, uint192[] memory portions, ..) function to allow redemption from a linear combination of current and previous baskets. During rebalancing this method of redemption may provide a higher overall redemption value than prorata redemption on the current basket nonce would.
    • Modify mint(address recipient, uint256 amtRToken) -> mint(uint256 amtRToken), since recipient is always BackingManager. Expand scope to include adjustments to basketsNeeded
    • Add dissolve(uint256 amount): burns RToken and reduces basketsNeeded, similar to redemption. Only callable by BackingManager
    • Modify setBasketsNeeded(..) to revert when supply is 0
    • Bugfix: Accumulate throttles upon change
  • StRSR [+2 slots]
    Summary: Add the ability to cancel unstakings and a withdrawal() gas-saver to allow small RSR amounts to be exempt from asset refreshes

    • Lower MAX_REWARD_RATIO from 1e18 to 1e14.
    • Remove duplicate stakeRate() getter (same as 1 / exchangeRate())
    • Add withdrawalLeak gov param, with setWithdrawalLeak(..) setter and WithdrawalLeakSet() event
    • Modify withdraw() to allow a small % of RSR to exit without paying to refresh all assets
    • Modify withdraw() to check for warmupPeriod
    • Add cancelUnstake(uint256 endId) to allow re-staking during unstaking
    • Add UnstakingCancelled() event
    • Allow payout of (already acquired) RSR rewards while frozen
    • Add ability for governance to resetStakes() when stake rate falls outside (1e12, 1e24)
  • StRSRVotes [+0 slots]

    • Add stakeAndDelegate(uint256 rsrAmount, address delegate) function to encourage people to receive voting weight upon staking

Facades

Remove FacadeMonitor - now redundant with nextRecollateralizationAuction() and revenueOverview()

  • FacadeAct
    Summary: Remove unused getActCalldata() and add way to run revenue auctions

    • Remove getActCalldata(..)
    • Remove canRunRecollateralizationAuctions(..)
    • Remove runRevenueAuctions(..)
    • Add revenueOverview(IRevenueTrader) returns ( IERC20[] memory erc20s, bool[] memory canStart, uint256[] memory surpluses, uint256[] memory minTradeAmounts)
    • Add nextRecollateralizationAuction(..) returns (bool canStart, IERC20 sell, IERC20 buy, uint256 sellAmount)
    • Modify all functions to work on both 3.0.0 and 2.1.0 RTokens
  • FacadeRead
    Summary: Add new data summary views frontends may be interested in

  • Remove basketNonce from redeem(.., uint48 basketNonce)

  • Add redeemCustom(.., uint48[] memory basketNonces, uint192[] memory portions) callstatic to simulate multi-basket redemptions

  • Remove traderBalances(..)

  • Add balancesAcrossAllTraders(IBackingManager) returns (IERC20[] memory erc20s, uint256[] memory balances, uint256[] memory balancesNeededByBackingManager)

  • FacadeWrite
    Summary: More expressive and fine-grained control over the set of pausers and freezers

    • Do not automatically grant Guardian PAUSER/SHORT_FREEZER/LONG_FREEZER
    • Do not automatically grant Owner PAUSER/SHORT_FREEZER/LONG_FREEZER
    • Add ability to initialize with multiple pausers, short freezers, and long freezers
    • Modify setupGovernance(.., address owner, address guardian, address pauser) -> setupGovernance(.., GovernanceRoles calldata govRoles)

Plugins

DutchTrade

A cheaper, simpler, trading method. Intended to be the new dominant trading method, with GnosisTrade (batch auctions) available as a backup option. Generally speaking the batch auction length can be kept shorter than the dutch auction length.

DutchTrade implements a four-stage, single-lot, falling price dutch auction:

  1. In the first 20% of the auction, the price falls from 1000x the best price to the best price in a geometric/exponential decay as a price manipulation defense mechanism. Bids are not expected to occur (but note: unlike the GnosisTrade batch auction, this mechanism is not resistant to arbitrary price manipulation). If a bid occurs, then trading for the pair of tokens is disabled as long as the trade was started by the BackingManager.
  2. Between 20% and 45%, the price falls linearly from 1.5x the best price to the best price.
  3. Between 45% and 95%, the price falls linearly from the best price to the worst price.
  4. Over the last 5% of the auction, the price remains constant at the worst price.

Duration: 30 min (default)

Assets and Collateral

  • Add version() return (string) getter to pave way for separation of asset versioning and core protocol versioning
  • Deprecate claimRewards()
  • Add lastSave() to RTokenAsset
  • Remove CurveVolatileCollateral
  • Switch CToken*Collateral (Compound V2) to using a CTokenVault ERC20 rather than the raw cToken
  • Bugfix: lotPrice() now begins at 100% the lastSavedPrice, instead of below 100%. It can be at 100% for up to the oracleTimeout in the worst-case.
  • Bugfix: Handle oracle deprecation as indicated by the aggregator() being set to the zero address
  • Bugfix: AnkrStakedETHCollateral/CBETHCollateral/RethCollateral now correctly detects soft default (note that Ankr still requires a new oracle before it can be deployed)
  • Bugfix: Adjust Curve*Collateral and RTokenAsset to treat FIX_MAX correctly as +inf
  • Bugfix: Continue updating cached price after collateral default (impacts all appreciating collateral)