BIP-48: Whitelist BEANwstETH and Migrate Unripe Liquidity

Proposed: July 19, 2024

Status: Passed

Link: Snapshot


Proposer

Beanstalk Farms, Brendan Sanderson, Ben Weintraub

Proposer Wallet: 0xf1a621fe077e4e9ac2c0cefd9b69551db9c3f657

Summary

Problem

Opportunities for Bean liquidity providers to receive Stalk and Seed rewards are currently limited. A wider variety of options increases utility for Silo Depositors and will be possible with the combination of the Seed Gauge System and Generalized Convert.

Beanstalk could be offering higher yield opportunities to Bean liquidity providers, including Unripe LP holders, via the ETH staking yield with minimal additional trust assumptions.

As of writing, over 99% of liquidity for Beans is currently in the BEANETH Well, which uses Multi Flow v1.0.0. Multi Flow v1.0.0 does not have a way to change the exchange rate between Well reserves when the cap on the maximum percent change in reserves per block is reached (see EBIP-9).

Beanstalk currently does not support multiple Unripe LP tokens.

Currently, the ETH:USD oracle defaults to using Chainlink when the Chainlink price is greater than 1% different than the prices in the ETH:USDC and ETH:USDT Uniswap V3 pools (which is considered manipulation).

Proposed Solution

Whitelist a BEAN:wstETH Constant Product 2 Well with Multi Flow Pump v1.1.0 for Deposit in the Silo.

We propose to set the initial Gauge Points of BEANwstETH to 400 and set the optimal distribution of the LP Seed Gauge System to 80% BEANwstETH and 20% BEANETH.

wstETH:USD Oracle Design

Beanstalk requires a wstETH:USD oracle in order to compute deltaB in the BEAN:wstETH Well.

The oracle reads from multiple data sources and calculates the wstETH:ETH price using 3 separate methods:

  1. The wstETH:stETH redemption rate (A) * the stETH:ETH Chainlink price feed (B);
  2. The wstETH:WETH Uniswap V3 pool; and
  3. (A).

And computes a final wstETH:ETH price via the following logic:

Multiplying the wstETH:ETH and ETH:USD prices results in the final wstETH:USD price.

Multi Flow v1.1.0

Multi Flow v1.1.0 introduces the ability to set the maximum percent change in exchange rate between any two tokens in a Well per block. We propose setting this parameter to 0.5%.

Multi Flow v1.1.0 also introduces the ability the set the maximum percent change in LP token supply in a Well per block. We propose setting this parameter to 3%.

Because the existing BEANETH Well uses Multi Flow v1.0.0 which does not contain these improvements, we propose turning off minting from the BEANETH Well for 24 Seasons upon the deployment of this BIP while the reserves stored in its Pump catch up to the reserves in the Well post-Unripe liquidity migration.

Migration Process

Migrate the BEANETH Well LP tokens that underly the urBEANETH token to BEANwstETH Well LP tokens. In doing so, the liquidity underlying urBEANETH is migrated to the BEANwstETH Well and the urBEANETH token becomes the urBEANwstETH token (albeit, with the same token address).

Transaction # Protocol Description
1 Beanstalk Execute the BIP Diamond Cut and transfer underlying BEANETH to the BCM
2 Basin Remove BEANETH liquidity as Bean and WETH proportionate to the ratio of the pool
3 CoW Swap Swap WETH into stETH using TWAP order*
4 Lido Wrap stETH into wstETH
5 Basin Add Beans from Step (2) and wstETH from Step (4) as liquidity to the BEANwstETH Well
6 Beanstalk Add BEANwstETH LP as underlying token for the new urBEANwstETH

*If the DAO can get a better execution price by directly staking WETH for stETH (rather than swapping), the BCM will do this.

During the migration process, attempts to Deposit, Convert or Chop Unripe assets will revert. After the migration process is complete, Conversions between urBEAN and urBEANwstETH will Convert in/out of the BEANwstETH Well instead of the BEANETH Well.

The cost of slippage during Step (3) is passed on to Unripe LP holders.

Fertilizer Changes

Upgrade the Barn to add liquidity to the BEANwstETH Well instead of the BEANETH Well upon each Fertilizer purchase.

As a result:

