Skip to content

Instantly share code, notes, and snippets.

@h0lydev
Last active March 26, 2024 13:55
Show Gist options
  • Save h0lydev/fcdb00c797adfdf8e4816031e095fd6c to your computer and use it in GitHub Desktop.
Save h0lydev/fcdb00c797adfdf8e4816031e095fd6c to your computer and use it in GitHub Desktop.

1 - Setup project to run with Hardhat/Foundry.

Run foundryup to ensure using the latest Foundry version

Run forge install to ensure dependencies are installed

Run yarn add --dev @nomicfoundation/hardhat-foundry to support foundry with hardhat

Add require("@nomicfoundation/hardhat-foundry"); on hardhat.config.js

2 - Disable deprecated foundry test files

Comment all the test foundry contracts below:

  1. Silo.t.sol
  2. Sun.t.sol
  3. Weather.t.sol
  4. Field.t.sol
  5. Bean.t.sol

3 - Adapt Foundry.toml to fork mainnet and run tests sucessfully

[profile.default]
# Project
# https://book.getfoundry.sh/reference/config/project
src = 'contracts'
test = 'test/foundry'
no_match_test = "testDiff"
out = 'out'
libs = [
  'node_modules',
  'lib'
]
cache = true
cache_path = 'cache'

# Compiler
# https://book.getfoundry.sh/reference/config/solidity-compiler
libraries = []
auto_detect_solc = true
ignored_error_codes = [1878, 2837]
optimizer = true
optimizer_runs = 200
via_ir = false
bytecode_hash = 'ipfs'
ignored_warnings_from = ["node_modules", "lib", "protocol"]

# Testing
# https://book.getfoundry.sh/reference/config/testing
verbosity = 0
ffi = true
fs_permissions = [{ access = "read", path = "./" }]
sender = '0x00a329c0648769a73afac7f9381e08fb43dbea72' # The value of `msg.sender` in tests.
tx_origin = '0x00a329c0648769a73afac7f9381e08fb43dbea72' # The value of `tx.origin` in tests.
gas_reports = ['*']
 # Cache to `$HOME/.foundry/cache/<chain id>/<block number>`.
no_storage_caching = false
evm_version = "shanghai" # fix for https://github.com/foundry-rs/foundry/issues/4988

[rpc_endpoints]
local = "http://127.0.0.1:8545"
 
[profile.differential]
match_test = "testDiff"
no_match_test = "a^"

# Formatter
# https://book.getfoundry.sh/reference/config/formatter
# ...pass...
[profile.default.rpc_storage_caching]
chains = 'all'
endpoints = 'all'

Adapting mocks

1. Add sync function to MockSetComponentsWell so it doesn't revert when IWell.sync is called

function sync(address recipient, uint256 minLpAmountOut) public returns (uint256 lpAmountOut) { 
        IERC20[] memory _tokens = tokens();
        uint256 tokensLength = _tokens.length;
        uint256[] memory reserves = new uint256[](tokensLength);
        for (uint256 i; i < tokensLength; ++i) {
            reserves[i] = _tokens[i].balanceOf(address(this));
        }
        uint256 newTokenSupply = _calcLpTokenSupply(wellFunction(), reserves);
        uint256 oldTokenSupply = totalSupply();
        if (newTokenSupply > oldTokenSupply) {
            lpAmountOut = newTokenSupply - oldTokenSupply;
            _mint(recipient, lpAmountOut);
        }

        if (lpAmountOut < minLpAmountOut) {
            revert ("reverted, slippageOut");
        }

        setReserves(reserves);
    }

