Skip to content

Commit

Permalink
Implement plenty ebip tests in foundry, fix some app storage merge mi…
Browse files Browse the repository at this point in the history
…stakes
  • Loading branch information
pizzaman1337 committed Jul 28, 2024
1 parent 783dca0 commit ddee257
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ contract SiloGettersFacet is ReentrancyGuard {
}

function totalRainRoots() external view returns (uint256) {
return s.sys.silo.roots;
return s.sys.rain.roots;
}

//////////////////////// STEM ////////////////////////
Expand Down
2 changes: 2 additions & 0 deletions protocol/contracts/interfaces/IMockFBeanstalk.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,8 @@ interface IMockFBeanstalk {

function totalPods(uint256 fieldId) external view returns (uint256);

function totalRainRoots() external view returns (uint256);

function totalRealSoil() external view returns (uint256);

function totalRoots() external view returns (uint256);
Expand Down
2 changes: 1 addition & 1 deletion protocol/contracts/libraries/Silo/LibFlood.sol
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ library LibFlood {
* ownership when the Farm became Oversaturated. Also, at the beginning of the
* Flood, all Pods that were minted before the Farm became Oversaturated Ripen
* and become Harvestable.
* For more information On Oversaturation see {Weather.handleRain}.
* For more information On Oversaturation see {handleRain}.
*/
function sopWell(WellDeltaB memory wellDeltaB) private {
AppStorage storage s = LibAppStorage.diamondStorage();
Expand Down
1 change: 1 addition & 0 deletions protocol/contracts/libraries/Silo/LibGerminate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ library LibGerminate {
);
germinatingStalk = firstStalk;
totalRootsFromGermination = roots;
firstGerminatingRoots = roots;
emit FarmerGerminatingStalkBalanceChanged(
account,
-int256(uint256(germinatingStalk)),
Expand Down
5 changes: 3 additions & 2 deletions protocol/contracts/libraries/Silo/LibSilo.sol
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ library LibSilo {
if (s.accts[account].sop.rainRoots > s.accts[account].roots) {
uint256 deltaRoots = s.accts[account].sop.rainRoots.sub(s.accts[account].roots);
s.accts[account].sop.rainRoots = s.accts[account].roots;
s.sys.silo.roots = s.sys.silo.roots.sub(deltaRoots);
// decrease system rain roots
s.sys.rain.roots = s.sys.rain.roots.sub(deltaRoots);
}

// emit event.
Expand Down Expand Up @@ -316,7 +317,7 @@ library LibSilo {
if (s.accts[sender].sop.rainRoots > s.accts[sender].roots) {
uint256 deltaRoots = s.accts[sender].sop.rainRoots.sub(s.accts[sender].roots);
s.accts[sender].sop.rainRoots = s.accts[sender].roots;
s.sys.silo.roots = s.sys.silo.roots.sub(deltaRoots);
s.sys.rain.roots = s.sys.rain.roots.sub(deltaRoots);
}

// Add Stalk and Roots to the 'recipient' balance.
Expand Down
250 changes: 244 additions & 6 deletions protocol/test/foundry/sun/Flood.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ contract FloodTest is TestHelper {

// test accounts
address[] farmers;
int96 depositStemBean;

event SeasonOfPlentyWell(uint256 indexed season, address well, address token, uint256 amount);
event SeasonOfPlentyField(uint256 toField);
Expand Down Expand Up @@ -55,16 +56,250 @@ contract FloodTest is TestHelper {
season.siloSunrise(0);
season.siloSunrise(0);

depositStemBean = bs.stemTipForToken(C.BEAN);

// users 1 and 2 deposits 1000 beans into the silo.
address[] memory depositUsers = new address[](2);
depositUsers[0] = users[1];
depositUsers[1] = users[2];
depostBeansForUsers(depositUsers, 1_000e6, 10_000e6);
depostBeansForUsers(depositUsers, 1_000e6, 10_000e6, true);

// give user2 some eth
vm.deal(users[2], 10 ether);
}

function testBugReportLostPlenty2() public {
season.rainSunrise();
bs.mow(users[1], C.BEAN);
season.rainSunrise();
bs.mow(users[1], C.BEAN);

// set reserves so next season plenty is accrued
setReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);
setInstantaneousReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);

season.rainSunrise(); // 1st actual sop
bs.mow(users[1], C.BEAN);

season.rainSunrise();
season.rainSunrise();

season.droughtSunrise();
season.droughtSunrise();

// withdraw deposit
vm.prank(users[1]);
bs.withdrawDeposit(C.BEAN, depositStemBean, 1_000e6, 0);

season.rainSunrise();
bs.mow(users[1], C.BEAN);

uint256 userPlenty = bs.balanceOfPlenty(users[1], C.BEAN_ETH_WELL);
assertEq(userPlenty, 25595575914848452999);
}

function testReducesRainRootsUponWithdrawal() public {
setReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);
setInstantaneousReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);

season.rainSunrise();
season.rainSunrise();

bs.mow(users[1], C.BEAN);

uint256 rainRoots = bs.balanceOfRainRoots(users[1]);

assertEq(rainRoots, 10004000000000000000000000);

vm.prank(users[1]);
bs.withdrawDeposit(C.BEAN, depositStemBean, 1_000e6, 0);

rainRoots = bs.balanceOfRainRoots(users[1]);

assertEq(rainRoots, 0);
}

function testStopsRainingAndWithdraw() public {
int96 stem = bs.stemTipForToken(C.BEAN);
address[] memory testUsers = new address[](1);

testUsers[0] = users[3];
depostBeansForUsers(testUsers, 50_000e6, 100_000e6, false);

// set reserves so that a sop will occur
setReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);
setInstantaneousReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);

season.rainSunrise();
bs.mow(users[3], C.BEAN);

uint256 rainRoots = bs.balanceOfRainRoots(users[3]);
assertEq(rainRoots, 500000000000000000000000000);

season.droughtSunrise();

// withdraw
vm.prank(users[3]);
bs.withdrawDeposit(C.BEAN, stem, 50_000e6, 0);
rainRoots = bs.balanceOfRainRoots(users[3]);
assertEq(rainRoots, 0);

// start raining again
season.rainSunrise();
season.rainSunrise();
bs.mow(users[3], C.BEAN);
rainRoots = bs.balanceOfRainRoots(users[3]);
assertEq(rainRoots, 0);

// measure actual roots
uint256 roots = bs.balanceOfRoots(users[3]);
assertEq(roots, 0);
}

function testBurnsRainRootsUponTransfer() public {
setReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);
setInstantaneousReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);

season.rainSunrise(); // start raining
season.rainSunrise(); // sop

bs.mow(users[1], C.BEAN);

uint256 rainRoots = bs.balanceOfRainRoots(users[1]);

assertEq(rainRoots, 10004000000000000000000000);

vm.prank(users[1]);
bs.transferDeposit(users[1], users[3], C.BEAN, depositStemBean, 1_000e6);
bs.mow(users[1], C.BEAN);

// user[1] should have 0 rain roots
assertEq(bs.balanceOfRainRoots(users[1]), 0);
// user[3] should have 0 rain roots, none transferred
assertEq(bs.balanceOfRainRoots(users[3]), 0);
}

function testBurnsHalfOfRainRootsUponHalfTransfer() public {
setReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);
setInstantaneousReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);

season.rainSunrise(); // start raining
season.rainSunrise(); // sop

bs.mow(users[1], C.BEAN);

uint256 rainRoots = bs.balanceOfRainRoots(users[1]);

assertEq(rainRoots, 10004000000000000000000000);

vm.prank(users[1]);
bs.transferDeposit(users[1], users[3], C.BEAN, depositStemBean, 500e6);
bs.mow(users[1], C.BEAN);

// user[1] should be down by 500 rain roots
assertEq(bs.balanceOfRainRoots(users[1]), 5004000000000000000000000);
}

function testDoesNotBurnRainRootsUponTransferIfExtraRootsAvailable() public {
setReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);
setInstantaneousReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);

