--- tags: BIP --- # BIP-X: Stems deployment ## Proposer Beanstalk Farms, pizzaman1337 ## Summary * Support variable seeds per BDV * Store Silo deposits based on _stem_, or Grown Stalk Per BDV, rather than seasons * Turn Silo deposits into ERC-1155 tokens * Change seeds for unBEAN and urBEAN3CRV to 1 seed per BDV * Include migration function to migrate from old Silo deposits to stem-based deposits with 1155 support ## Links * [Grown stalk per bdv GitHub PR](https://github.com/BeanstalkFarms/Beanstalk/pull/353) * GitHub Commit Hash: <wip> ## Problem There are currently 3 problems in Beanstalk for Silo deposits: 1. Farmers with older Deposits may forfeit Stalk when converting from a Whitelisted Silo token with a higher `seedsPerBdv` to a Whitelisted Silo token with a lower `seedsPerBdv` - This is due to the fact that we calculate the season of the deposit based on the total grown stalk. For example, if we had an Bean3CRV-LP deposit (meaning they get 4 seeds per BDV) deposited at season 0, and converted into single-sided bean at season 500 (2 seeds), the single sided bean would have to be deposited at season -500 in order for stalk to be properly calculated. Since seasons are stored as a `uint32` , this sets the value to 0, and causes the user to lose stalk. As the number of seasons increases, so does the likihood that this will occur. 2. `seedsPerBdv` must be static over time for each Whitelisted Silo token, otherwise if a seeds value changed, incorrect amounts of stalk would be removed upon withdrawal of existing deposits 3. `seedsPerBdv` currently cannot have Decimals. 4. Deposits do not implement any standard interface, preventing protocols such as Seaport from supporting Deposits. Also, protocols such as Tractor, Depot need to to implement Deposit specific functionality in order to support Deposits. 5. Bean is currently below peg, despite ~$24m in locked up unripe Bean-3crv liquidity, as long-term unripe holders are incetivized to hold LP for more seeds rather than convert for urBean and lower seed amounts. ## Proposed Solution - Introduce a `cumulativeStalkPerBdv` counter, or "stem", for each Whitelisted Silo Token. - The cumulative grown stalk per bdv, or "stem tip" for a token increases every season to reflect the amount of stalk grown per BDV of deposited value. This starts at 0 at the time of stem deployment for each whitelisted token, or when a new token is whitelisted. - Index Deposits based on `cumulativeStalkPerBdv` or stem instead of Season - A higher `culumativeStalkPerBDV` for a deposit means that the user deposited in a later season. - Store stems using `int128` so grown stalk per bdv values can be negative to accommodate converts without requiring the depositor losing Stalk - Make claiming Grown Stalk token specific, since stalk now grows in different rates per asset. - Introduce decimals at the `stalkEarnedPerSeason` level (seeds), 6 decimal places - Modify existing `SiloFacet`, `ConvertFacet` and other facet functions to use stems - Add a `mowAndMigrate` migration function - Add ERC-1155 support - Split up `SiloFacet` into `MigrationFacet` and `ApprovalFacet` to reduce the `SiloFacet` size to fit within the deployable limit #### Storage The following changes are made to Beanstalk's storage: * Add a `MowStatus` struct: ``` struct MowStatus { int96 lastStem; // the last cumulative grown stalk per bdv index at which the farmer mowed uint128 bdv; // bdv of all of a farmer's deposits of this token type }; ``` * Rename `deposits` to `legacyDeposits`; * Add a new mapping for stems-based deposits `mapping(bytes32 => Deposit) deposits;` * Add a mapping for `MowStatus` on a per-token basis `mapping(address => MowStatus) mowStatuses;` * Add a mapping for ERC-1155 token approval for all `mapping(address => bool) isApprovedForAll;` * In the Silo struct, rename `seeds` to `deprecated_seeds` * In the Season struct, add `uint16 stemStartSeason;` which represents the season in which Stems were deployed * In SiloSettings - rename `seeds` to `stalkEarnedPerSeason` - rename `stalk` to `stalkIssuedPerBdv` - Add `uint32 milestoneSeason;` to keep track of the last season a stalkEarnedPerSeason change happened - Add `int96 milestoneStem;` to store the stem value at the time of the latest change * For ERC-1155 support add: ``` struct Metadata { address token; // the address of the token for a deposit int96 stem; // the grown stalk per BDV assoiated with the deposit uint256 id; // the id of the deposit } ``` * Add `mapping(bytes32 => Storage.Metadata) metadata;` as a mapping of ERC1155 deposit to metadata of the deposit ### ## Technical Rationale Storing deposits based on `uint32` season didn't allow for negative deposits, nor any flexibility in seeds values. Storing based on a "grown stalk per bdv index", or stem, allows for calculating grown stalk based on deposit and withdraw indexes rather than number of seasons passed * seeds. This means seeds, now called stalkEarnedPerSeason, can vary per season. Storing deposits based on Stem allows cleaning up the combination of Silo v1 and v2 deposit storage systems into a single storage method that supports 1155. This requires a migration function, in which users submit all of their deposit seasons and amounts, the system verifies their presence and removes them from the old storage slots for deposits into the stem-based slots with 1155 support. In order to verify the user submitted all of their deposits correctly, the amount of seeds stored for that user should align with the amount calculated based on the submitted deposits. This is handled by the `mowAndMigrate` function. A gas-saving migration function is available to those with no current deposits (but previous deposits had occured), `mowAndMigrateNoDeposits`. <Insert 1155 details here> ## Economic Rationale The penalty for converting from LP to Bean, both ripe and unripe, and losing grown stalk for doing so (in some cases) affects peg maintainence. Users expect to not lose grown stalk when converting from LP to Bean. For seeds rebalancing, to quote bacchist, the original proposer: > There is currently more than enough Unripe BEAN:3CRV LP to regain the peg through silo conversions to Unripe Bean. Unripe LP grants 4 seeds, compared to 2 seeds for Unripe Bean, which presents an opportunity cost to silo members who may otherwise be inclined to perform arbitrage that would restore the peg. > > Given that the majority of the liquidity represented by Unripe LP tokens will remain in the Curve pool until those assets ripen, there is less need to use seed rewards to incentivize liquidity for Bean to trade against. Adjusting the seed rewards for Unripe assets to parity will reduce friction for conversions and aid in peg maintenance. > > This can be achieved by increasing seed rewards for Unripe Bean to match LP at 4, reducing rewards for Unripe LP to 2, or removing seed rewards for unripe assets entirely. Reducing unripe seeds to 1:1 was the most popular choice based on an [informal discord poll](https://discord.com/channels/880413392916054098/1002259572389576734/1082743690092621844). ## Contract Changes ## Beans Minted None. ## Audit <todo> ## Effective Effective immediately upon commit.