From b53a729896d6f9ebeb69ab71b05ef0678088d769 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Thu, 10 Oct 2024 21:29:59 -0400 Subject: [PATCH 01/13] Implement a method to read the L2 block. --- .../beanstalk/silo/ConvertGettersFacet.sol | 5 +++-- .../beanstalk/sun/SeasonFacet/SeasonFacet.sol | 3 ++- .../libraries/Convert/LibConvert.sol | 5 +++-- protocol/contracts/libraries/LibArbitrum.sol | 22 +++++++++++++++++++ protocol/contracts/libraries/LibDibbler.sol | 9 +++++--- protocol/contracts/mocks/MockArbitrumSys.sol | 19 ++++++++++++++++ protocol/scripts/deploy.js | 6 ++++- protocol/scripts/impersonate.js | 12 ++++++++++ protocol/test/foundry/utils/TestHelper.sol | 8 ++++++- 9 files changed, 79 insertions(+), 10 deletions(-) create mode 100644 protocol/contracts/libraries/LibArbitrum.sol create mode 100644 protocol/contracts/mocks/MockArbitrumSys.sol diff --git a/protocol/contracts/beanstalk/silo/ConvertGettersFacet.sol b/protocol/contracts/beanstalk/silo/ConvertGettersFacet.sol index 94dbaf95b..da8a924f7 100644 --- a/protocol/contracts/beanstalk/silo/ConvertGettersFacet.sol +++ b/protocol/contracts/beanstalk/silo/ConvertGettersFacet.sol @@ -9,6 +9,7 @@ import {LibRedundantMath256} from "contracts/libraries/LibRedundantMath256.sol"; import {LibConvert} from "contracts/libraries/Convert/LibConvert.sol"; import {LibWellMinting} from "contracts/libraries/Minting/LibWellMinting.sol"; import {LibDeltaB} from "contracts/libraries/Oracle/LibDeltaB.sol"; +import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; /** * @author Publius @@ -93,7 +94,7 @@ contract ConvertGettersFacet { uint256 _overallCappedDeltaB = LibConvert.abs(LibDeltaB.overallCappedDeltaB()); uint256 overallConvertCapacityUsed = s .sys - .convertCapacity[block.number] + .convertCapacity[LibArbitrum.blockNumber()] .overallConvertCapacityUsed; return overallConvertCapacityUsed > _overallCappedDeltaB @@ -110,7 +111,7 @@ contract ConvertGettersFacet { AppStorage storage s = LibAppStorage.diamondStorage(); return LibConvert.abs(LibDeltaB.cappedReservesDeltaB(well)).sub( - s.sys.convertCapacity[block.number].wellConvertCapacityUsed[well] + s.sys.convertCapacity[LibArbitrum.blockNumber()].wellConvertCapacityUsed[well] ); } diff --git a/protocol/contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol b/protocol/contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol index d08d78843..9741ad006 100644 --- a/protocol/contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol +++ b/protocol/contracts/beanstalk/sun/SeasonFacet/SeasonFacet.sol @@ -13,6 +13,7 @@ import {Invariable} from "contracts/beanstalk/Invariable.sol"; import {LibTractor} from "contracts/libraries/LibTractor.sol"; import {LibRedundantMath256} from "contracts/libraries/LibRedundantMath256.sol"; import {IBean} from "contracts/interfaces/IBean.sol"; +import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; /** * @title SeasonFacet @@ -102,7 +103,7 @@ contract SeasonFacet is Invariable, Weather { function stepSeason() private returns (uint32 season) { s.sys.season.current += 1; season = s.sys.season.current; - s.sys.season.sunriseBlock = uint64(block.number); // Note: will overflow after 2^64 blocks. + s.sys.season.sunriseBlock = uint64(LibArbitrum.blockNumber()); // Note: will overflow after 2^64 blocks. emit Sunrise(season); } diff --git a/protocol/contracts/libraries/Convert/LibConvert.sol b/protocol/contracts/libraries/Convert/LibConvert.sol index a8875d065..392c82c78 100644 --- a/protocol/contracts/libraries/Convert/LibConvert.sol +++ b/protocol/contracts/libraries/Convert/LibConvert.sol @@ -24,6 +24,7 @@ import {LibTokenSilo} from "contracts/libraries/Silo/LibTokenSilo.sol"; import {GerminationSide} from "contracts/beanstalk/storage/System.sol"; import {LibBytes} from "contracts/libraries/LibBytes.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; /** * @title LibConvert @@ -235,7 +236,7 @@ library LibConvert { ); // Update penalties in storage. - ConvertCapacity storage convertCap = s.sys.convertCapacity[block.number]; + ConvertCapacity storage convertCap = s.sys.convertCapacity[LibArbitrum.blockNumber()]; convertCap.overallConvertCapacityUsed = convertCap.overallConvertCapacityUsed.add( overallConvertCapacityUsed ); @@ -318,7 +319,7 @@ library LibConvert { ) internal view returns (uint256 cumulativePenalty, PenaltyData memory pdCapacity) { AppStorage storage s = LibAppStorage.diamondStorage(); - ConvertCapacity storage convertCap = s.sys.convertCapacity[block.number]; + ConvertCapacity storage convertCap = s.sys.convertCapacity[LibArbitrum.blockNumber()]; // first check overall convert capacity, if none remaining then full penalty for amount in direction of peg if (convertCap.overallConvertCapacityUsed >= overallCappedDeltaB) { diff --git a/protocol/contracts/libraries/LibArbitrum.sol b/protocol/contracts/libraries/LibArbitrum.sol new file mode 100644 index 000000000..b6536547c --- /dev/null +++ b/protocol/contracts/libraries/LibArbitrum.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +interface IArbitrumSys { + function arbBlockNumber() external view returns (uint256); +} + +/** + * @title LibArbitrum + * @notice LibArbitrum provides constants for interacting with the Arbitrum network. + **/ +library LibArbitrum { + address internal constant ARB_SYS_PRECOMPILE = 0x0000000000000000000000000000000000000064; + + /** + * @notice Returns the current block number on Arbitrum. + */ + function blockNumber() internal view returns (uint256) { + return IArbitrumSys(ARB_SYS_PRECOMPILE).arbBlockNumber(); + } +} diff --git a/protocol/contracts/libraries/LibDibbler.sol b/protocol/contracts/libraries/LibDibbler.sol index 5da2d217c..ef6444e45 100644 --- a/protocol/contracts/libraries/LibDibbler.sol +++ b/protocol/contracts/libraries/LibDibbler.sol @@ -14,6 +14,7 @@ import {LibRedundantMath256} from "contracts/libraries/LibRedundantMath256.sol"; import {LibTransfer} from "contracts/libraries/Token/LibTransfer.sol"; import {LibTractor} from "contracts/libraries/LibTractor.sol"; import {IBean} from "contracts/interfaces/IBean.sol"; +import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; /** * @title LibDibbler @@ -230,9 +231,11 @@ library LibDibbler { */ function morningTemperature() internal view returns (uint256) { AppStorage storage s = LibAppStorage.diamondStorage(); - uint256 delta = block.number.sub(s.sys.season.sunriseBlock).mul(L2_BLOCK_TIME).div( - L1_BLOCK_TIME - ); + uint256 delta = LibArbitrum + .blockNumber() + .sub(s.sys.season.sunriseBlock) + .mul(L2_BLOCK_TIME) + .div(L1_BLOCK_TIME); // check most likely case first if (delta > 24) { diff --git a/protocol/contracts/mocks/MockArbitrumSys.sol b/protocol/contracts/mocks/MockArbitrumSys.sol new file mode 100644 index 000000000..c4f92b1cd --- /dev/null +++ b/protocol/contracts/mocks/MockArbitrumSys.sol @@ -0,0 +1,19 @@ +/* + SPDX-License-Identifier: MIT +*/ + +pragma solidity ^0.8.20; + +/** + * @author Brean + * @title MockArbitrumSys + **/ +contract MockArbitrumSys { + function arbBlockNumber() external view returns (uint256) { + return block.number; + } + + function arbChainID() external view returns (uint256) { + return block.chainid; + } +} diff --git a/protocol/scripts/deploy.js b/protocol/scripts/deploy.js index 1e5e77e86..10b37b413 100644 --- a/protocol/scripts/deploy.js +++ b/protocol/scripts/deploy.js @@ -20,7 +20,8 @@ const { impersonateChainlinkAggregator, impersonateUniswapV3, impersonatePipeline, - impersonateToken + impersonateToken, + impersonateMockArbitrumSys } = require("./impersonate.js"); const { getBeanstalk } = require("../utils/contracts"); const { impersonateBeanstalkOwner } = require("../utils/signer"); @@ -128,6 +129,9 @@ async function main( // deploy unripe tokens. if (unripe) await impersonateUnripe(); + // impersonate Arbitrum sys. + await impersonateMockArbitrumSys(); + const [beanstalkDiamond, diamondCut] = await diamond.deploy({ diamondName: "BeanstalkDiamond", initDiamond: initDiamondArg, diff --git a/protocol/scripts/impersonate.js b/protocol/scripts/impersonate.js index fcaed69fb..6df0a8065 100644 --- a/protocol/scripts/impersonate.js +++ b/protocol/scripts/impersonate.js @@ -69,6 +69,17 @@ async function pool() { return UNISWAP_V2_PAIR; } +async function MockArbitrumSys() { + const MockArbitrumSys = await ethers.getContractFactory("MockArbitrumSys"); + const mockArbitrumSys = await MockArbitrumSys.deploy(); + await mockArbitrumSys.deployed(); + + await network.provider.send("hardhat_setCode", [ + "0x0000000000000000000000000000000000000064", + await ethers.provider.getCode(mockArbitrumSys.address) + ]); +} + async function bean() { await token(BEAN, 6); // if a new beanstalk is deployed, the bean token should use "BeanstalkERC20", @@ -190,3 +201,4 @@ exports.impersonateChainlinkAggregator = chainlinkAggregator; exports.impersonateContract = impersonateContract; exports.impersonateUniswapV3 = uniswapV3; exports.impersonatePipeline = impersonatePipeline; +exports.impersonateMockArbitrumSys = MockArbitrumSys; diff --git a/protocol/test/foundry/utils/TestHelper.sol b/protocol/test/foundry/utils/TestHelper.sol index 3e0eb6be4..67da0b205 100644 --- a/protocol/test/foundry/utils/TestHelper.sol +++ b/protocol/test/foundry/utils/TestHelper.sol @@ -26,7 +26,7 @@ import {AppStorage} from "contracts/beanstalk/storage/AppStorage.sol"; ///// COMMON IMPORTED LIBRARIES ////// import {LibTransfer} from "contracts/libraries/Token/LibTransfer.sol"; import {LibConvertData} from "contracts/libraries/Convert/LibConvertData.sol"; - +import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; ///// ECOSYSTEM ////// import {Pipeline} from "contracts/pipeline/Pipeline.sol"; @@ -110,6 +110,8 @@ contract TestHelper is // initialize oracle configuration initWhitelistOracles(verbose); + + initMockArbitrumSys(); } /** @@ -161,6 +163,10 @@ contract TestHelper is } } + function initMockArbitrumSys() internal { + deployCodeTo("MockArbitrumSys.sol", "", LibArbitrum.ARB_SYS_PRECOMPILE); + } + /** * @notice max approves bean for beanstalk. */ From a3931acf89ead5b9f37df95130819fd9d1b1b119 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Thu, 10 Oct 2024 21:43:16 -0400 Subject: [PATCH 02/13] update foundry.toml --- protocol/foundry.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/foundry.toml b/protocol/foundry.toml index 2c463e984..8ff50cb1b 100644 --- a/protocol/foundry.toml +++ b/protocol/foundry.toml @@ -4,7 +4,6 @@ src = 'contracts' test = 'test' script = 'script' -no_match_test = "testDiff" out = 'out' libs = ['node_modules', 'lib'] cache = true @@ -42,12 +41,13 @@ ignored_warnings_from = [ gas_reports = ['*'] # Cache to `$HOME/.foundry/cache//`. no_storage_caching = false +no_match_test = "testFork" no_match_contract = "Reseed" [profile.differential] match_test = "testDiff" -no_match_test = "a^" -no_match_contract = "ReseedStateTest" +no_match_test = ["a^"] +no_match_contract = ["ReseedStateTest"] [profile.default.rpc_storage_caching] chains = 'all' From e9567a086c54f128db40d8cf18d7fc563d047e85 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Thu, 10 Oct 2024 23:35:46 -0400 Subject: [PATCH 03/13] Add tasks. --- .../contracts/ecosystem/arbitrum/ArbSys.sol | 16 +++++ .../contracts/interfaces/IMockFBeanstalk.sol | 2 + .../libraries/Convert/LibUnripeConvert.sol | 2 +- protocol/foundry.toml | 1 - protocol/hardhat.config.js | 23 ++++++- protocol/scripts/ebips.js | 64 +++++++++++++++---- .../foundry/Migration/ReseedContracts.sol | 2 +- .../test/foundry/Migration/ReseedState.t.sol | 7 +- .../test/foundry/Migration/data/accounts.txt | 20 +++--- protocol/utils/signer.js | 17 ++--- 10 files changed, 118 insertions(+), 36 deletions(-) create mode 100644 protocol/contracts/ecosystem/arbitrum/ArbSys.sol diff --git a/protocol/contracts/ecosystem/arbitrum/ArbSys.sol b/protocol/contracts/ecosystem/arbitrum/ArbSys.sol new file mode 100644 index 000000000..9e4fe17ec --- /dev/null +++ b/protocol/contracts/ecosystem/arbitrum/ArbSys.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; + +/** + * @title ArbSys + * @author Brean + * @notice ArbSys is a precompile for the Arbitrum network that provides the current block number. + */ +contract ArbSys { + function arbBlockNumber() external view returns (uint256) { + return LibArbitrum.blockNumber(); + } +} diff --git a/protocol/contracts/interfaces/IMockFBeanstalk.sol b/protocol/contracts/interfaces/IMockFBeanstalk.sol index ecefa2235..51473da2d 100644 --- a/protocol/contracts/interfaces/IMockFBeanstalk.sol +++ b/protocol/contracts/interfaces/IMockFBeanstalk.sol @@ -2166,4 +2166,6 @@ interface IMockFBeanstalk { function woohoo() external pure returns (uint256); function wrapEth(uint256 amount, uint8 mode) external payable; + + function l2BlockNumber() external view returns (uint256); } diff --git a/protocol/contracts/libraries/Convert/LibUnripeConvert.sol b/protocol/contracts/libraries/Convert/LibUnripeConvert.sol index 4e5ab6c58..2fb058969 100644 --- a/protocol/contracts/libraries/Convert/LibUnripeConvert.sol +++ b/protocol/contracts/libraries/Convert/LibUnripeConvert.sol @@ -114,7 +114,7 @@ library LibUnripeConvert { uint256 lp = LibUnripe.unripeToUnderlying( s.sys.tokens.urLp, amountIn, - IBean(s.sys.tokens.urBean).totalSupply() + IBean(s.sys.tokens.urLp).totalSupply() ); bean = LibWellConvert.getBeanAmountOut(LibBarnRaise.getBarnRaiseWell(), lp); bean = LibUnripe diff --git a/protocol/foundry.toml b/protocol/foundry.toml index 8ff50cb1b..86689af9b 100644 --- a/protocol/foundry.toml +++ b/protocol/foundry.toml @@ -42,7 +42,6 @@ gas_reports = ['*'] # Cache to `$HOME/.foundry/cache//`. no_storage_caching = false no_match_test = "testFork" -no_match_contract = "Reseed" [profile.differential] match_test = "testDiff" diff --git a/protocol/hardhat.config.js b/protocol/hardhat.config.js index 79ce642be..a016aab3c 100644 --- a/protocol/hardhat.config.js +++ b/protocol/hardhat.config.js @@ -53,7 +53,7 @@ const { bipSeedGauge, bipMiscellaneousImprovements } = require("./scripts/bips.js"); -const { ebip9, ebip10, ebip11, ebip13, ebip14, ebip15 } = require("./scripts/ebips.js"); +const { ebip9, ebip10, ebip11, ebip13, ebip14, ebip15, ebip19 } = require("./scripts/ebips.js"); //////////////////////// UTILITIES //////////////////////// @@ -488,6 +488,27 @@ task("updateBeanstalkForUI", async function () { /// EBIPS /// +task("ebip19", async function () { + await ebip19(); +}); + +task("verifyEBIP19", async function () { + let beanstalk = await getBeanstalk(L2_BEANSTALK); + let before = await beanstalk.getAmountOut( + "0x1BEA059c3Ea15F6C10be1c53d70C75fD1266D788", + "0x1BEA054dddBca12889e07B3E076f511Bf1d27543", + 1000000 + ); + console.log("view redeem before", before); + await ebip19(); + let after = await beanstalk.getAmountOut( + "0x1BEA059c3Ea15F6C10be1c53d70C75fD1266D788", + "0x1BEA054dddBca12889e07B3E076f511Bf1d27543", + 1000000 + ); + console.log("view redeem after", after); +}); + task("ebip17", async function () { await ebip17(); }); diff --git a/protocol/scripts/ebips.js b/protocol/scripts/ebips.js index 7f8fe5b62..d96304790 100644 --- a/protocol/scripts/ebips.js +++ b/protocol/scripts/ebips.js @@ -1,5 +1,5 @@ const fs = require("fs"); -const { BEANSTALK } = require("../test/hardhat/utils/constants"); +const { BEANSTALK, L2_BEANSTALK } = require("../test/hardhat/utils/constants"); const { getBeanstalk, impersonateBeanstalkOwner, mintEth, strDisplay } = require("../utils"); const { upgradeWithNewFacets } = require("./diamond"); @@ -175,9 +175,9 @@ async function ebip15(mock = true, account = undefined) { await upgradeWithNewFacets({ diamondAddress: BEANSTALK, facetNames: ["SiloFacet", "SiloGettersFacet"], - libraryNames: ['LibSilo'], + libraryNames: ["LibSilo"], facetLibraries: { - 'SiloFacet': ['LibSilo'] + SiloFacet: ["LibSilo"] }, bip: false, object: !mock, @@ -195,10 +195,10 @@ async function ebip16(mock = true, account = undefined) { await upgradeWithNewFacets({ diamondAddress: BEANSTALK, facetNames: ["SiloFacet", "SiloGettersFacet", "ConvertFacet", "EnrootFacet"], - libraryNames: ['LibSilo', 'LibConvert'], + libraryNames: ["LibSilo", "LibConvert"], facetLibraries: { - 'SiloFacet': ['LibSilo'], - 'ConvertFacet': ['LibConvert'] + SiloFacet: ["LibSilo"], + ConvertFacet: ["LibConvert"] }, bip: false, object: !mock, @@ -215,13 +215,20 @@ async function ebip17(mock = true, account = undefined) { await upgradeWithNewFacets({ diamondAddress: BEANSTALK, - facetNames: ["MigrationFacet", "MarketplaceFacet", "ConvertFacet", "EnrootFacet", "SiloGettersFacet", "SiloFacet"], + facetNames: [ + "MigrationFacet", + "MarketplaceFacet", + "ConvertFacet", + "EnrootFacet", + "SiloGettersFacet", + "SiloFacet" + ], initFacetName: "InitHotFix6", - libraryNames: ['LibSilo', 'LibConvert'], + libraryNames: ["LibSilo", "LibConvert"], facetLibraries: { - 'SiloFacet': ['LibSilo'], - 'ConvertFacet': ['LibConvert'], - 'EnrootFacet': ['LibSilo'] + SiloFacet: ["LibSilo"], + ConvertFacet: ["LibConvert"], + EnrootFacet: ["LibSilo"] }, bip: false, object: !mock, @@ -230,6 +237,40 @@ async function ebip17(mock = true, account = undefined) { }); } +async function ebip19(mock = true, account = undefined) { + if (account == undefined) { + account = await impersonateBeanstalkOwner(L2_BEANSTALK); + await mintEth(account.address); + } + + await upgradeWithNewFacets({ + diamondAddress: L2_BEANSTALK, + facetNames: [ + "SeasonGettersFacet", + "ConvertGettersFacet", + "FieldFacet", + "ConvertFacet", + "PipelineConvertFacet" + ], + libraryNames: [ + "LibLockedUnderlying", + "LibWellMinting", + "LibPipelineConvert", + "LibConvert", + "LibSilo", + "LibTokenSilo" + ], + facetLibraries: { + SeasonGettersFacet: ["LibLockedUnderlying", "LibWellMinting"], + ConvertFacet: ["LibPipelineConvert", "LibConvert", "LibSilo", "LibTokenSilo"], + PipelineConvertFacet: ["LibPipelineConvert", "LibSilo", "LibTokenSilo"] + }, + bip: false, + object: !mock, + verbose: true, + account: account + }); +} async function bipDiamondCut(name, dc, account, mock = true) { beanstalk = await getBeanstalk(); @@ -265,3 +306,4 @@ exports.ebip14 = ebip14; exports.ebip15 = ebip15; exports.ebip16 = ebip16; exports.ebip17 = ebip17; +exports.ebip19 = ebip19; diff --git a/protocol/test/foundry/Migration/ReseedContracts.sol b/protocol/test/foundry/Migration/ReseedContracts.sol index 1b447274e..9a8a124a2 100644 --- a/protocol/test/foundry/Migration/ReseedContracts.sol +++ b/protocol/test/foundry/Migration/ReseedContracts.sol @@ -29,7 +29,7 @@ Running this fork test against arbitrum requires the following steps: 8. forge t --fork-url http://127.0.0.1:8545 --match-contract L1ReceiverFacetForkTest */ -contract L1ReceiverFacetForkTest is Order, TestHelper { +contract ReseedL1ReceiverFacetForkTest is Order, TestHelper { using Strings for string; // Offset arbitrum uses for corresponding L2 address uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); diff --git a/protocol/test/foundry/Migration/ReseedState.t.sol b/protocol/test/foundry/Migration/ReseedState.t.sol index 1a21d413d..e53bbbfb4 100644 --- a/protocol/test/foundry/Migration/ReseedState.t.sol +++ b/protocol/test/foundry/Migration/ReseedState.t.sol @@ -15,6 +15,9 @@ 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"; +import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; +import {IDiamondCut} from "contracts/interfaces/IDiamondCut.sol"; +import {SeasonGettersFacet} from "contracts/beanstalk/sun/SeasonFacet/SeasonGettersFacet.sol"; interface IBeanstalkPrice { function price() external view returns (P.Prices memory p); @@ -88,6 +91,7 @@ contract ReseedStateTest is TestHelper { uint256 accountNumber; function setUp() public { + l2Beanstalk = IMockFBeanstalk(0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70); // parse accounts and populate the accounts.txt file // the number of accounts to parse, for testing purposes // the total number of accounts is 3665 @@ -103,9 +107,6 @@ contract ReseedStateTest is TestHelper { } function test_WhitelistingState() public { - // address L2_BEANSTALK = address(0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70); - // IMockFBeanstalk l2Beanstalk = IMockFBeanstalk(L2_BEANSTALK); - // get AssetSettings of L2BEAN IMockFBeanstalk.AssetSettings memory assetSettings = l2Beanstalk.tokenSettings(L2BEAN); diff --git a/protocol/test/foundry/Migration/data/accounts.txt b/protocol/test/foundry/Migration/data/accounts.txt index f1af72ef5..a61826da8 100644 --- a/protocol/test/foundry/Migration/data/accounts.txt +++ b/protocol/test/foundry/Migration/data/accounts.txt @@ -1,10 +1,10 @@ -0x0000002e4F99CB1e699042699b91623B1334D2F7 -0x001f43cd9E84d90E1ee9DB192724ceF073D3FB2e -0x002505eefcBd852a148f03cA3451811032A72f96 -0x00427C81629Cd592Aa068B0290425261cbB8Eba2 -0x0063886D458CC0790a170dEBA645A0bcCC7f44D9 -0x006b4b47C7F404335c87E85355e217305F97E789 -0x0086e622aC7afa3e5502dc895Fd0EAB8B3A78D97 -0x008829aCd7Ec452Fc50989aA9BFa5d196Baae20a -0x008D63fab8179Ee0aE2082Bb57C72ED0c61f990f -0x00975ae9c986df066c7bbDA496103B4cC44B26c3 \ No newline at end of file +0x0255b20571acc2e1708ADE387b692360537F9e89 +0x0259D65954DfbD0735E094C9CdACC256e5A29dD4 +0x028afa72DADB6311107c382cF87504F37F11D482 +0x029D058CFdBE37eb93949e4143c516557B89EB3c +0x02A527084F5E73AF7781846762c8753aCD096461 +0x02bfbb25bf8396910378bF3b3ce82C0CE6d5E61d +0x02df7e960FFda6Db4030003D1784A7639947d200 +0x02FE27e7000C7B31E25E08dC3cDFdE5F39d659c5 +0x0301871FeDc523AB336535Ed14B939A956c4c39F +0x030ae585DB6d5169B3594eC37c008662f2494a1D \ No newline at end of file diff --git a/protocol/utils/signer.js b/protocol/utils/signer.js index 46d435f0d..3f15e079b 100644 --- a/protocol/utils/signer.js +++ b/protocol/utils/signer.js @@ -1,22 +1,23 @@ -const { getBeanstalk } = require('./contracts.js'); +const { getBeanstalk } = require("./contracts.js"); +const { BEANSTALK } = require("../test/hardhat/utils/constants.js"); async function impersonateSigner(signerAddress, withEth = false) { await hre.network.provider.request({ method: "hardhat_impersonateAccount", - params: [signerAddress], + params: [signerAddress] }); if (withEth) { await hre.network.provider.send("hardhat_setBalance", [signerAddress, "0x21E19E0C9BAB2400000"]); } - - return await ethers.getSigner(signerAddress) + + return await ethers.getSigner(signerAddress); } -async function impersonateBeanstalkOwner() { - const owner = await (await getBeanstalk()).owner() - return await impersonateSigner(owner, true) +async function impersonateBeanstalkOwner(beanstalk = BEANSTALK) { + const owner = await (await getBeanstalk(beanstalk)).owner(); + return await impersonateSigner(owner, true); } exports.impersonateSigner = impersonateSigner; -exports.impersonateBeanstalkOwner = impersonateBeanstalkOwner; \ No newline at end of file +exports.impersonateBeanstalkOwner = impersonateBeanstalkOwner; From 08d1ec8d3cf63dee7ec4221dbd5aa3e534c4eb24 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Fri, 11 Oct 2024 01:08:03 -0400 Subject: [PATCH 04/13] add L2 block number --- protocol/abi/Beanstalk.json | 13 +++++++++++++ .../sun/SeasonFacet/SeasonGettersFacet.sol | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/protocol/abi/Beanstalk.json b/protocol/abi/Beanstalk.json index ffb6da878..82f0d3575 100644 --- a/protocol/abi/Beanstalk.json +++ b/protocol/abi/Beanstalk.json @@ -10228,6 +10228,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "l2BlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "paused", diff --git a/protocol/contracts/beanstalk/sun/SeasonFacet/SeasonGettersFacet.sol b/protocol/contracts/beanstalk/sun/SeasonFacet/SeasonGettersFacet.sol index 9bb6396e0..172bda027 100644 --- a/protocol/contracts/beanstalk/sun/SeasonFacet/SeasonGettersFacet.sol +++ b/protocol/contracts/beanstalk/sun/SeasonFacet/SeasonGettersFacet.sol @@ -16,6 +16,7 @@ import {LibRedundantMath256} from "contracts/libraries/LibRedundantMath256.sol"; import {LibDeltaB} from "contracts/libraries/Oracle/LibDeltaB.sol"; import {LibFlood} from "contracts/libraries/Silo/LibFlood.sol"; import {BeanstalkERC20} from "contracts/tokens/ERC20/BeanstalkERC20.sol"; +import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; /** * @title SeasonGettersFacet @@ -297,4 +298,8 @@ contract SeasonGettersFacet { { return LibFlood.getWellsByDeltaB(); } + + function l2BlockNumber() external view returns (uint256) { + return LibArbitrum.blockNumber(); + } } From 7ab7ee7e1dc4f6aa007c380969892f1754147866 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Fri, 11 Oct 2024 01:08:33 -0400 Subject: [PATCH 05/13] add ebip tasks --- protocol/hardhat.config.js | 7 ++++++- protocol/scripts/ebips.js | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/protocol/hardhat.config.js b/protocol/hardhat.config.js index a016aab3c..735f81fc6 100644 --- a/protocol/hardhat.config.js +++ b/protocol/hardhat.config.js @@ -54,6 +54,7 @@ const { bipMiscellaneousImprovements } = require("./scripts/bips.js"); const { ebip9, ebip10, ebip11, ebip13, ebip14, ebip15, ebip19 } = require("./scripts/ebips.js"); +const { impersonateMockArbitrumSys } = require("./scripts/impersonate.js"); //////////////////////// UTILITIES //////////////////////// @@ -492,7 +493,9 @@ task("ebip19", async function () { await ebip19(); }); -task("verifyEBIP19", async function () { +task("verify-ebip19", async function () { + // due to hardhats inability to impersonate precompiles, a mock is used instead. + await impersonateMockArbitrumSys(); let beanstalk = await getBeanstalk(L2_BEANSTALK); let before = await beanstalk.getAmountOut( "0x1BEA059c3Ea15F6C10be1c53d70C75fD1266D788", @@ -506,6 +509,8 @@ task("verifyEBIP19", async function () { "0x1BEA054dddBca12889e07B3E076f511Bf1d27543", 1000000 ); + let block = await beanstalk.l2BlockNumber(); + console.log("block", block); console.log("view redeem after", after); }); diff --git a/protocol/scripts/ebips.js b/protocol/scripts/ebips.js index d96304790..ddd0c8cec 100644 --- a/protocol/scripts/ebips.js +++ b/protocol/scripts/ebips.js @@ -247,6 +247,7 @@ async function ebip19(mock = true, account = undefined) { diamondAddress: L2_BEANSTALK, facetNames: [ "SeasonGettersFacet", + "SeasonFacet", "ConvertGettersFacet", "FieldFacet", "ConvertFacet", @@ -258,13 +259,32 @@ async function ebip19(mock = true, account = undefined) { "LibPipelineConvert", "LibConvert", "LibSilo", - "LibTokenSilo" + "LibTokenSilo", + "LibEvaluate", + "LibGauge", + "LibIncentive", + "LibShipping", + "LibFlood", + "LibGerminate", + "LibWellMinting" ], facetLibraries: { SeasonGettersFacet: ["LibLockedUnderlying", "LibWellMinting"], + SeasonFacet: [ + "LibEvaluate", + "LibGauge", + "LibIncentive", + "LibShipping", + "LibFlood", + "LibGerminate", + "LibWellMinting" + ], ConvertFacet: ["LibPipelineConvert", "LibConvert", "LibSilo", "LibTokenSilo"], PipelineConvertFacet: ["LibPipelineConvert", "LibSilo", "LibTokenSilo"] }, + linkedLibraries: { + LibEvaluate: ["LibLockedUnderlying"] + }, bip: false, object: !mock, verbose: true, From 3d17321f550d0326cba70e8beff8ee651776a225 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Fri, 11 Oct 2024 01:30:45 -0400 Subject: [PATCH 06/13] return the ratios if the Bean reserve is less than minimum. --- protocol/contracts/libraries/Minting/LibWellMinting.sol | 2 +- protocol/hardhat.config.js | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/protocol/contracts/libraries/Minting/LibWellMinting.sol b/protocol/contracts/libraries/Minting/LibWellMinting.sol index d5060b474..b0f29b9d4 100644 --- a/protocol/contracts/libraries/Minting/LibWellMinting.sol +++ b/protocol/contracts/libraries/Minting/LibWellMinting.sol @@ -148,7 +148,7 @@ library LibWellMinting { // If the Bean reserve is less than the minimum, the minting oracle should be considered off. if (reserves[beanIndex] < C.WELL_MINIMUM_BEAN_BALANCE) { - return (0, snapshot, new uint256[](0), new uint256[](0)); + return (0, snapshot, new uint256[](0), ratios); } // If the USD Oracle oracle call fails, the minting oracle should be considered off. diff --git a/protocol/hardhat.config.js b/protocol/hardhat.config.js index 735f81fc6..7ddec0562 100644 --- a/protocol/hardhat.config.js +++ b/protocol/hardhat.config.js @@ -493,6 +493,10 @@ task("ebip19", async function () { await ebip19(); }); +task("impersonateArb", async function () { + await impersonateMockArbitrumSys(); +}); + task("verify-ebip19", async function () { // due to hardhats inability to impersonate precompiles, a mock is used instead. await impersonateMockArbitrumSys(); From c0cec34ab88f3e24899481789938eb72ad146211 Mon Sep 17 00:00:00 2001 From: pizzaman1337 Date: Fri, 11 Oct 2024 15:21:01 -0400 Subject: [PATCH 07/13] Add getUsedConvertCapacity to inspect per-block capacities used --- protocol/abi/Beanstalk.json | 43 +++++++ protocol/abi/MockBeanstalk.json | 13 +++ .../beanstalk/silo/ConvertGettersFacet.sol | 40 +++++++ protocol/hardhat.config.js | 108 ++++++++++++++++++ protocol/scripts/ebips.js | 4 +- 5 files changed, 207 insertions(+), 1 deletion(-) diff --git a/protocol/abi/Beanstalk.json b/protocol/abi/Beanstalk.json index 82f0d3575..14ff0e626 100644 --- a/protocol/abi/Beanstalk.json +++ b/protocol/abi/Beanstalk.json @@ -5060,6 +5060,49 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "getUsedConvertCapacity", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "overallConvertCapacityUsed", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "address", + "name": "well", + "type": "address" + }, + { + "internalType": "uint256", + "name": "wellConvertCapacityUsed", + "type": "uint256" + } + ], + "internalType": "struct ConvertGettersFacet.WellConvertCapacityUsed[]", + "name": "wellConvertCapacityUsed", + "type": "tuple[]" + } + ], + "internalType": "struct ConvertGettersFacet.ConvertCapacityUsed", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/protocol/abi/MockBeanstalk.json b/protocol/abi/MockBeanstalk.json index 63757ef2d..3ef119f6e 100644 --- a/protocol/abi/MockBeanstalk.json +++ b/protocol/abi/MockBeanstalk.json @@ -9566,6 +9566,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "l2BlockNumber", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "paused", diff --git a/protocol/contracts/beanstalk/silo/ConvertGettersFacet.sol b/protocol/contracts/beanstalk/silo/ConvertGettersFacet.sol index da8a924f7..633b64783 100644 --- a/protocol/contracts/beanstalk/silo/ConvertGettersFacet.sol +++ b/protocol/contracts/beanstalk/silo/ConvertGettersFacet.sol @@ -10,6 +10,7 @@ import {LibConvert} from "contracts/libraries/Convert/LibConvert.sol"; import {LibWellMinting} from "contracts/libraries/Minting/LibWellMinting.sol"; import {LibDeltaB} from "contracts/libraries/Oracle/LibDeltaB.sol"; import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; +import {LibWhitelistedTokens} from "contracts/libraries/Silo/LibWhitelistedTokens.sol"; /** * @author Publius @@ -17,6 +18,15 @@ import {LibArbitrum} from "contracts/libraries/LibArbitrum.sol"; **/ contract ConvertGettersFacet { using LibRedundantMath256 for uint256; + struct WellConvertCapacityUsed { + address well; + uint256 wellConvertCapacityUsed; + } + + struct ConvertCapacityUsed { + uint256 overallConvertCapacityUsed; + WellConvertCapacityUsed[] wellConvertCapacityUsed; + } /** * @notice Returns the maximum amount that can be converted of `tokenIn` to `tokenOut`. @@ -115,6 +125,36 @@ contract ConvertGettersFacet { ); } + /** + * @notice Returns the used convert capacity information at a specific block number. + * @param blockNumber the block number to get the used convert capacity for + */ + function getUsedConvertCapacity( + uint256 blockNumber + ) external view returns (ConvertCapacityUsed memory) { + AppStorage storage s = LibAppStorage.diamondStorage(); + address[] memory tokens = LibWhitelistedTokens.getWhitelistedWellLpTokens(); + ConvertCapacityUsed memory usedConvertCapacity; + usedConvertCapacity.wellConvertCapacityUsed = new WellConvertCapacityUsed[](tokens.length); + for (uint i; i < tokens.length; i++) { + address well = tokens[i]; + uint256 wellConvertCapacityUsed = s + .sys + .convertCapacity[blockNumber] + .wellConvertCapacityUsed[well]; + + WellConvertCapacityUsed memory wellConvertCapacityUsedStruct; + wellConvertCapacityUsedStruct.well = well; + wellConvertCapacityUsedStruct.wellConvertCapacityUsed = wellConvertCapacityUsed; + usedConvertCapacity.wellConvertCapacityUsed[i] = wellConvertCapacityUsedStruct; + } + usedConvertCapacity.overallConvertCapacityUsed = s + .sys + .convertCapacity[blockNumber] + .overallConvertCapacityUsed; + return usedConvertCapacity; + } + /** * @notice Calculates the bdv penalized by a convert. * @dev See {LibConvert.calculateStalkPenalty}. diff --git a/protocol/hardhat.config.js b/protocol/hardhat.config.js index 7ddec0562..468bf477b 100644 --- a/protocol/hardhat.config.js +++ b/protocol/hardhat.config.js @@ -140,6 +140,114 @@ task("tokenSettings", async function () { console.log(tokenSettings); }); +task("l2BlockNumber", async function () { + beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); + const l2BlockNumber = await beanstalk.l2BlockNumber(); + console.log(l2BlockNumber); +}); + +task("totalSoil", async function () { + beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); + const totalSoil = await beanstalk.totalSoil(); + console.log(totalSoil); +}); + +task("temperature", async function () { + beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); + const temperature = await beanstalk.temperature(); + console.log(temperature); +}); + +task("next-block", "Advances to the next block").setAction(async (taskArgs, hre) => { + await hre.network.provider.send("evm_mine"); + const blockNumber = await hre.ethers.provider.getBlockNumber(); + console.log(`Advanced to block number: ${blockNumber}`); +}); + +task("advance-blocks", "Advances the blockchain by a specified number of blocks") + .addParam("number", "The number of blocks to advance") + .setAction(async (taskArgs, hre) => { + const numBlocks = parseInt(taskArgs.number); + + if (isNaN(numBlocks) || numBlocks <= 0) { + console.error("Please provide a valid positive number of blocks to advance."); + return; + } + + const startBlock = await hre.ethers.provider.getBlockNumber(); + + for (let i = 0; i < numBlocks; i++) { + await hre.network.provider.send("evm_mine"); + } + + const endBlock = await hre.ethers.provider.getBlockNumber(); + + console.log(`Advanced from block ${startBlock} to ${endBlock}`); + console.log(`Total blocks advanced: ${endBlock - startBlock}`); + }); + +task("send-custom-tx", "Sends a custom transaction with specified from, to, and data") + .addParam("from", "The address to send the transaction from") + .addParam("to", "The address to send the transaction to") + .addParam("data", "The transaction data (hex-encoded)") + .addOptionalParam("value", "The amount of ETH to send (in wei)", "0") + .setAction(async (taskArgs, hre) => { + const { from, to, data, value } = taskArgs; + + await mintEth(from); + + // Validate inputs + if (!hre.ethers.utils.isAddress(from)) { + throw new Error("Invalid 'from' address"); + } + if (!hre.ethers.utils.isAddress(to)) { + throw new Error("Invalid 'to' address"); + } + if (!/^0x[0-9A-Fa-f]*$/.test(data)) { + throw new Error("Invalid 'data' format. Must be hex-encoded starting with 0x"); + } + + // Prepare the transaction + const tx = { + from: from, + to: to, + data: data, + value: hre.ethers.utils.parseEther(value) + }; + + try { + // Send the transaction + const signer = await hre.ethers.getSigner(from); + const txResponse = await signer.sendTransaction(tx); + + console.log("Transaction sent successfully!"); + console.log("Transaction hash:", txResponse.hash); + + // Wait for the transaction to be mined + const receipt = await txResponse.wait(); + console.log("Transaction mined in block:", receipt.blockNumber); + } catch (error) { + console.error("Error sending transaction:", error.message); + } + }); + +// task for getOverallConvertCapacity +task("getOverallConvertCapacity", async function () { + beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); + const overallCappedDeltaB = await beanstalk.getOverallConvertCapacity(); + console.log(overallCappedDeltaB); +}); + +task("getUsedConvertCapacity", "Gets the used convert capacity at a specific block") + .addParam("block", "The block number to query") + .setAction(async (taskArgs) => { + const blockNumber = parseInt(taskArgs.block); + beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); + + const usedConvertCapacities = await beanstalk.getUsedConvertCapacity(blockNumber); + console.log(usedConvertCapacities); + }); + /*task('replant', async () => { const account = await impersonateSigner(PUBLIUS) await replant(account) diff --git a/protocol/scripts/ebips.js b/protocol/scripts/ebips.js index ddd0c8cec..597935406 100644 --- a/protocol/scripts/ebips.js +++ b/protocol/scripts/ebips.js @@ -266,7 +266,9 @@ async function ebip19(mock = true, account = undefined) { "LibShipping", "LibFlood", "LibGerminate", - "LibWellMinting" + "LibWellMinting", + "LibConvert", + "LibWhitelistedTokens" ], facetLibraries: { SeasonGettersFacet: ["LibLockedUnderlying", "LibWellMinting"], From bef323c59fd9e6333a276db3f0247ebbffe43f7f Mon Sep 17 00:00:00 2001 From: pizzaman1337 Date: Fri, 11 Oct 2024 15:34:15 -0400 Subject: [PATCH 08/13] Remove some hardhat tasks --- protocol/hardhat.config.js | 47 -------------------------------------- 1 file changed, 47 deletions(-) diff --git a/protocol/hardhat.config.js b/protocol/hardhat.config.js index 468bf477b..1c2c5fd7b 100644 --- a/protocol/hardhat.config.js +++ b/protocol/hardhat.config.js @@ -134,36 +134,6 @@ task("getTime", async function () { console.log("Current time: ", await this.seasonGetter.time()); }); -task("tokenSettings", async function () { - beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); - const tokenSettings = await beanstalk.tokenSettings("0xBEA0005B8599265D41256905A9B3073D397812E4"); - console.log(tokenSettings); -}); - -task("l2BlockNumber", async function () { - beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); - const l2BlockNumber = await beanstalk.l2BlockNumber(); - console.log(l2BlockNumber); -}); - -task("totalSoil", async function () { - beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); - const totalSoil = await beanstalk.totalSoil(); - console.log(totalSoil); -}); - -task("temperature", async function () { - beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); - const temperature = await beanstalk.temperature(); - console.log(temperature); -}); - -task("next-block", "Advances to the next block").setAction(async (taskArgs, hre) => { - await hre.network.provider.send("evm_mine"); - const blockNumber = await hre.ethers.provider.getBlockNumber(); - console.log(`Advanced to block number: ${blockNumber}`); -}); - task("advance-blocks", "Advances the blockchain by a specified number of blocks") .addParam("number", "The number of blocks to advance") .setAction(async (taskArgs, hre) => { @@ -231,23 +201,6 @@ task("send-custom-tx", "Sends a custom transaction with specified from, to, and } }); -// task for getOverallConvertCapacity -task("getOverallConvertCapacity", async function () { - beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); - const overallCappedDeltaB = await beanstalk.getOverallConvertCapacity(); - console.log(overallCappedDeltaB); -}); - -task("getUsedConvertCapacity", "Gets the used convert capacity at a specific block") - .addParam("block", "The block number to query") - .setAction(async (taskArgs) => { - const blockNumber = parseInt(taskArgs.block); - beanstalk = await getBeanstalk("0xD1A0060ba708BC4BCD3DA6C37EFa8deDF015FB70"); - - const usedConvertCapacities = await beanstalk.getUsedConvertCapacity(blockNumber); - console.log(usedConvertCapacities); - }); - /*task('replant', async () => { const account = await impersonateSigner(PUBLIUS) await replant(account) From 20c9c4010be0ab140d223ef0c87ae9e7d9732fdd Mon Sep 17 00:00:00 2001 From: Brean0 Date: Fri, 11 Oct 2024 18:15:59 -0400 Subject: [PATCH 09/13] add init script. --- .../init/InitMultFlowPumpUpgrade.sol | 54 ++++++++++++++++ .../libraries/Basin/LibWellDeployer.sol | 61 +++++++++++++++++++ protocol/scripts/ebips.js | 1 + 3 files changed, 116 insertions(+) create mode 100644 protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol create mode 100644 protocol/contracts/libraries/Basin/LibWellDeployer.sol diff --git a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol new file mode 100644 index 000000000..be47810e2 --- /dev/null +++ b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol @@ -0,0 +1,54 @@ +/* + SPDX-License-Identifier: MIT +*/ + +pragma solidity ^0.8.20; + +import {IAquifer} from "../interfaces/basin/IAquifer.sol"; +import {IWell} from "../interfaces/basin/IWell.sol"; +import {LibWhitelistedTokens} from "../libraries/Silo/LibWhitelistedTokens.sol"; + +interface IWellUpgradeable { + function upgradeTo(address implementation) external; +} + +/** + * @author Brean + * @title InitMultiFlowPumpUpgrade upgrades the Whitelisted Wells to use the new MultiFlowPump. + **/ +contract InitMultiFlowPumpUpgrade { + address internal constant MULTI_FLOW_PUMP_V1_2_1 = 0xBA150002660BbCA20675D1C1535Cd76C98A95b13; + address internal constant U_WELL_IMPLEMENTATION = + address(0xBA510995783111be5301d93CCfD5dE4e3B28e50B); + address internal constant AQUIFER = address(0xBA51AAAa8C2f911AE672e783707Ceb2dA6E97521); + + function init() external { + address[] memory wells = LibWhitelistedTokens.getWhitelistedWellLpTokens(); + + for (uint256 i; i < wells.length; i++) { + IWell well = IWell(wells[i]); + // fetch the well's immutable and init data + IERC20[] memory tokens = well.tokens(); + Call memory wellFunction = well.wellFunction(); + Call[] memory pumps = well.pumps(); + + // replace the pump addresses with the new MultiFlowPump address + pumps[0].target = MULTI_FLOW_PUMP_V1_2_1; + + // encode the immutable and init data + (bytes memory immutableData, bytes memory initData) = LibWellDeployer + .encodeWellDeploymentData(AQUIFER, tokens, wellFunction, pumps); + + // deploy the new well: + address minimalProxyWell = IAquifer(AQUIFER).boreWell( + U_WELL_IMPLEMENTATION, + immutableData, + initData, + bytes32(1) + ); + + // upgrade the well to the new implementation + IWellUpgradeable(wells[i]).upgradeTo(minimalProxyWell); + } + } +} diff --git a/protocol/contracts/libraries/Basin/LibWellDeployer.sol b/protocol/contracts/libraries/Basin/LibWellDeployer.sol new file mode 100644 index 000000000..58aba10c9 --- /dev/null +++ b/protocol/contracts/libraries/Basin/LibWellDeployer.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +/** + * @title LibWellDeployer + * @author Brean + * @notice LibWellDeployer provides helper functions for deploying Wells with Aquifers. + */ +library LibWellDeployer { + /** + * @notice Encode the Well's immutable data. + */ + function encodeWellDeploymentData( + address _aquifer, + IERC20[] memory _tokens, + Call memory _wellFunction, + Call[] memory _pumps + ) internal pure returns (bytes memory immutableData, bytes memory initData) { + immutableData = encodeWellImmutableData(_aquifer, _tokens, _wellFunction, _pumps); + initData = abi.encodeWithSelector(IWellUpgradeable.initNoWellToken.selector); + } + + function encodeWellImmutableData( + address _aquifer, + IERC20[] memory _tokens, + Call memory _wellFunction, + Call[] memory _pumps + ) internal pure returns (bytes memory immutableData) { + immutableData = abi.encodePacked( + _aquifer, // aquifer address + _tokens.length, // number of tokens + _wellFunction.target, // well function address + _wellFunction.data.length, // well function data length + _pumps.length, // number of pumps + _tokens, // tokens array + _wellFunction.data // well function data (bytes) + ); + for (uint256 i; i < _pumps.length; ++i) { + immutableData = abi.encodePacked( + immutableData, // previously packed pumps + _pumps[i].target, // pump address + _pumps[i].data.length, // pump data length + _pumps[i].data // pump data (bytes) + ); + } + } + + /** + * @notice Encode the Well's immutable data. + */ + function encodeWellDeploymentData( + address _aquifer, + IERC20[] memory _tokens, + Call memory _wellFunction, + Call[] memory _pumps + ) internal pure returns (bytes memory immutableData, bytes memory initData) { + immutableData = encodeWellImmutableData(_aquifer, _tokens, _wellFunction, _pumps); + initData = abi.encodeWithSelector(IWellUpgradeable.initNoWellToken.selector); + } +} diff --git a/protocol/scripts/ebips.js b/protocol/scripts/ebips.js index 597935406..35003a61a 100644 --- a/protocol/scripts/ebips.js +++ b/protocol/scripts/ebips.js @@ -287,6 +287,7 @@ async function ebip19(mock = true, account = undefined) { linkedLibraries: { LibEvaluate: ["LibLockedUnderlying"] }, + initFacetName: "InitMultFlowPumpUpgrade", bip: false, object: !mock, verbose: true, From e97706de064d607cb9e40764b91d88bc505d91e0 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Fri, 11 Oct 2024 18:16:13 -0400 Subject: [PATCH 10/13] add init script. --- .../init/InitMultFlowPumpUpgrade.sol | 15 +++++++++---- .../interfaces/basin/IWellUpgradeable.sol | 22 +++++++++++++++++++ .../libraries/Basin/LibWellDeployer.sol | 16 +++----------- protocol/reseed/data/gas-report.csv | 2 ++ protocol/scripts/ebips.js | 2 +- 5 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 protocol/contracts/interfaces/basin/IWellUpgradeable.sol diff --git a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol index be47810e2..6ed90615b 100644 --- a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol +++ b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol @@ -4,9 +4,11 @@ pragma solidity ^0.8.20; -import {IAquifer} from "../interfaces/basin/IAquifer.sol"; -import {IWell} from "../interfaces/basin/IWell.sol"; -import {LibWhitelistedTokens} from "../libraries/Silo/LibWhitelistedTokens.sol"; +import {IAquifer} from "contracts/interfaces/basin/IAquifer.sol"; +import {IWell, Call, IERC20} from "contracts/interfaces/basin/IWell.sol"; +import {LibWhitelistedTokens} from "contracts/libraries/Silo/LibWhitelistedTokens.sol"; +import {LibWellDeployer} from "contracts/libraries/Basin/LibWellDeployer.sol"; +import {AppStorage} from "contracts/beanstalk/storage/AppStorage.sol"; interface IWellUpgradeable { function upgradeTo(address implementation) external; @@ -22,6 +24,8 @@ contract InitMultiFlowPumpUpgrade { address(0xBA510995783111be5301d93CCfD5dE4e3B28e50B); address internal constant AQUIFER = address(0xBA51AAAa8C2f911AE672e783707Ceb2dA6E97521); + AppStorage internal s; + function init() external { address[] memory wells = LibWhitelistedTokens.getWhitelistedWellLpTokens(); @@ -44,11 +48,14 @@ contract InitMultiFlowPumpUpgrade { U_WELL_IMPLEMENTATION, immutableData, initData, - bytes32(1) + bytes32("1") ); // upgrade the well to the new implementation IWellUpgradeable(wells[i]).upgradeTo(minimalProxyWell); + + // delete the well Oracle snapshot. + delete s.sys.wellOracleSnapshots[wells[i]]; } } } diff --git a/protocol/contracts/interfaces/basin/IWellUpgradeable.sol b/protocol/contracts/interfaces/basin/IWellUpgradeable.sol new file mode 100644 index 000000000..69cad0f72 --- /dev/null +++ b/protocol/contracts/interfaces/basin/IWellUpgradeable.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {IWell} from "contracts/interfaces/basin/IWell.sol"; + +/** + * @title IWell is the interface for the Well contract. + * + * In order for a Well to be verified using a permissionless on-chain registry, a Well Implementation should: + * - Not be able to self-destruct (Aquifer's registry would be vulnerable to a metamorphic contract attack) + * - Not be able to change its tokens, Well Function, Pumps and Well Data + */ +interface IWellUpgradeable is IWell { + function init(string memory name, string memory symbol) external; + + function initNoWellToken() external; + + function upgradeTo(address newImplementation) external; + + function upgradeToAndCall(address newImplementation, bytes memory data) external; +} diff --git a/protocol/contracts/libraries/Basin/LibWellDeployer.sol b/protocol/contracts/libraries/Basin/LibWellDeployer.sol index 58aba10c9..d9b0362bb 100644 --- a/protocol/contracts/libraries/Basin/LibWellDeployer.sol +++ b/protocol/contracts/libraries/Basin/LibWellDeployer.sol @@ -2,6 +2,9 @@ pragma solidity ^0.8.20; +import {IWell, Call, IERC20} from "contracts/interfaces/basin/IWell.sol"; +import {IWellUpgradeable} from "contracts/interfaces/basin/IWellUpgradeable.sol"; + /** * @title LibWellDeployer * @author Brean @@ -45,17 +48,4 @@ library LibWellDeployer { ); } } - - /** - * @notice Encode the Well's immutable data. - */ - function encodeWellDeploymentData( - address _aquifer, - IERC20[] memory _tokens, - Call memory _wellFunction, - Call[] memory _pumps - ) internal pure returns (bytes memory immutableData, bytes memory initData) { - immutableData = encodeWellImmutableData(_aquifer, _tokens, _wellFunction, _pumps); - initData = abi.encodeWithSelector(IWellUpgradeable.initNoWellToken.selector); - } } diff --git a/protocol/reseed/data/gas-report.csv b/protocol/reseed/data/gas-report.csv index 8161f5e5b..6205e5172 100644 --- a/protocol/reseed/data/gas-report.csv +++ b/protocol/reseed/data/gas-report.csv @@ -1,2 +1,4 @@ initFacetName,initFacetDeploymentGas,initFacetCallGas,totalGasUsed ReseedBean, 3751444, 10139054, 13890498 +InitMultiFlowPumpUpgrade, 622042, 3560791, 57241327 +InitMultiFlowPumpUpgrade, 669704, 3570925, 57299123 diff --git a/protocol/scripts/ebips.js b/protocol/scripts/ebips.js index 35003a61a..2f0fc16e3 100644 --- a/protocol/scripts/ebips.js +++ b/protocol/scripts/ebips.js @@ -287,7 +287,7 @@ async function ebip19(mock = true, account = undefined) { linkedLibraries: { LibEvaluate: ["LibLockedUnderlying"] }, - initFacetName: "InitMultFlowPumpUpgrade", + initFacetName: "InitMultiFlowPumpUpgrade", bip: false, object: !mock, verbose: true, From b6d06180f7ba8dee165ad1ce6cab1095f82f6c65 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Fri, 11 Oct 2024 19:11:49 -0400 Subject: [PATCH 11/13] sync well. --- protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol index 6ed90615b..f84284d57 100644 --- a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol +++ b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol @@ -53,6 +53,8 @@ contract InitMultiFlowPumpUpgrade { // upgrade the well to the new implementation IWellUpgradeable(wells[i]).upgradeTo(minimalProxyWell); + // call add liquidity to start the pump. + well.sync(address(this), 0); // delete the well Oracle snapshot. delete s.sys.wellOracleSnapshots[wells[i]]; From 039f950f01485c53c0f55f4dce439a81d654d81d Mon Sep 17 00:00:00 2001 From: Brean0 Date: Fri, 11 Oct 2024 23:15:32 -0400 Subject: [PATCH 12/13] update cp2 --- .../contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol index f84284d57..999e88a76 100644 --- a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol +++ b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol @@ -23,6 +23,10 @@ contract InitMultiFlowPumpUpgrade { address internal constant U_WELL_IMPLEMENTATION = address(0xBA510995783111be5301d93CCfD5dE4e3B28e50B); address internal constant AQUIFER = address(0xBA51AAAa8C2f911AE672e783707Ceb2dA6E97521); + address internal constant CP2_WELL_FUNCTION = + address(0x0000000000000000000000000000000000000000); + address internal constant BEAN_USDC = address(0xBea00ee04D8289aEd04f92EA122a96dC76A91bd7); + address internal constant BEAN_USDT = address(0xbEA00fF437ca7E8354B174339643B4d1814bED33); AppStorage internal s; @@ -39,6 +43,11 @@ contract InitMultiFlowPumpUpgrade { // replace the pump addresses with the new MultiFlowPump address pumps[0].target = MULTI_FLOW_PUMP_V1_2_1; + // if the well is not USDC or USDT, set the well function to CP2_WELL_FUNCTION + if (wells[i] != BEAN_USDC && wells[i] != BEAN_USDT) { + wellFunction.target = CP2_WELL_FUNCTION; + } + // encode the immutable and init data (bytes memory immutableData, bytes memory initData) = LibWellDeployer .encodeWellDeploymentData(AQUIFER, tokens, wellFunction, pumps); From a26664dd64ca39bdc1db98355736363f29a9fc06 Mon Sep 17 00:00:00 2001 From: Brean0 Date: Fri, 11 Oct 2024 23:29:26 -0400 Subject: [PATCH 13/13] add CP2 address. --- protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol index 999e88a76..be1320cfd 100644 --- a/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol +++ b/protocol/contracts/beanstalk/init/InitMultFlowPumpUpgrade.sol @@ -24,7 +24,7 @@ contract InitMultiFlowPumpUpgrade { address(0xBA510995783111be5301d93CCfD5dE4e3B28e50B); address internal constant AQUIFER = address(0xBA51AAAa8C2f911AE672e783707Ceb2dA6E97521); address internal constant CP2_WELL_FUNCTION = - address(0x0000000000000000000000000000000000000000); + address(0xBA15000450Bf6d48ec50BD6327A9403E401b72b4); address internal constant BEAN_USDC = address(0xBea00ee04D8289aEd04f92EA122a96dC76A91bd7); address internal constant BEAN_USDT = address(0xbEA00fF437ca7E8354B174339643B4d1814bED33);