2. Add these additional functions on MockSeasonFacet so we can use them when testing.

 // Getters for testing
    function currentSeason() public view returns (uint32) {
        return s.season.current;
    }

    function weather() public view returns (Storage.Weather memory) {
        return s.w;
    }
    function getIsFarm() external view returns (uint256) {
        return s.isFarm;
    }

    function getUsdTokenPrice() external view returns (uint256) {
        return s.usdTokenPrice[C.BEAN_ETH_WELL];
    }

    function getReserve0() external view returns (uint256) {
        return uint256(s.twaReserves[C.BEAN_ETH_WELL].reserve0);
    }

    function getReserve1() external view returns (uint256) {
        return uint256(s.twaReserves[C.BEAN_ETH_WELL].reserve1);
    }

    function getSoil() external view returns (uint256) {
        return uint256(s.f.soil);
    }

    function getAbovePeg() external view returns (bool) {
        return s.season.abovePeg;
    }

    function getSeasonTimestamp() external view returns (uint256) {
        return s.season.timestamp;
    }

    function wellOracleSnapshot() external view returns (bytes memory) {
        return s.wellOracleSnapshots[C.BEAN_ETH_WELL];
    }

    function mockSopWell() external view returns (address) {
        return s.sopWell;
    }

3. Use the new TestHelper, it includes the necessary changes to run the tests properly.

/**
 * SPDX-License-Identifier: MIT
 **/
pragma solidity =0.7.6;
pragma abicoder v2;


import "forge-std/Test.sol";
import "./Strings.sol";

import {Utils} from "test/foundry/utils/Utils.sol";

// Diamond setup
import {Diamond} from "contracts/beanstalk/Diamond.sol";
import {IDiamondCut} from "contracts/interfaces/IDiamondCut.sol";
import {MockInitDiamond} from "contracts/mocks/MockInitDiamond.sol";

/// Modules
// Diamond
import {DiamondCutFacet} from "contracts/beanstalk/diamond/DiamondCutFacet.sol";
import {DiamondLoupeFacet} from "contracts/beanstalk/diamond/DiamondLoupeFacet.sol";
import {PauseFacet} from "contracts/beanstalk/diamond/PauseFacet.sol";
import {OwnershipFacet} from "contracts/beanstalk/diamond/OwnershipFacet.sol";

// Silo
import {MockSiloFacet} from "contracts/mocks/mockFacets/MockSiloFacet.sol";
import {BDVFacet} from "contracts/beanstalk/silo/BDVFacet.sol";
import {ConvertFacet} from "contracts/beanstalk/silo/ConvertFacet.sol";
import {WhitelistFacet} from "contracts/beanstalk/silo/WhitelistFacet/WhitelistFacet.sol";
import {LiquidityWeightFacet} from "contracts/beanstalk/sun/LiquidityWeightFacet.sol";

// Field
import {MockFieldFacet} from "contracts/mocks/mockFacets/MockFieldFacet.sol";
import {MockFundraiserFacet} from "contracts/mocks/mockFacets/MockFundraiserFacet.sol";

// Sun
import {GaugePointFacet} from "contracts/beanstalk/sun/GaugePointFacet.sol";

// Farm
import {FarmFacet} from "contracts/beanstalk/farm/FarmFacet.sol";
import {CurveFacet} from "contracts/beanstalk/farm/CurveFacet.sol";
import {TokenFacet} from "contracts/beanstalk/farm/TokenFacet.sol";

/// Ecosystem
import {BeanstalkPrice} from "contracts/ecosystem/price/BeanstalkPrice.sol";

/// Mocks
import {MockConvertFacet} from "contracts/mocks/mockFacets/MockConvertFacet.sol";
import {MockMarketplaceFacet} from "contracts/mocks/mockFacets/MockMarketplaceFacet.sol";
import {MockSeasonFacet} from "contracts/mocks/mockFacets/MockSeasonFacet.sol";
import {MockFertilizerFacet} from "contracts/mocks/mockFacets/MockFertilizerFacet.sol";
import {MockToken} from "contracts/mocks/MockToken.sol";
import {MockSetComponentsWell} from "contracts/mocks/well/MockSetComponentsWell.sol";
import {MockUnripeFacet} from "contracts/mocks/mockFacets/MockUnripeFacet.sol";
import {Mock3Curve} from "contracts/mocks/curve/Mock3Curve.sol";
import {MockCurveFactory} from "contracts/mocks/curve/MockCurveFactory.sol";
import {MockCurveZap} from "contracts/mocks/curve/MockCurveZap.sol";
import {MockMeta3Curve} from "contracts/mocks/curve/MockMeta3Curve.sol";
import {MockUniswapV3Pool} from "contracts/mocks/uniswap/MockUniswapV3Pool.sol";
import {MockUniswapV3Factory} from "contracts/mocks/uniswap/MockUniswapV3Factory.sol";
import {MockPump} from "contracts/mocks/well/MockPump.sol";
import {MockWETH} from "contracts/mocks/MockWETH.sol";
import {MockChainlinkAggregator} from "contracts/mocks/chainlink/MockChainlinkAggregator.sol";

