Skip to content

Commit

Permalink
Germination
Browse files Browse the repository at this point in the history
  • Loading branch information
Brean0 committed Dec 14, 2023
1 parent 354b7cc commit 12cad55
Show file tree
Hide file tree
Showing 25 changed files with 2,067 additions and 740 deletions.
54 changes: 29 additions & 25 deletions protocol/contracts/beanstalk/AppStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,17 @@ contract Account {
* @param stalk germinating stalk - stalk from assets deposited in the last two seasons.
* @param roots germinating roots - roots from assets deposited in the last two seasons.
* @param germinatingTokenMask - a bitmask of all the tokens that are germinating.
* @param germinatingAmounts - a mapping of token address to germinating amounts/bdv.
* @param germinatingBdv - a mapping of token address to germinating bdv.
*
* @dev germinatingTokenMask limits the amount of whitelisted silo assets to 32.
*/
struct FarmerGerminating {
uint112 stalk;
uint112 roots;
uint32 germinatingTokenMask;
mapping(address => Deposit) germinatingAmounts;
bytes4 germinatingTokenMask;
mapping(address => uint256) bdv;
}

/**
* @notice This struct stores the mow status for each Silo-able token, for each farmer.
* This gets updated each time a farmer mows, or adds/removes deposits.
Expand Down Expand Up @@ -106,6 +109,7 @@ contract Account {
* @param lastUpdate The Season in which the Farmer last updated their Silo.
* @param lastSop The last Season that a SOP occured at the time the Farmer last updated their Silo.
* @param lastRain The last Season that it started Raining at the time the Farmer last updated their Silo.
* @param deprecated_deltaRoots DEPRECATED – BIP-39 introduced germination.
* @param deprecated_lastSIs DEPRECATED – In Silo V1.2, the Silo reward mechanism was updated to no longer need to store the number of the Supply Increases at the time the Farmer last updated their Silo.
* @param deprecated_proposedUntil DEPRECATED – Replant removed on-chain governance including the ability to propose BIPs.
* @param deprecated_sop DEPRECATED – Replant reset the Season of Plenty mechanism
Expand Down Expand Up @@ -157,7 +161,7 @@ contract Account {
uint32 lastUpdate; // The Season in which the Farmer last updated their Silo.
uint32 lastSop; // The last Season that a SOP occured at the time the Farmer last updated their Silo.
uint32 lastRain; // The last Season that it started Raining at the time the Farmer last updated their Silo.
uint128 deltaRoots; // the number of roots to add, in the case where a farmer has mowed in the morning
uint128 depreciated_deltaRoots; // DEPRECATED - BIP-39 introduced germination.
SeasonOfPlenty deprecated; // DEPRECATED – Replant reset the Season of Plenty mechanism
uint256 roots; // A Farmer's Root balance.
uint256 wrappedBeans; // DEPRECATED – Replant generalized Internal Balances. Wrapped Beans are now stored at the AppStorage level.
Expand All @@ -171,8 +175,8 @@ contract Account {
mapping(uint256 => Deposit) deposits; // SiloV3 Deposits stored as a map from uint256 to Deposit. This is an concat of the token address and the CGSPBDV for a ERC20 deposit, and a hash for an ERC721/1155 deposit.
mapping(address => MowStatus) mowStatuses; // Store a MowStatus for each Whitelisted Silo token
mapping(address => bool) isApprovedForAll; // ERC1155 isApprovedForAll mapping
FarmerGerminating oddGeminating; // A Farmer's germinating assets from odd seasons.
FarmerGerminating evenGeminating; // A Farmer's germinating assets from even seasons.
FarmerGerminating oddGerminating; // A Farmer's germinating assets from odd seasons.
FarmerGerminating evenGerminating; // A Farmer's germinating assets from even seasons.
}
}

Expand Down Expand Up @@ -408,16 +412,16 @@ contract Storage {
* @dev A Token is considered Whitelisted if there exists a non-zero {SiloSettings} selector.
*/
struct SiloSettings {
bytes4 selector; // ─────────────┐ 4
uint32 stalkEarnedPerSeason; // │ 4 (16)
uint32 stalkIssuedPerBdv; // │ 4 (8)
uint32 milestoneSeason; // │ 4 (12)
int96 milestoneStem; // │ 12 (28)
bytes1 encodeType; // ───────────┘ 1 (29)
// 3 bytes are left here.
uint128 gaugePoints; // ────--------───┐ 16
bytes4 gpSelector; // │ 4 (20)
uint96 optimalPercentDepositedBdv; // ──┘ 12 (32)
bytes4 selector; // ────────────────────┐ 4
uint32 stalkEarnedPerSeason; // │ 4 (16)
uint32 stalkIssuedPerBdv; // │ 4 (8)
uint32 milestoneSeason; // │ 4 (12)
int96 milestoneStem; // │ 12 (28)
bytes1 encodeType; // 1 (29)
int24 deltaStalkEarnedPerSeason; // ────┘ 3 (32)
uint128 gaugePoints; // ──────────────┐ 16
bytes4 gpSelector; // │ 4 (20)
uint96 optimalPercentDepositedBdv; // ──┘ 12 (32)
}

/**
Expand Down Expand Up @@ -470,9 +474,9 @@ contract Storage {
/**
* @notice Stores the total germination amounts for each whitelisted token.
*/
struct SiloGermination {
uint128 deposited;
uint128 depositedBdv;
struct Deposited {
uint128 amount;
uint128 bdv;
}

/**
Expand All @@ -481,7 +485,7 @@ contract Storage {
struct TotalGerminating {
uint128 stalk;
uint128 roots;
mapping(address => SiloGermination) siloGerminating;
mapping(address => Deposited) deposited;
}
}

Expand Down Expand Up @@ -515,7 +519,7 @@ contract Storage {
* @param siloBalances A mapping from Token address to Silo Balance storage (amount deposited and withdrawn).
* @param ss A mapping from Token address to Silo Settings for each Whitelisted Token. If a non-zero storage exists, a Token is whitelisted.
* @param deprecated2 DEPRECATED - 2 slots that used to store state variables which have been depreciated through various updates. Storage slots can be left alone or reused.
* @param newEarnedStalk the amount of earned stalk issued this season. Since 1 stalk = 1 bean, it represents the earned beans as well.
* @param depreciated_newEarnedStalk the amount of earned stalk issued this season. Since 1 stalk = 1 bean, it represents the earned beans as well.
* @param sops A mapping from Season to Plenty Per Root (PPR) in that Season. Plenty Per Root is 0 if a Season of Plenty did not occur.
* @param internalTokenBalance A mapping from Farmer address to Token address to Internal Balance. It stores the amount of the Token that the Farmer has stored as an Internal Balance in Beanstalk.
* @param unripeClaimed True if a Farmer has Claimed an Unripe Token. A mapping from Farmer to Unripe Token to its Claim status.
Expand All @@ -528,7 +532,7 @@ contract Storage {
* @param fFirst The lowest active Fertilizer Id (start of linked list that is stored by nextFid).
* @param fLast The highest active Fertilizer Id (end of linked list that is stored by nextFid).
* @param bpf The cumulative Beans Per Fertilizer (bfp) minted over all Season.
* @param vestingPeriodRoots the number of roots to add to the global roots, in the case the user plants in the morning. // placed here to save a storage slot.s
* @param depreciated_vestingPeriodRoots DEPRECIATED - removed in BIP-39 in favor of germination.
* @param recapitalized The number of USDC that has been recapitalized in the Barn Raise.
* @param isFarm Stores whether the function is wrapped in the `farm` function (1 if not, 2 if it is).
* @param ownerCandidate Stores a candidate address to transfer ownership to. The owner must claim the ownership transfer.
Expand Down Expand Up @@ -569,8 +573,8 @@ struct AppStorage {
mapping(address => Storage.AssetSilo) siloBalances;
mapping(address => Storage.SiloSettings) ss;
uint256[2] deprecated2;
uint128 newEarnedStalk; // ──────┐ 16
uint128 vestingPeriodRoots; // ──┘ 16 (32/32)
uint128 depreciated_newEarnedStalk; // ──────┐ 16
uint128 depreciated_vestingPeriodRoots; // ──┘ 16 (32/32)
mapping (uint32 => uint256) sops;

// Internal Balances
Expand Down Expand Up @@ -611,7 +615,7 @@ struct AppStorage {

// Seed Gauge
Storage.SeedGauge seedGauge;
bytes32[144] casesV2;
bytes32[144] casesV2;

// Germination
Storage.TotalGerminating oddGerminating;
Expand Down
8 changes: 4 additions & 4 deletions protocol/contracts/beanstalk/init/InitBipSeedGauge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ contract InitBipSeedGauge is Weather {
uint256 private constant TARGET_SEASONS_TO_CATCHUP = 4320;
uint256 private constant PRECISION = 1e6;

uint256 internal constant BEAN_UNMIGRATED_BDV = 304_630_107407; // ~300k BDV
uint256 internal constant BEAN_3CRV_UNMIGRATED_BDV = 26_212_521946; // ~26k BDV
uint256 internal constant UNRIPE_BEAN_UNMIGRATED_BDV = 3_209_210_313166; // 3.2m BDV
uint256 internal constant UNRIPE_LP_UNMIGRATED_BDV = 6_680_992_571569; // 6.68m BDV
uint256 internal constant BEAN_UNMIGRATED_BDV = 347_206_927197; // ~347k BDV
uint256 internal constant BEAN_3CRV_UNMIGRATED_BDV = 26_512_602_424; // ~26k BDV
uint256 internal constant UNRIPE_BEAN_UNMIGRATED_BDV = 3_736_196_158_417; // ~3.7m BDV
uint256 internal constant UNRIPE_LP_UNMIGRATED_BDV = 7_119_564_766_493; // ~7.1m BDV

// assumption is that unripe assets has been migrated to the bean-eth Wells.
function init() external {
Expand Down
105 changes: 56 additions & 49 deletions protocol/contracts/beanstalk/silo/ConvertFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {LibConvert} from "contracts/libraries/Convert/LibConvert.sol";
import {LibGerminate} from "contracts/libraries/Silo/LibGerminate.sol";

/**
* @author Publius, Brean, DeadManWalking
Expand Down Expand Up @@ -97,6 +98,14 @@ contract ConvertFacet is ReentrancyGuard {
emit Convert(msg.sender, fromToken, toToken, fromAmount, toAmount);
}

/**
* @notice removes the deposits from msg.sender and returns the
* grown stalk and bdv removed.
*
* @dev if a user inputs a stem of a deposit that is `germinating`,
* the function will omit that deposit. This is due to the fact that
* germinating deposits can be manipulated and skip the germination process.
*/
function _withdrawTokens(
address token,
int96[] memory stems,
Expand All @@ -114,45 +123,33 @@ contract ConvertFacet is ReentrancyGuard {
{
uint256[] memory bdvsRemoved = new uint256[](stems.length);
uint256[] memory depositIds = new uint256[](stems.length);

// get germinating stem and stemTip for the token
LibGerminate.GermStem memory germStem = LibGerminate.getGerminatingStem(token);

while ((i < stems.length) && (a.tokensRemoved < maxTokens)) {
if (a.tokensRemoved.add(amounts[i]) < maxTokens) {
//keeping track of stalk removed must happen before we actually remove the deposit
//this is because LibTokenSilo.grownStalkForDeposit() uses the current deposit info
depositBDV = LibTokenSilo.removeDepositFromAccount(
// skip any stems that are germinating.
if (germStem.germinatingStem <= stems[i]) {
i++;
continue;
}

if (a.tokensRemoved.add(amounts[i]) >= maxTokens) amounts[i] = maxTokens.sub(a.tokensRemoved);
depositBDV = LibTokenSilo.removeDepositFromAccount(
msg.sender,
token,
stems[i],
amounts[i]
amounts[i],
LibGerminate.Germinate.NOT_GERMINATING
);
bdvsRemoved[i] = depositBDV;
a.stalkRemoved = a.stalkRemoved.add(
LibSilo.stalkReward(
stems[i],
LibTokenSilo.stemTipForToken(token),
depositBDV.toUint128()
)
);

} else {
amounts[i] = maxTokens.sub(a.tokensRemoved);

depositBDV = LibTokenSilo.removeDepositFromAccount(
msg.sender,
token,
bdvsRemoved[i] = depositBDV;
a.stalkRemoved = a.stalkRemoved.add(
LibSilo.stalkReward(
stems[i],
amounts[i]
);

bdvsRemoved[i] = depositBDV;
a.stalkRemoved = a.stalkRemoved.add(
LibSilo.stalkReward(
stems[i],
LibTokenSilo.stemTipForToken(token),
depositBDV.toUint128()
)
);

}
germStem.stemTip,
depositBDV.toUint128()
)
);

a.tokensRemoved = a.tokensRemoved.add(amounts[i]);
a.bdvRemoved = a.bdvRemoved.add(depositBDV);
Expand Down Expand Up @@ -188,9 +185,12 @@ contract ConvertFacet is ReentrancyGuard {
"Convert: Not enough tokens removed."
);
LibTokenSilo.decrementTotalDeposited(token, a.tokensRemoved, a.bdvRemoved);

// all deposits converted are not germinating.
LibSilo.burnStalk(
msg.sender,
a.stalkRemoved.add(a.bdvRemoved.mul(s.ss[token].stalkIssuedPerBdv))
a.stalkRemoved.add(a.bdvRemoved.mul(s.ss[token].stalkIssuedPerBdv)),
LibGerminate.Germinate.NOT_GERMINATING
);
return (a.stalkRemoved, a.bdvRemoved);
}
Expand All @@ -204,27 +204,34 @@ contract ConvertFacet is ReentrancyGuard {
) internal returns (int96 stem) {
require(bdv > 0 && amount > 0, "Convert: BDV or amount is 0.");

//calculate stem index we need to deposit at from grownStalk and bdv
//if we attempt to deposit at a half-season (a grown stalk index that would fall between seasons)
//then in affect we lose that partial season's worth of stalk when we deposit
//so here we need to update grownStalk to be the amount you'd have with the above deposit
// TODO: add increment TotalGerminating logic
// 1: check whether the stem is a germinating stem. If so,
// increment the total germinating amount.

/// @dev the two functions were combined into one function to save gas.
// _stemTip = LibTokenSilo.grownStalkAndBdvToStem(IERC20(token), grownStalk, bdv);
// grownStalk = uint256(LibTokenSilo.calculateStalkFromStemAndBdv(IERC20(token), _stemTip, bdv));

(grownStalk, stem) = LibTokenSilo.calculateGrownStalkAndStem(token, grownStalk, bdv);

LibSilo.mintStalk(msg.sender, bdv.mul(LibTokenSilo.stalkIssuedPerBdv(token)).add(grownStalk));

LibTokenSilo.incrementTotalDeposited(token, amount, bdv);
LibGerminate.Germinate germ;
(grownStalk, stem, germ) = LibTokenSilo.calculateGrownStalkAndStem(token, grownStalk, bdv);

LibSilo.mintStalk(
msg.sender,
bdv.mul(LibTokenSilo.stalkIssuedPerBdv(token)).add(grownStalk),
germ
);

// if the new stem is germinating, increment total germinating.
// else increment total deposited.
if (germ == LibGerminate.Germinate.NOT_GERMINATING) {
LibTokenSilo.incrementTotalDeposited(token, amount, bdv);
} else {
LibTokenSilo.incrementTotalGerminating(token, amount, bdv, germ);
}
LibTokenSilo.addDepositToAccount(
msg.sender,
token,
stem,
amount,
amount,
bdv,
LibTokenSilo.Transfer.emitTransferSingle
LibTokenSilo.Transfer.emitTransferSingle,
germ
);
}
}
Loading

0 comments on commit 12cad55

Please sign in to comment.