ETH:USD Oracle Design

Beanstalk requires an ETH:USD oracle in order to compute deltaB in the BEAN:ETH and BEAN:wstETH Wells. We propose to remove Uniswap V3 as an input to this price oracle, such that Beanstalk calculates the ETH:USD price exclusively using Chainlink.

Technical Rationale

The Season Facet must be updated to generalize stepOracle to support minting in any whitelisted Well.

Given that the migration process cannot be executed atomically and Unripe asset functionality will be in a broken state during it, attempts to Deposit, Convert or Chop Unripe assets during the migration process will revert.

The Fertilizer Facet must be updated to use the new Barn Raise token (wstETH), which is now abstracted away via the new LibBarnRaise library.

The Unripe Facet, BDV Facet, LibConvert, LibUnripeConvert, LibEvaluate and LibFertilizer must be updated to reflect the use of LibBarnRaise.

All of the oracle libraries are updated to be generalized to support reading data for any tokens that Beanstalk supports.

Economic Rationale

Beanstalk could be offering higher yield opportunities to Bean liquidity providers (and Unripe LP holders) via the ETH staking yield with minimal additional trust assumptions. stETH has the largest TVL of any liquid staking derivative. All other liquid staking derivatives with reasonable market adoption – a requirement for sufficient decentralization – still have arbitrary and unilateral upgradability privileges held by a small party.

Basin does not have a Well Implementation that tracks rebasing tokens. Thus, Basin (and thus, Beanstalk) must use wstETH (i.e., the wrapped, non-rebasing version of stETH).

Setting the optimal LP BDV distribution to 80% BEANwstETH and 20% BEANETH reflects the preference for higher yield opportunities to Depositors. Setting the BEANwstETH Gauge Points to 400 initializes the Gauge Point distribution with the same proportions as the optimal LP BDV distribution (BEANETH currently has 100 Gauge Points).

There is no wstETH:USD Chainlink feed on Ethereum mainnet. Using 3 sources for the wstETH:ETH prices increases accuracy and reliability.

For as long as most liquidity for Beans is in the existing BEANETH Well, Beanstalk is still subject to risks associated with Multi Flow v1.0.0 that were pointed out in EBIP-9.

Setting the percent exchange rate change per block cap and percent LP token supply change per block cap in Multi Flow v1.1.0 to 0.5% and 3%, respectively, minimizes both potential inter-block MEV manipulation and instances where the Pump reserves are out of sync with the Well reserves. Investigation of past data in the ETH:USDC and ETH:USDT Uniswap V2 pools can be found here.

The Uniswap V3 component of the existing ETH:USD oracle serves little to no purpose given that it already defaults to Chainlink in the case of manipulation.

Contract Changes

Initialization Contract

The init function on the following InitMigrateUnripeBeanEthToBeanSteth contract is called:

BDV Facet

The following BDVFacet is being removed from Beanstalk:

The following BDVFacet is added to Beanstalk:

BDVFacet Function Changes

Name Selector Action Type New Functionality
beanToBDV 0x5a049a47 Replace Read
curveToBDV 0xf984019b Replace Read
unripeBeanToBDV 0xc8cda2a0 Replace Read
unripeLPToBDV 0xb0c22bb1 Replace Read
wellBdv 0xc84c7727 Replace Read

Convert Facet

The following ConvertFacet is removed from Beanstalk:

The following ConvertFacet is added to Beanstalk:

ConvertFacet Function Changes

Name Selector Action Type New Functionality
convert 0xb362a6e8 Replace Write

Convert Getters Facet

The following ConvertGettersFacet is removed from Beanstalk:

The following ConvertGettersFacet is added to Beanstalk:

ConvertGettersFacet Function Changes

Name Selector Action Type New Functionality
getAmountOut 0x4aa06652 Replace View
getMaxAmountIn 0x24dd285c Replace View

Enroot Facet

The following EnrootFacet is removed from Beanstalk:

The following EnrootFacet is added to Beanstalk:

EnrootFacet Function Changes

Name Selector Action Type New Functionality
enrootDeposit 0x0b58f073 Replace Write
enrootDeposits 0x88fcd169 Replace Write

Fertilizer Facet

