BeanstalkFarms/Beanstalk

RFC: Seed Gauge System

Closed this issue · 0 comments

RFC: Seed Gauge System

[DRAFT]

Authors

Ben Weintraub, Brendan Sanderson, Brean, Guy

Summary

  • Implement the Seed Gauge System;
    • Measure the Bean Liquidity to Supply Ratio (L2SR);
    • Add support for changing Temperature on a relative scale;
    • Support the change in allocation of Seeds to Beans vs. LP tokens based on the L2SR, deltaB, Pod Rate, and change in Soil demand;
    • Support the change in allocation Seeds to various LP tokens based on a predefined gauge;
  • Initialize the Seed Gauge System;
    • Allocate 100% of LP gauge points to BEANETH;
    • Set the minimum ratio of Seeds to Beans and Seeds to the LP token with the highest number of gauge points per BDV to 1:2;
    • Set the maximum ratio of Seeds to Beans and Seeds to the LP token with the highest number of gauge points per BDV to 1:1;
    • Set the target number of Seasons for a new Depositor to catch up to the average Grown Stalk per BDV to 4320 Seasons (~6 months);
  • Implement the second step in a two step process to track Deposited BDV that has not migrated to Silo V3;
  • Implement an excessive Bean price threshold case (P > $1.05);
  • Support Unripe lambda to lambda Conversions in the Silo;
  • Remove BEAN3CRV from the Deposit and Minting Whitelists;
  • Remove the BEAN → BEAN3CRV Conversion from the Convert Whitelist; and
  • Remove the 10 block delay in Earned Bean issuance and implement a 2 Season delay in receiving Earned Beans after Deposit.

Problem

Seeds per BDV for whitelisted assets are currently static and can only be changed via governance.

The L2SR is an important indicator of Beanstalk's health, but Beanstalk currently does not measure it or use it to evaluate its state.

Beanstalk currently changes Temperature on an absolute scale, and does not support the ability to change it on a relative scale.

Beanstalk does not currently target an amount of grown stalk issued per season.

Farmers can only Chop Unripe assets by forfeiting all their associated Grown Stalk.

BDV that has not been migrated to Silo V3 is not included in s.siloBalances[token].depositedBdv. For Beanstalk to support a LP gauge system, it is necessary to track the total Deposited BDV for each whitelisted token.

Beanstalk does not respond differently to an excessively high Bean price.

Unripe Assets are not able to stay in the silo and retain grown stalk when redeeming for their ripe counterpart, reducing the stickiness of tokens from unripe assets.

BEAN3CRV:

  • Is exposed to the centralization risks of USDC, USDT and DAI.
  • Uses an oracle that is not resistant to inter-block MEV manipulation; and
  • Is subject to unnecessary trading fees that negatively affect peg maintenance.

Implementing the Seed Gauge System without any changes to Earned Bean issuance would open up potential vectors for manipulation of Deposited BDV, and thus Seed allocations for various whitelisted assets.

Solution

Add the Seed Gauge System to the Silo.

Liquidity to Supply Ratio

Beanstalk now calculates the Bean L2SR when evaluating its state.

Liquidity is defined as the USD value of the non-Bean assets trading against Bean that are whitelisted in the Silo. Supply is defined as the total Bean supply minus the Beans underlying Unripe assets.

Beanstalk can be in 4 states from the L2SR:

  • Excessively high L2SR; above 80%
  • Moderately high L2SR; between 40% and 80%
  • Moderately low L2SR; or between 12% and 40%
  • Excessively low L2SR. below 12%

Locked Beans

Due to the Barn Raise and the associated Beans underlying Unripe assets, the number of tradable Beans does not equal the total Bean supply. At the time of writing, ~0.224 Beans underlying each Unripe Bean, but holders can only redeem 0.006 Beans per Unripe Bean. Thus, ~0.218 Beans Per Unripe are considered not tradable. This is calculated and omitted from the Bean supply in LibEvaluate.calcLPToSupplyRatio() via LibUnripe.getLockedBeans().

Cases V2

Case Evaluation

Cases V1 calculated the state of Beanstalk based on the price (deltaB), Pod Rage, and change in Soil demand. This resulted in 24 cases for Beanstalk's state (2 price states * 4 Pod Rate states * and 3 Soil demand states). Based on the case, Beanstalk adjusted the Temperature.

Cases V2 now measures the L2SR and adds an additional case for price (excessively high price, P > Q) when calculating the state of Beanstalk. This now results in 144 cases (3 price states * 4 Pod Rate states * 3 Soil demand states * and 4 L2SR states). Based on the case, Beanstalk adjusts two parameters:

  • Temperature; and
  • BeanToMaxLpGaugePointPerBDV Scalar (See Bean to MaxLp GaugePoints Per BDV Section)

P > Q

During times where the price is excessively high, Beanstalk may want to adjust its parameters differently.

P is calculated like so:

$$P = \frac{USD/ETH}{BEAN/ETH}$$

Beanstalk assumes that sufficient arbitrage occurs between the BEANETH Well, and other pools trading beans.

EthUsdOracle

Beanstalk now temporarily stores the EthUsdPrice to reduce redundant calculation of the EthUsdPrice. This uses the same logic as how the gm incentive uses the beanEthPrice thats temporarily stored.

Case Calculation

In Cases V1, Beanstalk was only able to adjust the Temperature absolutely. In other words, Temperature could only increase/decrease incrementally (i.e +/- 3%). Cases V2 allows beanstalk to adjust the Temperature and BeanToMaxLpGaugePointPerBDV Scalar by a relative amount (i.e 110% of the previous value).

Temperature and BeanToMaxLPRatio Update

Beanstalk now updates the Temperature and the BeanToMaxLpGaugePointPerBDV Scalar like so:

$$t_{s+1} = max(m_t * t_{s} + b_t,1)$$

$$l_{s+1} = max(min(m_l * l_{s} + b_l,0), 100)$$

Temperature has a minimum of 1%, and the BeanToMaxLpGaugePointPerBDV Scalar ranges between $[0,1]$. See (Bean to MaxLp GaugePoints Per BDV) on how it is used.

Case Values

In Cases V1, only 1 parameter was stored (Temperature increase/decrease). Cases V2 stores 4 parameter for each case:
- Relative Temperature Adjustment ($mT$ with 6 decimal precision, i.e., 100% = 100e6)
- Absolute Temperature Adjustment ($bT$ with 2 decimal precision, i.e., 100% = 1e2)
- Relative BeanToMaxLpGaugePointPerBDV Scalar Adjustment ($mL$ with 18 decimal precision, i.e., 100% = 100e18)
- Absolute BeanToMaxLpGaugePointPerBDV Scalar Adjustment ($bL$) with 18 decimal precision, i.e., 100% = 100e18)

This is stored as a bytes32, which is decoded when used. 7 bytes are left unused for potential future development.

struct CaseData {
        uint32 mT;
        int8 bT;
        uint80 mL;
        int80 bL;
    }

StepGauge

AverageGrownStalkPerBDVPerSeason

The Seed Gauge System now allows Beanstalk to target an amount of Grown Stalk per BDV that should be issued per Season.
This is updated by calling LibGauge.updateStalkPerBdvPerSeason(). The current target is the amount of Grown Stalk per BDV that should be issued, such that an average new Depositor (i.e no Grown Stalk), will catch up to the current average Depositor (i.e the current average Grown Stalk per BDV), in X Seasons, where X is set at 4320 Seasons (6 months).

Gauge Points

Gauge Points are a new value introduced in the Seed Gauge, which determine how the Grown Stalk issued that Season should be distributed between whitelisted LP tokens. Only whitelisted LP tokens can have Gauge Points stored. Gauge Points have 18 decimal precision (1e18).

Every Season, Beanstalk calculates the amount of Gauge Points a LP token should have. To calculate it, Beanstalk calls the gaugePoint Selector stored for each LP asset. For the initial implementation, all LP assets use the function below:

function defaultGaugePointFunction(
        uint256 currentGaugePoints,
        uint256 optimalPercentDepositedBdv,
        uint256 percentOfDepositedBdv
    ) external pure returns (uint256 newGaugePoints) {
        if(percentOfDepositedBdv > optimalPercentDepositedBdv){
            if(currentGaugePoints < ONE_PERCENT) return 0;
            newGaugePoints = currentGaugePoints.sub(ONE_PERCENT);
        } else {
            newGaugePoints = currentGaugePoints.add(ONE_PERCENT);
        }
    }

Gauge Points are normalized to 100e18 after calculation, if the summation of Gauge Points do not equal 100e18.

GaugePointPerBDV

Gauge Points Per BDV is the amount of Gauge Points allocated to a whitelisted LP token, divided by the BDV of the LP token. The largest value of the LP tokens is used to determine the Grown Stalk issued to Beans in the Silo.

Bean to MaxLP GaugePoints Per BDV Ratio

When allocating Grown Stalk to Bean, Beanstalk bases it as a ratio to the largest gaugePointsPerBDV for LP. The
BeanToMaxLpGaugePointPerBDVScalar is used in the following formula to determine the ratio:

