Beanstalk icon indicating copy to clipboard operation
Beanstalk copied to clipboard

BIP-X: Misc Improvements

Open nickkatsios opened this issue 11 months ago • 0 comments

BIP-X: Misc. Improvements

Proposer

Beanstalk Farms, Brendan Sanderson, Ben Weintraub

Proposer Wallet: TBD

Summary

  • Migrate the Fertilizer metadata hosted at fert.bean.money on-chain;
  • When $\Delta B_{\overline{t-1}} < 0$, change Soil issued at gm to be the minimum of (1) $-\Delta B$ calculated using the instantaneous reserves from Multi Flow and (2) $-\Delta B_{\overline{t-1}}$;
  • Implement an Anti λ → λ Convert type that allows any user to decrease a Deposit's BDV if the Recorded BDV (the BDV stored on-chain with the Deposit) is greater than the Current BDV (the number of tokens in the Deposit × the current BDV of the token);
  • Change the Chop Rate calculation from the [% recapitalized × % of Sprouts that have become Rinsable] to the [% recapitalized]^2;
  • Update the Locked Beans calculation to account for the change to the Chop Rate; and
  • Decrease the recapitalization amount upon Chop per BIR-14.

Links

Problem

Currently, Fertilizer metadata is currently hosted on centralized infrastructure. This includes the SVG, the traits, etc. Any data hosted on centralized infrastructure requires maintenance and is subject to downtime.

Beanstalk is prone to overissuing Soil at gm call because Soil issuance below peg is solely based on $-\Delta B_{\overline{t-1}}$.

Currently, the Recorded BDV of a Deposit is not necessarily equal to its Current BDV. For example, if a user Deposits BEANETH into the Silo and the USD price of ETH falls significantly (and thus the BDV of BEANETH decreases), the Recorded BDV does not decrease, i.e., the user “locked in” the higher BDV at the time of Deposit. In practice, this may mean Beanstalk is over crediting BDV to Deposits relative to their value to the system. Beanstalk only supports Convert types that do not decrease the BDV of the Deposit.

Currently, Beanstalk offers a conservative Chop Rate of ~1.03% (22.49% recapitalization * 4.5627% of debt repaid to Fertilizer) to Unripe holders. Although the current model is likely to result in the least volatility as Beanstalk is recapitalized (as a result of Unripe holders Chopping later on average), Beanstalk can be more aggressive given its healthy position in terms of liquidity. Chopping is healthy for Beanstalk as its obligations and the amount needed to fully recapitalize Unripe assets decreases.

Locked Beans are conservatively calculated under the assumption that all Unripe LP is Chopped within a single Season.

Currently, when Unripe LP holders Chop, s.recapitalized is unchanged as demonstrated in BIR-14. As a result, the Barn Raise ends earlier than intended, i.e., Beanstalk will not sell enough Fertilizer to fully recapitalize all Unripe LP.

Proposed Solution

On-Chain Fert Metadata

We propose to migrate the Fertilizer metadata hosted at fert.bean.money on-chain.

Specification

  • Create a new contract called FertilizerImage.sol to dynamically and efficiently assemble the SVG image for Active, Used and Available fertilizer; and
  • Modify uri in Internalizer.sol to (1) assemble the required on-chain data, (2) generate the correct Fertilizer image URI using imageUri from FertilizerImage.sol and (3) return the final metadata URI to be consumed.

Soil Issuance Update

We propose to change Soil issued at gm when $\Delta B_{\overline{t-1}} < 0$ to be the minimum of (1) $-\Delta B$ calculated using the instantaneous reserves from Multi Flow and (2) $-\Delta B_{\overline{t-1}}$.

Specification

In SeasonFacet/Sun.sol:

  • Implement setSoilBelowPeg, which calculates the cumulative deltaB across all whitelisted Wells using the instantaneous reserves in Multi Flow, compares it to its time weighted counterpart and picks the minimum of those two values; and
  • Update the P < 1 case in stepSun to use setSoilBelowPeg.

BDV Decrease

Implement a new ANTI_LAMBDA_LAMBDA Convert type, callable by anyone, that Converts on behalf of a user and can decrease a Deposit's BDV.

Specification

  • Add an additional parameter account in LibConvert.convert(…);
  • Add to the return parameters a decreaseBDV boolean representing whether the Convert type should decrease BDV or not;
  • Add the ANTI_LAMBDA_LAMBDA Convert case to the convert ladder in LibConvert;
  • Reorder the Convert ladder in LibConvert so that more frequenly used Converts appear first;
  • To conform with previous convertData types, update them return 0, and add a check that uses msg.sender instead of account when applicable;
  • In ConvertFacet.convert(), replace msg.sender in other Convert helper functions with account;
  • Check if the ANTI_LAMBDA_LAMBDA convert is called in ConvertFacet.convert(), and when it is, update the input Deposit with the current BDV; and
  • Pack all convert properties in a struct called convertParams in LibConvert for code readability and extensibility.

Chop Changes

We propose to:

  • Change the Chop Rate calculation from the [% recapitalized × % of Sprouts that have become Rinsable] to the [% recapitalized]^2;
  • Update the Locked Beans calculation to account for the change to the Chop Rate; and
  • Decrease the recapitalization amount upon Chop per BIR-14.

Specification

Chop rate change:

  • Remove LibUnripe.getRecapPaidPercentAmount() (the percentage paid back to Fertilizer holders) from the Chop Rate calculation;
  • Update LibUnripe.getPenalizedUnderlying() to calculate the Unripe λ → λ Conversion using the new formula;
  • Isolate the calculation to get the dollar amount needed for recapitalization to a new function called LibFertilizer.getTotalRecapDollarsNeeded();
  • Implement getTotalRecapitalizedPercent() to get the US dollar percentage recapitalized by Beanstalk;
  • Update getLockedBeansUnderlyingUnripeBean() to account for the percent recapitalized percentage instead of the amount paid back to Fertilizer;
  • Refactor LibChop.chop() to use LibUnripe.removeUnderlying() instead of LibUnripe.decrementUnderlying() when decreasing the available Ripe underlying amount for the Unripe token after a Chop.

Technical Rationale

The Fertilizer Facet must be updated due to changes to the ERC-1155 token metadata. Hosting the metadata on-chain instead of on centralized infrastructure eliminates any risks associated with uptime.

The Unripe Facet must be updated due to changes to the chop function.

The Convert Facet must be updated due to the introduction of ANTI_LAMBDA_LAMBDA.

The Season Facet must be updated due to changes to the deltaB calculation introduced by the Soil issuance change below peg.

Economic Rationale

Soil Issuance Update

Consider an example where Beanstalk is at -300k deltaB for the first 58 minutes of a Season. At the 58th minute, i.e., 10 blocks before the next gm call, a Farmer buys and Sows 200k Beans, bringing the current deltaB to -100k.

Assuming no other trades in the final 2 minutes of the Season, the Soil issued at gm will be slightly less than 300k, despite Beanstalk only needing 100k Beans to be bought to return to peg. Before Multi Flow, this was necessary for sufficient manipulation resistance.

In general, Beanstalk does not need to be particularly aggressive when issuing Soil—it does not want to issue debt if it doesn't have to.

Thus, using the inter-block MEV manipulation resistant instantaneous reserves in Multi Flow to calculate deltaB below peg is ideal.

BDV Decrease

The ability to decrease a Deposit's BDV is necessary to prevent Beanstalk from over crediting BDV to Deposits relative to their value to the system. There is no incentive for a Farmer to decrease their own BDV. Thus, the Anti λ → λ Convert is callable by any user. There may be sufficient incentive for someone to call Anti λ → λ Convert on a Farmer's Deposit to increase their own Stalk ownership.

Chop Changes

Beanstalk can be more aggressive given its healthy position in terms of liquidity. Chopping is healthy for Beanstalk as its obligations and the amount needed to fully recapitalize Unripe assets decreases.

The Locked Beans calculation must be updated to account for the new Chop Rate calculation.

Ensuring the chop function is implemented as intended is essential to the structure of the Barn approved by the DAO.

Contract Changes

Initialization Contract

The init function on the following TBD contract is called:

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
balanceOfBatchFertilizer 0x304ec65d Replace View
balanceOfFertilized 0xb6f42085 Replace View
balanceOfFertilizer 0x1799b3b2 Replace View
balanceOfUnfertilized 0x1edb6be1 Replace View
beansPerFertilizer 0x9bb4e35a Replace View
getActiveFertilizer 0xdc6ba285 Replace View
getBarnRaiseWell TBD Replace View
getBarnRaiseToken TBD Replace View
getCurrentHumidity 0x39448802 Replace View
getEndBpf 0xc85951a1 Replace View
getFertilizer 0x9c45a1d5 Replace View
getFertilizers 0x34af5416 Replace View
getFirst 0x1e223143 Replace View
getHumidity 0x29130a66 Replace View
getLast 0x4d622831 Replace View
getMintFertilizerOut 0x69744dd0 Replace View
getNext 0xf4a057e2 Replace View
getTotalRecapDollarsNeeded TBD Add View
isFertilizing 0x6ae1c014 Replace View
remainingRecapitalization 0x4a16607c Replace View
totalFertilizedBeans 0x4f9a9678 Replace View
totalFertilizerBeans 0xf9c4ebde Replace View
totalUnfertilizedBeans 0xa3ef48c9 Replace View
beginBarnRaiseMigration TBD Replace Call
claimFertilized 0x83e08888 Replace Call
mintFertilizer 0xbb02e10b Replace Call
payFertilizer 0xd47aee59 Replace Call

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
_getPenalizedUnderlying 0xa84643e4 Remove View
balanceOfPenalizedUnderlying 0x1acc0a47 Replace View
balanceOfUnderlying 0x1be655e8 Replace View
getLockedBeans 0x087d78b4 Replace View
getLockedBeansFromTwaReserves 0x7caa025f Replace View
getLockedBeansUnderlyingUnripeBean 0xbfe2f3be Replace View
getLockedBeansUnderlyingUnripeLP TBD Replace View
getPenalizedUnderlying 0x6de45df2 Replace View
getPenalty 0x014a8a49 Replace View
getPercentPenalty 0xbb7de478 Replace View
getRecapFundedPercent 0x43cc4ee0 Replace View
getRecapitalized TBD Add View
getRecapPaidPercent 0xab434eb7 Replace View
getTotalUnderlying 0xadef4533 Replace View
getUnderlying 0x9f06b3fa Replace View
getUnderlyingPerUnripeToken 0xb8a04d1b Replace View
getUnderlyingToken 0x691bcc88 Replace View
isUnripe 0xfc6a19df Replace View
picked 0xd3c73ec8 Replace View
addMigratedUnderlying 0x787cee99 Replace Call
addUnripeToken 0xfa345569 Replace Call
chop 0x9a516cad Replace Call
pick 0x13ed3cea Replace Call
switchUnderlyingToken 0xa33fa99f Replace Call

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 Call

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
seasonTime 0xca7b7d7b Replace View
gm 0x64ee4b80 Replace Call
sunrise 0xfc06d2a6 Replace Call

Beans Minted

None.

Audit

The commit hash of this BIP is TBD.

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

Audit remediations were committed and documented in PR #TBD.

Post Audit Changes

The following changes have been made to the BIP code, but have not been audited.

PR #TBD, which does TBD.

Effective

Immediate upon commitment.

nickkatsios avatar Mar 23 '24 15:03 nickkatsios