import "contracts/beanstalk/AppStorage.sol";
import "contracts/libraries/Decimal.sol";
import "contracts/libraries/LibSafeMath32.sol";
import "contracts/libraries/Token/LibTransfer.sol";

import "contracts/C.sol";
import "contracts/interfaces/basin/IWell.sol";

interface IBDVFacet {
    function wellBdv(address token, uint256 amount)
        external
        view
        returns (uint256);
}

interface IGaugePointFacet {
    function defaultGaugePointFunction(
        uint256 currentGaugePoints,
        uint256 optimalPercentDepositedBdv,
        uint256 percentOfDepositedBdv
    ) external pure returns (uint256 newGaugePoints);
}

interface ILiquidityWeightFacet {
    function maxWeight() external pure returns (uint256);
}

// We deploy every facet, even if a facet is unused
abstract contract TestHelper is Test {
    using strings for *;
    
    Utils internal utils;

    uint32 constant private BEAN_ETH_SEEDS_PER_BDV = 4.5e6;
    uint16 constant private STALK_ISSUED_PER_BDV = 10000;
  
    address payable[] internal users;

    // the cool dudes
    address internal deployer;
    address internal user1;
    address internal user2;
    address internal user3;
    address internal user4;
    address internal user5;
    address internal diamond;

    // season mocks
    MockSeasonFacet internal season;
    MockSiloFacet internal silo;
    MockFieldFacet internal field;
    MockConvertFacet internal convert;
    MockFundraiserFacet internal fundraiser;
    MockMarketplaceFacet internal marketplace;
    MockFertilizerFacet internal fertilizer;
    MockUnripeFacet internal unripe;
    WhitelistFacet internal whitelist;
    MockToken internal unripeLP;
    MockToken internal unripeBean;
    MockWETH internal mockWETH;
    TokenFacet internal token;
    MockSetComponentsWell internal well;
    MockPump internal mockPump;

    // oracle mocks
    MockChainlinkAggregator internal chainlinkAggregator;

    function setupDiamond() public {
        diamond = address(deployMocks());
        season = MockSeasonFacet(diamond);
        silo = MockSiloFacet(diamond);
        field = MockFieldFacet(diamond);
        convert = MockConvertFacet(diamond);
        fundraiser = MockFundraiserFacet(diamond);
        marketplace = MockMarketplaceFacet(diamond);
        fertilizer = MockFertilizerFacet(diamond);
        whitelist = WhitelistFacet(diamond);
        unripe = MockUnripeFacet(diamond);
        token = TokenFacet(diamond);

        _setupWell();

        vm.prank(deployer);
        _whitelistBeanEthWell();
    }

    function deployMocks() public returns (Diamond d) {
        // create accounts
        utils = new Utils();
        users = utils.createUsers(6);
        deployer = users[0];
        user1 = users[1];
        user2 = users[2];
        user3 = users[3];
        user4 = users[4];
        user5 = users[5];

        vm.label(deployer, "Deployer");
        vm.label(user1, "user1");
        vm.label(user2, "user2");
        vm.label(user3, "user3");
        vm.label(user4, "user4");
        vm.label(user5, "user5");

        // create facet cuts
        IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](17);

        cut[0] = _cut("BDVFacet", address(new BDVFacet()));
        cut[1] = _cut("CurveFacet", address(new CurveFacet()));
        cut[2] = _cut("MockConvertFacet", address(new MockConvertFacet()));
        cut[3] = _cut("FarmFacet", address(new FarmFacet()));
        cut[4] = _cut("MockFieldFacet", address(new MockFieldFacet()));
        cut[5] = _cut("MockFundraiserFacet", address(new MockFundraiserFacet()));
        cut[6] = _cut("PauseFacet", address(new PauseFacet()));
        cut[7] = _cut("MockSeasonFacet", address(new MockSeasonFacet()));
        cut[8] = _cut("MockSiloFacet", address(new MockSiloFacet()));
        cut[9] = _cut("MockFertilizerFacet", address(new MockFertilizerFacet()));
        cut[10] = _cut("OwnershipFacet", address(new OwnershipFacet()));
        cut[11] = _cut("TokenFacet", address(new TokenFacet()));
        cut[12] = _cut("MockUnripeFacet", address(new MockUnripeFacet()));
        cut[13] = _cut("WhitelistFacet", address(new WhitelistFacet()));
        cut[14] = _cut("MockMarketplaceFacet", address(new MockMarketplaceFacet()));
        cut[15] = _cut("GaugePointFacet", address(new GaugePointFacet()));
        cut[16] = _cut("LiquidityWeightFacet", address(new LiquidityWeightFacet()));
        console.log("Deployed mock facets.");
        deployMockTokens();
        // create diamond    
        d = new Diamond(deployer);
        MockInitDiamond i = new MockInitDiamond();

        vm.prank(deployer);
        IDiamondCut(address(d)).diamondCut(
        cut,
        address(i), // address of contract with init() function
        abi.encodeWithSignature("init()")
        );

        console.log("Initialized diamond at", address(d));
    }

    function deployMockTokens() public {
        // impersonate tokens and utilities
        _mockToken("Bean", address(C.bean()));
        MockToken(address(C.bean())).setDecimals(6);
        _mockToken("USDC", address(C.usdc()));
        _mockPrice();
        _mockCurve(); // only if "reset"
        _mockUniswap();
        _mockChainlinkAggregator();
        _mockUnripe();
        _mockWeth(); // only if "reset"
    }

    function _whitelistBeanEthWell() internal { 
        bytes4 gpSelector = IGaugePointFacet.defaultGaugePointFunction.selector;
        bytes4 lwSelector = ILiquidityWeightFacet.maxWeight.selector;
        
        whitelist.whitelistTokenWithEncodeType(
            C.BEAN_ETH_WELL,
            IBDVFacet.wellBdv.selector,
            STALK_ISSUED_PER_BDV,
            BEAN_ETH_SEEDS_PER_BDV,
            0x01,
            gpSelector,
            lwSelector,
            100e18,
            100e6
        );
    }

    function _setupWell() internal {
        // deploy well
        well = MockSetComponentsWell(_etch("MockSetComponentsWell.sol", C.BEAN_ETH_WELL, abi.encode("")));
        vm.label(C.BEAN_ETH_WELL, "BeanEthWell");
        well.init();

        // deploy pump
        mockPump =  MockPump(_etch("MockPump.sol", C.BEANSTALK_PUMP, abi.encode(
            bytes16(0x3ffe0000000000000000000000000000), // 0.5
            bytes16(0x3ffd555555555555553cbcd83d925070), // 0.333333333333333333
            12,
            bytes16(0x3ffecccccccccccccccccccccccccccc) // 0.9
        )));
        vm.label(C.BEANSTALK_PUMP, "Pump");

        // set pumps
        Call[] memory pumps = new Call[](1);
        pumps[0] = Call({ target: C.BEANSTALK_PUMP, data: "0x" });
        well.setPumps(pumps);

        // well function
        address beanstalkWellFunctionAddress = deployCode("../node_modules/@beanstalk/wells/out/ConstantProduct2.sol:ConstantProduct2");
        Call memory wellFunctionParameters = Call({ target: beanstalkWellFunctionAddress, data: "0x" });
        well.setWellFunction(wellFunctionParameters);

        // set tokens
        IERC20[] memory tokens = new IERC20[](2);
        tokens[0] = IERC20(C.BEAN);
        tokens[1] = IERC20(C.WETH);
        well.setTokens(tokens);

        _setReservesForWell(1000000e6, 1000e18);
        _setInstantaneousReservesForPump(1e18, 1e18);

        well.setSymbol("MOCK");
      //  _etch("IWell.sol", C.BEAN_ETH_WELL, abi.encode("MOCK", address(well)));
        
        console.log("well function address", beanstalkWellFunctionAddress);
        console.log("well address", address(well));
    }

    function _setReservesForWell(uint256 _beanReserve, uint256 _wethReserve) internal {
        uint256[] memory reserves = new uint256[](2);
        reserves[0] = _beanReserve;
        reserves[1] = _wethReserve;
        well.setReserves(reserves);
    }

    function _setInstantaneousReservesForPump(uint256 balance1, uint256 balance2) internal {
        uint256[] memory balances = new uint256[](2);
        balances[0] = balance1;
        balances[1] = balance2;
        mockPump.setInstantaneousReserves(balances);
    }

    function _mockChainlinkAggregator() internal { 
        address chainlinkAggregatorAddress = address(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
        chainlinkAggregator =  MockChainlinkAggregator(_etch("MockChainlinkAggregator.sol", chainlinkAggregatorAddress, abi.encode("")));
        chainlinkAggregator.setDecimals(6);
    }

    ///////////////////////// Utilities /////////////////////////

    function _abs(int256 v) pure internal returns (uint256) {
        return uint256(v < 0 ? 0 : v);
    }

    function _reset(uint256 _snapId) internal returns (uint256) {
        vm.revertTo(_snapId);
        return vm.snapshot();
    }

    function _advanceInTime(uint256 _seconds) internal { 
        //vm.roll(block.number + 1);
        vm.warp(block.timestamp + _seconds);
    }

    //////////////////////// Deploy  /////////////////////////


    function _etch(string memory _file, address _address, bytes memory args) internal returns (address) {
        address codeaddress = deployCode(_file, args);
        vm.etch(_address, at(codeaddress));
        return _address;
    }

    function _mockToken(string memory _tokenName, address _tokenAddress) internal returns (MockToken) {
        MockToken mockToken = MockToken(_etch("MockToken.sol", _tokenAddress,abi.encode(_tokenName,"")));
        vm.label(address(mockToken), _tokenName);   
        return mockToken;
    }

    function _mockWeth() internal returns (MockWETH) {
        address payable weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
        vm.label(weth, "WETH");
        mockWETH = MockWETH(payable(_etch("MockWETH.sol", weth, abi.encode("Wrapped Ether","WETH"))));
        return mockWETH;
    }

    function _mockPrice() internal returns (BeanstalkPrice p) {
        address PRICE_DEPLOYER = 0x884B463E078Ff26C4b83792dB9bEF33619a69767;
        vm.prank(PRICE_DEPLOYER);
        p = new BeanstalkPrice();
    }

    function _mockCurve() internal {
        address THREE_CRV = address(C.threeCrv());

        MockToken crv3 = _mockToken("3CRV", THREE_CRV);
        MockToken(crv3).setDecimals(18);
        //
        Mock3Curve pool3 = Mock3Curve(_etch("Mock3Curve.sol", C.curve3PoolAddress(), abi.encode(""))); // 3Curve = 3Pool
        Mock3Curve(pool3).set_virtual_price(1);

        //
        address STABLE_FACTORY = 0xB9fC157394Af804a3578134A6585C0dc9cc990d4;
        MockCurveFactory stableFactory = MockCurveFactory(_etch("MockCurveFactory.sol", STABLE_FACTORY, abi.encode("")));


        // address CRYPTO_REGISTRY = 0x8F942C20D02bEfc377D41445793068908E2250D0;
        address CURVE_REGISTRY = 0x90E00ACe148ca3b23Ac1bC8C240C2a7Dd9c2d7f5;
        _etch("MockToken.sol", CURVE_REGISTRY, abi.encode("")); // why this interface?
        stableFactory.set_coins(C.CURVE_BEAN_METAPOOL, [
            C.BEAN,
            THREE_CRV,
            address(0),
            address(0)
        ]);
        //
        MockCurveZap curveZap = MockCurveZap(_etch("MockCurveZap.sol", C.curveZapAddress(), abi.encode("")));
        curveZap.approve();
    }

    function _mockUniswap() internal {
        //address UNIV3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984; 
        MockUniswapV3Factory uniFactory = MockUniswapV3Factory(new MockUniswapV3Factory());

        // USDC
        address ethUsdc = 
            uniFactory.createPool(
            0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,//weth
            address(C.usdc()),//usdc
            3000
            );
        bytes memory code = at(ethUsdc);
        address targetAddr = C.UNIV3_ETH_USDC_POOL;
        vm.etch(targetAddr, code);
        MockUniswapV3Pool(C.UNIV3_ETH_USDC_POOL).setOraclePrice(1000e6,18);

        // USDT
        address ethUsdt = 
            uniFactory.createPool(
            0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2,//weth
            address(C.USDT),//usdt
            3000
            );
        code = at(ethUsdt);
        targetAddr = C.UNIV3_ETH_USDT_POOL;
        vm.etch(targetAddr, code);
        MockUniswapV3Pool(C.UNIV3_ETH_USDT_POOL).setOraclePrice(1000e6,18);
    }

    function _setEthUsdcPrice(uint256 _price) internal {
        MockUniswapV3Pool(C.UNIV3_ETH_USDC_POOL).setOraclePrice(_price,uint8(18));
    }

    function _setEthUsdtPrice(uint256 _price) internal {
        MockUniswapV3Pool(C.UNIV3_ETH_USDT_POOL).setOraclePrice(_price,uint8(18));
    }

    function _addEthUsdPriceChainlink(int256 _price) internal {
        chainlinkAggregator.addRound(_price, block.timestamp, block.timestamp, 1);
    }

     function _setEthUsdPriceChainlink(uint80 _roundId, int256 _price) internal {
        chainlinkAggregator.setRound(_roundId, _price, block.timestamp, block.timestamp, 1);
    }

    function _mockCurveMetapool() internal {
        address THREE_CRV = address(C.threeCrv());
        MockMeta3Curve p = MockMeta3Curve(_etch("MockMeta3Curve.sol", C.CURVE_BEAN_METAPOOL, abi.encode("")));
        p.init(C.BEAN, THREE_CRV, C.curve3PoolAddress());
        p.set_A_precise(1000);
        p.set_virtual_price(1 wei);
    }

    function _mockUnripe() internal {
        unripeBean = _mockToken("UNRIPE_BEAN", C.UNRIPE_BEAN);
        unripeBean.setDecimals(6);
        unripeLP = _mockToken("UNRIPE_BEAN:3CRV", C.UNRIPE_LP);
    }

    function _printAddresses() internal view {
        console.log("C: Bean = %s", address(C.bean()));
    }

    function _cut(string memory _facetName, address _facetAddress)
        internal
        returns (IDiamondCut.FacetCut memory cut) 
    {
        bytes4[] memory functionSelectors = generateSelectors(_facetName);
        cut = IDiamondCut.FacetCut({
            facetAddress: _facetAddress,
            action: IDiamondCut.FacetCutAction.Add,
            functionSelectors: functionSelectors
        });

    }

     function generateSelectors(string memory _facetName) internal returns (bytes4[] memory selectors) {
        //get string of contract methods
        string[] memory cmd = new string[](4);
        cmd[0] = "forge";
        cmd[1] = "inspect";
        cmd[2] = _facetName;
        cmd[3] = "methods";
        bytes memory res = vm.ffi(cmd);
        string memory st = string(res);

        // extract function signatures and take first 4 bytes of keccak
        strings.slice memory s = st.toSlice();

        // Skip TRACE lines if any
        strings.slice memory nl = "\n".toSlice();
        strings.slice memory trace = "TRACE".toSlice();
        while (s.contains(trace)) {
        s.split(nl);
        }

        strings.slice memory colon = ":".toSlice();
        strings.slice memory comma = ",".toSlice();
        strings.slice memory dbquote = '"'.toSlice();
        selectors = new bytes4[]((s.count(colon)));

        for (uint i = 0; i < selectors.length; i++) {
        s.split(dbquote); // advance to next doublequote
        // split at colon, extract string up to next doublequote for methodname
        strings.slice memory method = s.split(colon).until(dbquote);
        selectors[i] = bytes4(method.keccak());
        strings.slice memory selectr = s.split(comma).until(dbquote); // advance s to the next comma
        }
        return selectors;
    }

    function _generateSelectors(string memory _facetName)
        internal
        returns (bytes4[] memory selectors)
    {
        string[] memory cmd = new string[](3);
        cmd[0] = "node";
        cmd[1] = "scripts/genSelectors.js";
        cmd[2] = _facetName;
        bytes memory res = vm.ffi(cmd);
        selectors = abi.decode(res, (bytes4[]));
    }

    //gets bytecode at specific address (cant use address.code as we're in 0.7.6)
    function at(address _addr) public view returns (bytes memory o_code) {
        assembly {
            // retrieve the size of the code
            let size := extcodesize(_addr)
            // allocate output byte array
            // by using o_code = new bytes(size)
            o_code := mload(0x40)
            // new "memory end" including padding
            mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
            // store length in memory
            mstore(o_code, size)
            // actually retrieve the code, this needs assembly
            extcodecopy(_addr, add(o_code, 0x20), 0, size)
        }
    }



    // function initUser() internal {
    //     users = new Users();
    //     address[] memory _user = new address[](2);
    //     _user = users.createUsers(2);
    //     user = _user[0];
    //     user2 = _user[1];
    // }

    // /// @dev deploy `n` mock ERC20 tokens and sort by address
    // function deployMockTokens(uint n) internal {
    //     IERC20[] memory _tokens = new IERC20[](n);
    //     for (uint i = 0; i < n; i++) {
    //         IERC20 temp = IERC20(
    //             new MockToken(
    //                 string.concat("Token ", i.toString()), // name
    //                 string.concat("TOKEN", i.toString()), // symbol
    //                 18 // decimals
    //             )
    //         );
    //         // Insertion sort
    //         uint j;
    //         if (i > 0) {
    //             for (j = i; j >= 1 && temp < _tokens[j - 1]; j--)
    //                 _tokens[j] = _tokens[j - 1];
    //             _tokens[j] = temp;
    //         } else _tokens[0] = temp;
    //     }
    //     for (uint i = 0; i < n; i++) tokens.push(_tokens[i]);
    // }

    // /// @dev mint mock tokens to each recipient
    // function mintTokens(address recipient, uint amount) internal {
    //     for (uint i = 0; i < tokens.length; i++)
    //         MockToken(address(tokens[i])).mint(recipient, amount);
    // }

    // /// @dev approve `spender` to use `owner` tokens
    // function approveMaxTokens(address owner, address spender) prank(owner) internal {
    //     for (uint i = 0; i < tokens.length; i++)
    //         tokens[i].approve(spender, type(uint).max);
    // }

    // /// @dev add the same `amount` of liquidity for all underlying tokens
    // function addLiquidityEqualAmount(address from, uint amount) prank(from) internal {
    //     uint[] memory amounts = new uint[](tokens.length);
    //     for (uint i = 0; i < tokens.length; i++) amounts[i] = amount;
    //     well.addLiquidity(amounts, 0, from);
    // }

    // /// @dev gets the first `n` mock tokens
    // function getTokens(uint n)
    //     internal
    //     view
    //     returns (IERC20[] memory _tokens)
    // {
    //     _tokens = new IERC20[](n);
    //     for (uint i; i < n; ++i) {
    //         _tokens[i] = tokens[i];
    //     }
    // }

    // /// @dev get `account` balance of each token, lp token, total lp token supply
    // function getBalances(address account) internal view returns (Balances memory balances) {
    //     uint[] memory tokenBalances = new uint[](tokens.length);
    //     for (uint i = 0; i < tokenBalances.length; ++i) {
    //         tokenBalances[i] = tokens[i].balanceOf(account);
    //     }
    //     balances = Balances(
    //         tokenBalances,
    //         well.balanceOf(account),
    //         well.totalSupply()
    //     );
    // }

    /// @dev impersonate `from`
    modifier prank(address from) {
        vm.startPrank(from);
        _;
        vm.stopPrank();
    }

    modifier resetSnapshot() { 
        uint256 _snapId = vm.snapshot();
        _;
        vm.revertTo(_snapId);
    }
}

Setup the Environment

1. Run anvil to fork the mainnet locally

anvil --fork-url https://rpc.ankr.com/eth

Success - now you should be able to run the PoCs included in @holydevoti0n findings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment