Skip to content

Commit

Permalink
Pipeline convert updates to be able to handle zero liquidity wells (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Brean0 committed Sep 22, 2024
2 parents 6d29fce + bd8735e commit 32d6d7b
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 16 deletions.
35 changes: 26 additions & 9 deletions protocol/contracts/libraries/Oracle/LibDeltaB.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,17 @@ library LibDeltaB {
* @return The current deltaB uses the current reserves in the well.
*/
function currentDeltaB(address well) internal view returns (int256) {
uint256[] memory reserves = IWell(well).getReserves();
return calculateDeltaBFromReserves(well, reserves, ZERO_LOOKBACK);
try IWell(well).getReserves() returns (uint256[] memory reserves) {
uint256 beanIndex = LibWell.getBeanIndex(IWell(well).tokens());
// if less than minimum bean balance, return 0, otherwise
// calculateDeltaBFromReserves will revert
if (reserves[beanIndex] < C.WELL_MINIMUM_BEAN_BALANCE) {
return 0;
}
return calculateDeltaBFromReserves(well, reserves, ZERO_LOOKBACK);
} catch {
return 0;
}
}

/**
Expand Down Expand Up @@ -73,13 +82,20 @@ library LibDeltaB {
address pump = pumps[0].target;

// well address , data[]
uint256[] memory instReserves = ICappedReservesPump(pump).readCappedReserves(
well,
pumps[0].data
);

// calculate deltaB.
return calculateDeltaBFromReserves(well, instReserves, ZERO_LOOKBACK);
try ICappedReservesPump(pump).readCappedReserves(well, pumps[0].data) returns (
uint256[] memory instReserves
) {
uint256 beanIndex = LibWell.getBeanIndex(IWell(well).tokens());
// if less than minimum bean balance, return 0, otherwise
// calculateDeltaBFromReserves will revert
if (instReserves[beanIndex] < C.WELL_MINIMUM_BEAN_BALANCE) {
return 0;
}
// calculate deltaB.
return calculateDeltaBFromReserves(well, instReserves, ZERO_LOOKBACK);
} catch {
return 0;
}
}

// Calculates overall deltaB, used by convert for stalk penalty purposes
Expand Down Expand Up @@ -124,6 +140,7 @@ library LibDeltaB {
uint256 lpSupply
) internal view returns (int256 wellDeltaB) {
wellDeltaB = currentDeltaB(well);
if (wellDeltaB == 0) return 0; // prevent divide by zero
wellDeltaB = scaledDeltaB(lpSupply, IERC20(well).totalSupply(), wellDeltaB);
}

Expand Down
114 changes: 114 additions & 0 deletions protocol/test/foundry/Migration/ReseedState.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {IFertilizer} from "contracts/interfaces/IFertilizer.sol";
import "forge-std/StdUtils.sol";
import {BeanstalkPrice, WellPrice} from "contracts/ecosystem/price/BeanstalkPrice.sol";
import {P} from "contracts/ecosystem/price/P.sol";
import {MockToken} from "contracts/mocks/MockToken.sol";

interface IBeanstalkPrice {
function price() external view returns (P.Prices memory p);
Expand Down Expand Up @@ -117,6 +118,119 @@ contract ReseedStateTest is TestHelper {
);
}

function test_pipelineConvertRealUserLPToBean() public {
address realUser = 0x0b8e605A7446801ae645e57de5AAbbc251cD1e3c; // first user in deposits with bean:weth
address beanWethWell = 0xBEA00A3F7aaF99476862533Fe7DcA4b50f6158cB;
address beanWeethWell = 0xBEA00865405A02215B44eaADB853d0d2192Fc29D;

// add liquidity to beanWeethWell
// addLiquidityToWellArb(
// realUser,
// beanWeethWell,
// 10_000e6, // 10,000 bean,
// 10 ether // 10 WETH
// );

address token;
int96 stem;
uint256 amount;

IMockFBeanstalk.TokenDepositId[] memory deposits = l2Beanstalk.getDepositsForAccount(
realUser
);
for (uint256 i; i < deposits.length; i++) {
if (deposits[i].token == address(beanWethWell)) {
(token, stem) = l2Beanstalk.getAddressAndStem(deposits[i].depositIds[0]);
amount = deposits[i].tokenDeposits[0].amount;
break;
}
}

int96[] memory stems = new int96[](1);
stems[0] = stem;
uint256[] memory amounts = new uint256[](1);
amounts[0] = amount;

IMockFBeanstalk.AdvancedPipeCall[] memory calls = createLPToBeanPipeCalls(
amount,
beanWethWell
);

// previously this would revert with "Well: Bean reserve is less than the minimum"
vm.prank(realUser);
l2Beanstalk.pipelineConvert(beanWethWell, stems, amounts, L2BEAN, calls);
}

function createLPToBeanPipeCalls(
uint256 amountOfLP,
address well
) private view returns (IMockFBeanstalk.AdvancedPipeCall[] memory output) {
// setup approve max call
bytes memory approveEncoded = abi.encodeWithSelector(
IERC20.approve.selector,
well,
type(uint256).max
);

uint256[] memory tokenAmountsIn = new uint256[](2);
tokenAmountsIn[0] = amountOfLP;
tokenAmountsIn[1] = 0;

// encode remove liqudity.
bytes memory removeLiquidityEncoded = abi.encodeWithSelector(
IWell.removeLiquidityOneToken.selector,
amountOfLP, // tokenAmountsIn
L2BEAN, // tokenOut
0, // min out
PIPELINE, // recipient
type(uint256).max // deadline
);

// Fabricate advancePipes:
IMockFBeanstalk.AdvancedPipeCall[]
memory advancedPipeCalls = new IMockFBeanstalk.AdvancedPipeCall[](2);

// Action 0: approve the Bean-Eth well to spend pipeline's bean.
advancedPipeCalls[0] = IMockFBeanstalk.AdvancedPipeCall(
L2BEAN, // target
approveEncoded, // calldata
abi.encode(0) // clipboard
);

// Action 2: Remove One sided Liquidity into the well.
advancedPipeCalls[1] = IMockFBeanstalk.AdvancedPipeCall(
well, // target
removeLiquidityEncoded, // calldata
abi.encode(0) // clipboard
);

return advancedPipeCalls;
}

function addLiquidityToWellArb(
address user,
address well,
uint256 beanAmount,
uint256 nonBeanTokenAmount
) internal returns (uint256 lpOut) {
(address nonBeanToken, ) = l2Beanstalk.getNonBeanTokenAndIndexFromWell(well);

if (runningOnFork()) {
console.log("dealing tokens on fork");
deal(address(L2BEAN), well, beanAmount, true);
deal(address(nonBeanToken), well, nonBeanTokenAmount, true);
} else {
// mint and sync.
MockToken(BEAN).mint(well, beanAmount);
MockToken(nonBeanToken).mint(well, nonBeanTokenAmount);
}

lpOut = IWell(well).sync(user, 0);

// sync again to update reserves.
IWell(well).sync(user, 0);
}

// LibUsdOracle: 0x5003dF9E48dA96e4B4390373c8ae70EbFA5415A7
function test_beanstalkPrice() public {
// Get beanstalk price
Expand Down
8 changes: 1 addition & 7 deletions protocol/test/foundry/utils/TestHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,8 @@ contract TestHelper is
MockToken(nonBeanToken).mint(well, nonBeanTokenAmount);
}

uint256 beanWellBalance = IERC20(BEAN).balanceOf(well);
console.log("bean well balance", beanWellBalance);
uint256 wbtcWellBalance = IERC20(WBTC).balanceOf(well);
console.log("wbtc well balance", wbtcWellBalance);

console.log("syncing");
lpOut = IWell(well).sync(user, 0);
console.log("syncing again");

// sync again to update reserves.
IWell(well).sync(user, 0);
}
Expand Down

0 comments on commit 32d6d7b

Please sign in to comment.