$$x =100\%-(Y-Z)*BeanToMaxLpGaugePointPerBDVScalar$$

At $100\%$ (100e18), the Bean to MaxLPGaugePointPerBDV should be at its lowest (max Seeds to LP), 25e18 (the lowest Seeds allocated to Beans is 25% of the Seeds allocated to the LP with the largest amount of Seeds).

  • at $0\%$ (0e18), the Bean to MaxLPGaugePointPerBDV should be at its highest (lowest Seeds to LP), 100e18 (the largest Seeds that are allocated to Beans is 100% of the Seeds allocated to the LP with the largest amount of Seeds).

UpdateGrownStalkEarnedPerSeason

Below are the formulas on how Beanstalk updates the Grown Stalk earned each Season for each whitelisted asset (excluding Unripe assets):

$$AverageGrownStalkPerBDVPerSeason = g$$

$$BDVofSiloLPTokens_{token} =(BDV_{bean3CRV}, BDV_{beanETH})$$

$$BDVofSiloTokens = (BDVofSiloLPTokens + BDV_{Bean})$$

$$GrownStalkIssuedThisSeason= g * BDVofSiloTokens$$

$$LpGaugePoints_{token} = ( GP_{bean3CRV}, GP_{beanETH} ) $$

$$GaugePointsPerBDV_{token} = \frac{LpGaugePoints_{token}}{BDVofSiloLPTokens_{token}} $$

$$Y = maxRatio = 100\%$$

$$Z = minRatio = 25\%$$

$$f(beanToMaxLpGpPerBDVRatioScalar) = 100\%-(Y-Z)*beanToMaxLpGpPerBDVRatioScalar $$

$$BeanToMaxLpGpPerBDVRatio = f(beanToMaxLpGpPerBDVRatioScalar) $$

$$BeanLPGaugePointsPerBDV = max(GaugePointsPerBDV_{token}) * beanToMaxLpGpPerBDVRatio $$

$$TotalGaugePoints = LpGaugePoints + beanLPGaugePointsPerBDV * BDV_{bean}$$

$$GrownStalkPerGaugePoint = \frac{GrownStalkIssuedThisSeason}{TotalGaugePoints}$$

$$GrownStalkIssuedPerSeason_{token} = GrownStalkPerGaugePoint * GaugePointsPerBDV_{token} $$

$$GrownStalkIssuedPerSeason_{BEAN} = GrownStalkPerGaugePoint * BeanLPGaugePointsPerBDV$$

Unripe to Ripe Chop Update

See #645.

Add support for Conversions from Unripe λ to λ. In doing so:

  • Add new Convert type in LibDataConvert;
  • Modify LibConvert to include new Convert type;
  • Implement LibChop and modify chop(...) and _getPenalizedUnderlying(...) to use LibChop; and
  • Implement LibChopConvert with the convertUnripeBeansToBeans(...) and getBeanAmountOut(...)`` functions which wrap LibChop` functions.

Total Deposited BDV Migration

Currently, the total Deposited BDV stored on-chain does not account for Deposits that have not been migrated to Silo V3. To fix this, a two-step process is implemented. Step one was implemented in BIP-38.

The second step is to calculate the remaining un-migrated BDV for each token at the BIP-38 migration block, and incrementing totalDepositedBDV by the difference of the un-migrated BDV and the migrated BDV between BIP excecution.

LibTokenSilo.incrementTotalDepositedBdv(C.TOKEN, TOKEN_UN_MIGRATED_BDV - s.migratedBdvs[C.TOKEN]);

Dewhitelist BEAN3CRV

Beanstalk is still subject to inter-block MEV manipulation at a scale proportional to the amount of liquidity in the BEAN3CRV pool. Any volume in the BEAN3CRV pool is subject to an unnecessary trading fee in the pool, which results in worse prices for Farmers and worse peg maintenance for Beanstalk. The price of 3CRV is dependent on the worst of the prices of USDC, USDT and DAI.

Beanstalk should not continue incentivizing providing liquidity to the BEAN3CRV pool.

After BEAN3CRV is dewhitelisted, new BEAN3CRV Deposits cannot be created. BEAN3CRV Depositors will still be able to Withdraw and Convert to BEAN when deltaB in the pool is less than 0, but BEAN Depositors can no longer Convert to BEAN3CRV Deposits.

Beanstalk is subject to inter-block MEV manipulation. The Multi Flow Pump on the BEANETH Well is the first Ethereum-native oracle for Ethereum-native data that offers inter-block MEV manipulation resistance in a post-Merge environment.

2 Season Delay

TBD.

Liquidity Weight