season.rainSunrise(); // start raining
season.rainSunrise(); // sop

bs.mow(users[1], C.BEAN);

uint256 rainRoots = bs.balanceOfRainRoots(users[1]);

assertEq(rainRoots, 10004000000000000000000000);

// do another deposit
vm.prank(users[1]);
bs.deposit(C.BEAN, 1_000e6, 0);

// pass germination
season.siloSunrise(0);
season.siloSunrise(0);

// verify roots went up
assertEq(bs.balanceOfRoots(users[1]), 20008000000000000000000000);

// verify rain roots stayed the same
assertEq(bs.balanceOfRainRoots(users[1]), 10004000000000000000000000);

vm.prank(users[1]);
bs.transferDeposit(users[1], users[3], C.BEAN, depositStemBean, 500e6);
bs.mow(users[1], C.BEAN);

// user should have full rain roots, since they had non-rain roots that could be removed before
assertEq(bs.balanceOfRainRoots(users[1]), 10004000000000000000000000);
}

function testGerminationRainRoots() public {
C.bean().mint(users[3], 50_000e6);
vm.prank(users[3]);
C.bean().approve(BEANSTALK, type(uint256).max);
vm.prank(users[3]);
bs.deposit(C.BEAN, 50_000e6, 0);

setReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);
setInstantaneousReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);

season.rainSunrise();

season.rainSunrise();
bs.mow(users[3], C.BEAN);

uint256 totalRainRoots = bs.totalRainRoots();
uint256 userRainRoots = bs.balanceOfRainRoots(users[3]);
// expect user rain roots to be less than total rain roots
assertLt(userRainRoots, totalRainRoots);

// also rain roots should be zero
assertEq(userRainRoots, 0);
}

function testSecondGerminationRainRoots() public {
// not raining

season.rainSunrise(); // start raining

uint256 totalRainRootsBefore = bs.totalRainRoots();

C.bean().mint(users[3], 50_000e6);
vm.prank(users[3]);
C.bean().approve(BEANSTALK, type(uint256).max);
vm.prank(users[3]);
bs.deposit(C.BEAN, 50_000e6, 0);
// set reserves so we'll sop
setReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);
setInstantaneousReserves(C.BEAN_ETH_WELL, 1000000e6, 1100e18);

season.rainSunrise(); // sop
bs.mow(users[3], C.BEAN);

uint256 totalRainRootsAfter = bs.totalRainRoots();
// rain roots before should equal rain roots after, anything deposited after raining doesn't count
assertEq(
totalRainRootsBefore,
totalRainRootsAfter,
"total rain roots before and after should be equal"
);

uint256 userRainRoots = bs.balanceOfRainRoots(users[3]);

// assert that user rain roots are zero
assertEq(userRainRoots, 0, "user rain roots should be zero");

// shouldn't be a way for a user to get more rain roots than total rain roots
// couldn't find a way to do lessThan without importing something else that supports BigNumber from chai
assertLt(
userRainRoots,
totalRainRootsAfter,
"user rain roots should be less than total rain roots"
);
}

function testNotRaining() public view {
Season memory s = seasonGetters.time();
assertFalse(s.raining);
Expand Down Expand Up @@ -615,7 +850,8 @@ contract FloodTest is TestHelper {
function depostBeansForUsers(
address[] memory users,
uint256 beansDeposit,
uint256 beansMint
uint256 beansMint,
bool mow
) public {
for (uint i = 0; i < users.length; i++) {
C.bean().mint(users[i], beansMint);
Expand All @@ -629,10 +865,12 @@ contract FloodTest is TestHelper {
season.siloSunrise(0);
season.siloSunrise(0);

for (uint i = 0; i < users.length; i++) {
// mow, so that lastUpdated has been called at least once
vm.prank(users[i]);
bs.mow(users[i], C.BEAN);
if (mow) {
for (uint i = 0; i < users.length; i++) {
// mow, so that lastUpdated has been called at least once
vm.prank(users[i]);
bs.mow(users[i], C.BEAN);
}
}
}

Expand Down

0 comments on commit ddee257

Please sign in to comment.