The following FertilizerFacet is removed from Beanstalk:

The following FertilizerFacet is added to Beanstalk:

FertilizerFacet Function Changes

Name Selector Action Type New Functionality
mintFertilizer 0xbb02e10b Remove Write
_getMintFertilizerOut 0x94daa221 Add Read
beginBarnRaiseMigration 0xe3d4e44c Add Write
getBarnRaiseToken 0xf255da60 Add Read
getBarnRaiseWell 0x93a39bea Add Read
mintFertilizer 0x363591d0 Add Write
balanceOfBatchFertilizer 0x304ec65d Replace Read
balanceOfFertilized 0xb6f42085 Replace Read
balanceOfFertilizer 0x1799b3b2 Replace Read
balanceOfUnfertilized 0x1edb6be1 Replace Read
beansPerFertilizer 0x9bb4e35a Replace Read
claimFertilized 0x83e08888 Replace Write
getActiveFertilizer 0xdc6ba285 Replace Read
getCurrentHumidity 0x39448802 Replace Read
getEndBpf 0xc85951a1 Replace Read
getFertilizer 0x9c45a1d5 Replace Read
getFertilizers 0x34af5416 Replace Read
getFirst 0x1e223143 Replace Read
getHumidity 0x29130a66 Replace Read
getLast 0x4d622831 Replace Read
getMintFertilizerOut 0x69744dd0 Replace Read
getNext 0xf4a057e2 Replace Read
isFertilizing 0x6ae1c014 Replace Read
payFertilizer 0xd47aee59 Replace Write
remainingRecapitalization 0x4a16607c Replace Read
totalFertilizedBeans 0x4f9a9678 Replace Read
totalFertilizerBeans 0xf9c4ebde Replace Read
totalUnfertilizedBeans 0xa3ef48c9 Replace Read

Metadata Facet

The following MetadataFacet is being removed from Beanstalk:

The following MetadataFacet is added to Beanstalk:

MetadataFacet Function Changes

Name Selector Action Type New Functionality
imageURI 0xc20b8071 Replace Read
name 0x06fdde03 Replace Read
symbol 0x95d89b41 Replace Read
uri 0x0e89341c Replace Read

Season Facet

The following SeasonFacet is removed from Beanstalk:

The following SeasonFacet is added to Beanstalk:

SeasonFacet Function Changes

Name Selector Action Type New Functionality
gm 0x64ee4b80 Replace Write
seasonTime 0xca7b7d7b Replace Read
sunrise 0xfc06d2a6 Replace Write

Season Getters Facet

The following SeasonGettersFacet is removed from Beanstalk:

The following SeasonGettersFacet is added to Beanstalk:

SeasonGettersFacet Function Changes

Name Selector Action Type New Functionality
abovePeg 0x2a27c499 Replace Read
calcGaugePointsWithParams 0xeedc7ab9 Replace Read
getAverageGrownStalkPerBdv 0x7ba6cbf8 Replace Read
getAverageGrownStalkPerBdvPerSeason 0xeb0e1215 Replace Read
getBeanEthGaugePointsPerBdv 0xd1db56b8 Replace Read
getBeanGaugePointsPerBdv 0x69aa7e02 Replace Read
getBeanToMaxLpGpPerBdvRatio 0xcc88d4f9 Replace Read
getBeanToMaxLpGpPerBdvRatioScaled 0x673c75f0 Replace Read
getDeltaPodDemand 0x64b3496b Replace Read
getGaugePoints 0x93523425 Replace Read
getGaugePointsPerBdvForToken 0x64887852 Replace Read
getGaugePointsPerBdvPerWell 0xb2b0556d Replace Read
getGaugePointsWithParams 0x141933bf Replace Read
getGrownStalkIssuedPerGp 0xf98da2de Replace Read
getGrownStalkIssuedPerSeason 0x383f170f Replace Read
getLargestLiqWell 0xd1943f7f Replace Read
getLiquidityToSupplyRatio 0xcb2d0a3c Replace Read
getPodRate 0xcce813a1 Replace Read
getSeedGauge 0x6af8e5a4 Replace Read
getSopWell 0x7d23804d Replace Read
getTotalBdv 0x50539159 Replace Read
getTotalUsdLiquidity 0xbbf459a7 Replace Read
getTotalWeightedUsdLiquidity 0xf788b47c Replace Read
getTwaLiquidityForWell 0xa13a3742 Replace Read
getWeightedTwaLiquidityForWell 0x93c9e531 Replace Read
paused 0x5c975abb Replace Read
plentyPerRoot 0xe60d7a83 Replace Read
poolDeltaB 0x471bcdbe Replace Read
rain 0x43def26e Replace Read
season 0xc50b0fb0 Replace Read
sunriseBlock 0x3b2ecb70 Replace Read
time 0x16ada547 Replace Read
totalDeltaB 0x06c499d8 Replace Read
weather 0x686b6159 Replace Read
wellOracleSnapshot 0x597490c0 Replace Read

Unripe Facet

The following UnripeFacet is removed from Beanstalk:

The following UnripeFacet is added to Beanstalk:

UnripeFacet Function Changes

Name Selector Action Type New Functionality
getLockedBeansUnderlyingUnripeBeanEth 0x208c2c98 Remove Read
getLockedBeansUnderlyingUnripeLP 0x33f37f27 Add Read
_getPenalizedUnderlying 0xa84643e4 Replace Read
addMigratedUnderlying 0x787cee99 Replace Write
addUnripeToken 0xfa345569 Replace Write
balanceOfPenalizedUnderlying 0x1acc0a47 Replace Read
balanceOfUnderlying 0x1be655e8 Replace Read
chop 0x9a516cad Replace Write
getLockedBeans 0x087d78b4 Replace Read
getLockedBeansFromTwaReserves 0x7caa025f Replace Read
getLockedBeansUnderlyingUnripeBean 0xbfe2f3be Replace Read
getPenalizedUnderlying 0x6de45df2 Replace Read
getPenalty 0x014a8a49 Replace Read
getPercentPenalty 0xbb7de478 Replace Read
getRecapFundedPercent 0x43cc4ee0 Replace Read
getRecapPaidPercent 0xab434eb7 Replace Read
getTotalUnderlying 0xadef4533 Replace Read
getUnderlying 0x9f06b3fa Replace Read
getUnderlyingPerUnripeToken 0xb8a04d1b Replace Read
getUnderlyingToken 0x691bcc88 Replace Read
isUnripe 0xfc6a19df Replace Read
pick 0x13ed3cea Replace Write
picked 0xd3c73ec8 Replace Read
switchUnderlyingToken 0xa33fa99f Replace Write

Whitelist Facet

The following WhitelistFacet is removed from Beanstalk:

The following WhitelistFacet is added to Beanstalk:

WhitelistFacet Function Changes

Name Selector Action Type New Functionality
dewhitelistToken 0x86b40a1b Replace Write
getSiloTokens 0xe9522c08 Add Read
getWhitelistStatus 0xd9ba32fc Add Read
getWhitelistStatuses 0x170cf084 Add Read
getWhitelistedLpTokens 0x9d1d2877 Add Read
getWhitelistedTokens 0xe26f7900 Add Read
getWhitelistedWellLpTokens 0x76a7bc84 Add Read
updateGaugeForToken 0xce5fb821 Add Write
updateStalkPerBdvPerSeasonForToken 0xf18d9ed0 Replace Write
whitelistToken 0x371b5b03 Add Write
whitelistTokenWithEncodeType 0x052ebc26 Add Write

Event Changes

None.

Beans Minted

None.

Audit

Beanstalk

The commit hash of this BIP is 45afeaf1f9c57bcdf506336aff63fa8805a1081f.

An audit competition of this upgrade was held via Codehawks using commit hash 27ff8c87c9164c1fbff054be5f22e56f86cdf127. The final report can be read here.

Audit remediations were committed and documented in PR #922. All changes were reviewed by Cyfrin.

Basin

The commit hash of Multi Flow v1.1.0 is 8d54b04fd24ec621966f30683a90d4985c6bd6db.

An audit competition of this upgrade was held via Codehawks using commit hash 038609d8adf1bf941f8abd12820bc92ecd998375. The final report can be read here.

Audit remediations were committed and documented in PR #132. All changes were reviewed by Cyfrin.

Effective

Immediately upon commitment.