text: const hre = require("hardhat");
  
  const addresses = require("../utils/addresses");
  const fundAccounts = require("../utils/funding");
  const { getAssetAddresses, daiUnits, isFork } = require("./helpers");
  const { utils } = require("ethers");
  
  const { airDropPayouts } = require("../scripts/staking/airDrop.js");
  const testPayouts = require("../scripts/staking/rawAccountsToBeCompensated.json");
  const { loadFixture, getOracleAddresses } = require("./helpers");
  
  const daiAbi = require("./abi/dai.json").abi;
  const usdtAbi = require("./abi/usdt.json").abi;
  const tusdAbi = require("./abi/erc20.json");
  const usdcAbi = require("./abi/erc20.json");
  const compAbi = require("./abi/erc20.json");
  const crvAbi = require("./abi/erc20.json");
  const ognAbi = require("./abi/erc20.json");
  const crvMinterAbi = require("./abi/crvMinter.json");
  
  async function defaultFixture() {
    await deployments.fixture();
  
    const { governorAddr } = await getNamedAccounts();
  
    const ousdProxy = await ethers.getContract("OUSDProxy");
    const vaultProxy = await ethers.getContract("VaultProxy");
    const compoundStrategyProxy = await ethers.getContract(
      "CompoundStrategyProxy"
    );
  
    const ousd = await ethers.getContractAt("OUSD", ousdProxy.address);
    const vault = await ethers.getContractAt("IVault", vaultProxy.address);
    const governorContract = await ethers.getContract("Governor");
    const CompoundStrategyFactory = await ethers.getContractFactory(
      "CompoundStrategy"
    );
    const compoundStrategy = await ethers.getContractAt(
      "CompoundStrategy",
      compoundStrategyProxy.address
    );
  
    const threePoolStrategyProxy = await ethers.getContract(
      "ThreePoolStrategyProxy"
    );
    const threePoolStrategy = await ethers.getContractAt(
      "ThreePoolStrategy",
      threePoolStrategyProxy.address
    );
    const convexStrategyProxy = await ethers.getContract("ConvexStrategyProxy");
    const convexStrategy = await ethers.getContractAt(
      "ConvexStrategy",
      convexStrategyProxy.address
    );
  
    const aaveStrategyProxy = await ethers.getContract("AaveStrategyProxy");
    const aaveStrategy = await ethers.getContractAt(
      "AaveStrategy",
      aaveStrategyProxy.address
    );
    const aaveIncentivesController = await ethers.getContract(
      "MockAaveIncentivesController"
    );
  
    const liquidityRewardOUSD_USDT = await ethers.getContractAt(
      "LiquidityReward",
      (
        await ethers.getContract("LiquidityRewardOUSD_USDTProxy")
      ).address
    );
  
    const ognStaking = await ethers.getContractAt(
      "SingleAssetStaking",
      (
        await ethers.getContract("OGNStakingProxy")
      ).address
    );
  
    const oracleRouter = await ethers.getContract("OracleRouter");
  
    const testPayoutsModified = {
      ...testPayouts,
      payouts: testPayouts.payouts.map((each) => {
        return { address: each[0], ogn_compensation: each[1] };
      }),
    };
  
    const signedPayouts = await airDropPayouts(
      ognStaking.address,
      testPayoutsModified
    );
  
    const compensationClaims = await ethers.getContract("CompensationClaims");
  
    const buyback = await ethers.getContract("Buyback");
  
    let usdt,
      dai,
      tusd,
      usdc,
      ogn,
      nonStandardToken,
      cusdt,
      cdai,
      cusdc,
      comp,
      adai,
      aave,
      aaveToken,
      stkAave,
      mockNonRebasing,
      mockNonRebasingTwo;
  
    let chainlinkOracleFeedDAI,
      chainlinkOracleFeedUSDT,
      chainlinkOracleFeedUSDC,
      chainlinkOracleFeedOGNETH,
      chainlinkOracleFeedETH,
      crv,
      crvMinter,
      threePool,
      threePoolToken,
      threePoolGauge,
      aaveAddressProvider,
      uniswapPairOUSD_USDT,
      flipper,
      cvx,
      cvxBooster,
      cvxRewardPool;
  
    if (isFork) {
      usdt = await ethers.getContractAt(usdtAbi, addresses.mainnet.USDT);
      dai = await ethers.getContractAt(daiAbi, addresses.mainnet.DAI);
      tusd = await ethers.getContractAt(tusdAbi, addresses.mainnet.TUSD);
      usdc = await ethers.getContractAt(usdcAbi, addresses.mainnet.USDC);
      comp = await ethers.getContractAt(compAbi, addresses.mainnet.COMP);
      crv = await ethers.getContractAt(crvAbi, addresses.mainnet.CRV);
      cvx = await ethers.getContractAt(crvAbi, addresses.mainnet.CVX);
      ogn = await ethers.getContractAt(ognAbi, addresses.mainnet.OGN);
      crvMinter = await ethers.getContractAt(
        crvMinterAbi,
        addresses.mainnet.CRVMinter
      );
      aaveAddressProvider = await ethers.getContractAt(
        "ILendingPoolAddressesProvider",
        addresses.mainnet.AAVE_ADDRESS_PROVIDER
      );
    } else {
      usdt = await ethers.getContract("MockUSDT");
      dai = await ethers.getContract("MockDAI");
      tusd = await ethers.getContract("MockTUSD");
      usdc = await ethers.getContract("MockUSDC");
      ogn = await ethers.getContract("MockOGN");
      nonStandardToken = await ethers.getContract("MockNonStandardToken");
  
      cdai = await ethers.getContract("MockCDAI");
      cusdt = await ethers.getContract("MockCUSDT");
      cusdc = await ethers.getContract("MockCUSDC");
      comp = await ethers.getContract("MockCOMP");
  
      crv = await ethers.getContract("MockCRV");
      cvx = await ethers.getContract("MockCVX");
      crvMinter = await ethers.getContract("MockCRVMinter");
      threePool = await ethers.getContract("MockCurvePool");
      threePoolToken = await ethers.getContract("Mock3CRV");
      threePoolGauge = await ethers.getContract("MockCurveGauge");
      cvxBooster = await ethers.getContract("MockBooster");
      cvxRewardPool = await ethers.getContract("MockRewardPool");
  
      adai = await ethers.getContract("MockADAI");
      aaveToken = await ethers.getContract("MockAAVEToken");
      aave = await ethers.getContract("MockAave");
      // currently in test the mockAave is itself the address provder
      aaveAddressProvider = await ethers.getContractAt(
        "ILendingPoolAddressesProvider",
        aave.address
      );
      stkAave = await ethers.getContract("MockStkAave");
  
      uniswapPairOUSD_USDT = await ethers.getContract("MockUniswapPairOUSD_USDT");
  
      chainlinkOracleFeedDAI = await ethers.getContract(
        "MockChainlinkOracleFeedDAI"
      );
      chainlinkOracleFeedUSDT = await ethers.getContract(
        "MockChainlinkOracleFeedUSDT"
      );
      chainlinkOracleFeedUSDC = await ethers.getContract(
        "MockChainlinkOracleFeedUSDC"
      );
      chainlinkOracleFeedOGNETH = await ethers.getContract(
        "MockChainlinkOracleFeedOGNETH"
      );
      chainlinkOracleFeedETH = await ethers.getContract(
        "MockChainlinkOracleFeedETH"
      );
  
      // Mock contracts for testing rebase opt out
      mockNonRebasing = await ethers.getContract("MockNonRebasing");
      await mockNonRebasing.setOUSD(ousd.address);
      mockNonRebasingTwo = await ethers.getContract("MockNonRebasingTwo");
      await mockNonRebasingTwo.setOUSD(ousd.address);
  
      flipper = await ethers.getContract("Flipper");
    }
    const assetAddresses = await getAssetAddresses(deployments);
  
    const sGovernor = await ethers.provider.getSigner(governorAddr);
  
    // Add TUSD in fixture, it is disabled by default in deployment
    await vault.connect(sGovernor).supportAsset(assetAddresses.TUSD);
  
    // Enable capital movement
    await vault.connect(sGovernor).unpauseCapital();
  
    // Add Buyback contract as trustee
    await vault.connect(sGovernor).setTrusteeAddress(buyback.address);
  
    const signers = await hre.ethers.getSigners();
    const governor = signers[1];
    const strategist = signers[0];
    const adjuster = signers[0];
    const matt = signers[4];
    const josh = signers[5];
    const anna = signers[6];
  
    await fundAccounts();
  
    // Matt and Josh each have $100 OUSD
    for (const user of [matt, josh]) {
      await dai.connect(user).approve(vault.address, daiUnits("100"));
      await vault.connect(user).mint(dai.address, daiUnits("100"), 0);
    }
  
    return {
      // Accounts
      matt,
      josh,
      anna,
      governor,
      strategist,
      adjuster,
      // Contracts
      ousd,
      vault,
      mockNonRebasing,
      mockNonRebasingTwo,
      // Oracle
      chainlinkOracleFeedDAI,
      chainlinkOracleFeedUSDT,
      chainlinkOracleFeedUSDC,
      chainlinkOracleFeedOGNETH,
      chainlinkOracleFeedETH,
      governorContract,
      compoundStrategy,
      oracleRouter,
      // Assets
      usdt,
      dai,
      tusd,
      usdc,
      ogn,
      nonStandardToken,
      // cTokens
      cdai,
      cusdc,
      cusdt,
      comp,
      // aTokens,
      adai,
      // CompoundStrategy contract factory to deploy
      CompoundStrategyFactory,
      // ThreePool
      crv,
      crvMinter,
      threePool,
      threePoolGauge,
      threePoolToken,
      threePoolStrategy,
      convexStrategy,
      cvx,
      cvxBooster,
      cvxRewardPool,
  
      aaveStrategy,
      aaveToken,
      aaveAddressProvider,
      aaveIncentivesController,
      aave,
      stkAave,
      uniswapPairOUSD_USDT,
      liquidityRewardOUSD_USDT,
      ognStaking,
      signedPayouts,
      compensationClaims,
      flipper,
      buyback,
    };
  }
  
  /**
   * Configure the MockVault contract by initializing it and setting supported
   * assets and then upgrade the Vault implementation via VaultProxy.
   */
  async function mockVaultFixture() {
    const fixture = await loadFixture(defaultFixture);
  
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = ethers.provider.getSigner(governorAddr);
  
    // Initialize and configure MockVault
    const cMockVault = await ethers.getContract("MockVault");
  
    // There is no need to initialize and setup the mock vault because the
    // proxy itself is already setup and the proxy is the one with the storage
  
    // Upgrade Vault to MockVault via proxy
    const cVaultProxy = await ethers.getContract("VaultProxy");
    await cVaultProxy.connect(sGovernor).upgradeTo(cMockVault.address);
  
    fixture.mockVault = await ethers.getContractAt(
      "MockVault",
      cVaultProxy.address
    );
  
    return fixture;
  }
  
  /**
   * Configure a Vault with only the Compound strategy.
   */
  async function compoundVaultFixture() {
    const fixture = await loadFixture(defaultFixture);
  
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = await ethers.provider.getSigner(governorAddr);
  
    const assetAddresses = await getAssetAddresses(deployments);
  
    // Approve in Vault
    await fixture.vault
      .connect(sGovernor)
      .approveStrategy(fixture.compoundStrategy.address);
    // Add USDT
    await fixture.compoundStrategy
      .connect(sGovernor)
      .setPTokenAddress(assetAddresses.USDT, assetAddresses.cUSDT);
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(
        fixture.usdt.address,
        fixture.compoundStrategy.address
      );
    // Add USDC
    await fixture.compoundStrategy
      .connect(sGovernor)
      .setPTokenAddress(assetAddresses.USDC, assetAddresses.cUSDC);
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(
        fixture.usdc.address,
        fixture.compoundStrategy.address
      );
    // Add allocation mapping for DAI
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(
        fixture.dai.address,
        fixture.compoundStrategy.address
      );
  
    return fixture;
  }
  
  /**
   * Configure a Vault with only the 3Pool strategy.
   */
  async function threepoolVaultFixture() {
    const fixture = await loadFixture(defaultFixture);
  
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = await ethers.provider.getSigner(governorAddr);
    // Add 3Pool
    await fixture.vault
      .connect(sGovernor)
      .approveStrategy(fixture.threePoolStrategy.address);
  
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(
        fixture.usdt.address,
        fixture.threePoolStrategy.address
      );
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(
        fixture.usdc.address,
        fixture.threePoolStrategy.address
      );
    return fixture;
  }
  
  /**
   * Configure a Vault with only the Convex strategy.
   */
  async function convexVaultFixture() {
    const fixture = await loadFixture(defaultFixture);
  
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = await ethers.provider.getSigner(governorAddr);
    // Add 3Pool
    await fixture.vault
      .connect(sGovernor)
      .approveStrategy(fixture.convexStrategy.address);
  
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(
        fixture.usdt.address,
        fixture.convexStrategy.address
      );
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(
        fixture.usdc.address,
        fixture.convexStrategy.address
      );
    return fixture;
  }
  
  /**
   * Configure a Vault with only the Aave strategy.
   */
  async function aaveVaultFixture() {
    const fixture = await loadFixture(defaultFixture);
  
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = await ethers.provider.getSigner(governorAddr);
    // Add Aave which only supports DAI
    await fixture.vault
      .connect(sGovernor)
      .approveStrategy(fixture.aaveStrategy.address);
    // Add direct allocation of DAI to Aave
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(fixture.dai.address, fixture.aaveStrategy.address);
    return fixture;
  }
  
  /**
   * Configure a compound fixture with a false valt for testing
   */
  async function compoundFixture() {
    const fixture = await loadFixture(defaultFixture);
    const assetAddresses = await getAssetAddresses(deployments);
    const { deploy } = deployments;
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = await ethers.provider.getSigner(governorAddr);
  
    await deploy("StandaloneCompound", {
      from: governorAddr,
      contract: "CompoundStrategy",
    });
  
    fixture.cStandalone = await ethers.getContract("StandaloneCompound");
  
    // Set governor as vault
    await fixture.cStandalone.connect(sGovernor).initialize(
      addresses.dead,
      governorAddr, // Using Governor in place of Vault here
      assetAddresses.COMP,
      [assetAddresses.DAI, assetAddresses.USDC],
      [assetAddresses.cDAI, assetAddresses.cUSDC]
    );
  
    await fixture.usdc.transfer(
      await fixture.matt.getAddress(),
      utils.parseUnits("1000", 6)
    );
  
    return fixture;
  }
  
  /**
   * Configure a threepool fixture with the governer as vault for testing
   */
  async function threepoolFixture() {
    const fixture = await loadFixture(defaultFixture);
    const assetAddresses = await getAssetAddresses(deployments);
    const { deploy } = deployments;
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = await ethers.provider.getSigner(governorAddr);
  
    await deploy("StandaloneThreePool", {
      from: governorAddr,
      contract: "ThreePoolStrategy",
    });
  
    fixture.tpStandalone = await ethers.getContract("StandaloneThreePool");
  
    // Set governor as vault
    await fixture.tpStandalone.connect(sGovernor)[
      // eslint-disable-next-line
      "initialize(address,address,address,address[],address[],address,address)"
    ](
      assetAddresses.ThreePool,
      governorAddr, // Using Governor in place of Vault here
      assetAddresses.CRV,
      [assetAddresses.DAI, assetAddresses.USDC, assetAddresses.USDT],
      [
        assetAddresses.ThreePoolToken,
        assetAddresses.ThreePoolToken,
        assetAddresses.ThreePoolToken,
      ],
      assetAddresses.ThreePoolGauge,
      assetAddresses.CRVMinter
    );
  
    return fixture;
  }
  
  /**
   * Configure a Vault with two strategies
   */
  async function multiStrategyVaultFixture() {
    const fixture = await compoundVaultFixture();
    const assetAddresses = await getAssetAddresses(deployments);
    const { deploy } = deployments;
  
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = await ethers.provider.getSigner(governorAddr);
  
    await deploy("StrategyTwo", {
      from: governorAddr,
      contract: "CompoundStrategy",
    });
  
    const cStrategyTwo = await ethers.getContract("StrategyTwo");
    // Initialize the second strategy with DAI and USDC
    await cStrategyTwo
      .connect(sGovernor)
      .initialize(
        addresses.dead,
        fixture.vault.address,
        assetAddresses.COMP,
        [assetAddresses.DAI, assetAddresses.USDC],
        [assetAddresses.cDAI, assetAddresses.cUSDC]
      );
    // Add second strategy to Vault
    await fixture.vault.connect(sGovernor).approveStrategy(cStrategyTwo.address);
    // DAI to second strategy
    await fixture.vault
      .connect(sGovernor)
      .setAssetDefaultStrategy(fixture.dai.address, cStrategyTwo.address);
  
    // Set up third strategy
    await deploy("StrategyThree", {
      from: governorAddr,
      contract: "CompoundStrategy",
    });
    const cStrategyThree = await ethers.getContract("StrategyThree");
    // Initialize the third strategy with only DAI
    await cStrategyThree
      .connect(sGovernor)
      .initialize(
        addresses.dead,
        fixture.vault.address,
        assetAddresses.COMP,
        [assetAddresses.DAI],
        [assetAddresses.cDAI]
      );
  
    fixture.strategyTwo = cStrategyTwo;
    fixture.strategyThree = cStrategyThree;
    return fixture;
  }
  
  /**
   * Configure a hacked Vault
   */
  async function hackedVaultFixture() {
    const fixture = await loadFixture(defaultFixture);
    const assetAddresses = await getAssetAddresses(deployments);
    const { deploy } = deployments;
    const { vault, oracleRouter } = fixture;
    const { governorAddr } = await getNamedAccounts();
    const sGovernor = await ethers.provider.getSigner(governorAddr);
    const oracleAddresses = await getOracleAddresses(hre.deployments);
  
    await deploy("MockEvilDAI", {
      from: governorAddr,
      args: [vault.address, assetAddresses.DAI],
    });
  
    const evilDAI = await ethers.getContract("MockEvilDAI");
  
    await oracleRouter.setFeed(
      evilDAI.address,
      oracleAddresses.chainlink.DAI_USD
    );
    await fixture.vault.connect(sGovernor).supportAsset(evilDAI.address);
  
    fixture.evilDAI = evilDAI;
  
    return fixture;
  }
  
  /**
   * Configure a reborn hack attack
   */
  async function rebornFixture() {
    const fixture = await loadFixture(defaultFixture);
    const assetAddresses = await getAssetAddresses(deployments);
    const { deploy } = deployments;
    const { governorAddr } = await getNamedAccounts();
    const { vault } = fixture;
  
    await deploy("Sanctum", {
      from: governorAddr,
      args: [assetAddresses.DAI, vault.address],
    });
  
    const sanctum = await ethers.getContract("Sanctum");
  
    const encodedCallbackAddress = utils.defaultAbiCoder
      .encode(["address"], [sanctum.address])
      .slice(2);
    const initCode = (await ethers.getContractFactory("Reborner")).bytecode;
    const deployCode = `${initCode}${encodedCallbackAddress}`;
  
    await sanctum.deploy(12345, deployCode);
    const rebornAddress = await sanctum.computeAddress(12345, deployCode);
    const reborner = await ethers.getContractAt("Reborner", rebornAddress);
  
    const rebornAttack = async (shouldAttack = true, targetMethod = null) => {
      await sanctum.setShouldAttack(shouldAttack);
      if (targetMethod) await sanctum.setTargetMethod(targetMethod);
      await sanctum.setOUSDAddress(fixture.ousd.address);
      await sanctum.deploy(12345, deployCode);
    };
  
    fixture.reborner = reborner;
    fixture.rebornAttack = rebornAttack;
  
    return fixture;
  }
  
  module.exports = {
    defaultFixture,
    mockVaultFixture,
    compoundFixture,
    compoundVaultFixture,
    multiStrategyVaultFixture,
    threepoolFixture,
    threepoolVaultFixture,
    convexVaultFixture,
    aaveVaultFixture,
    hackedVaultFixture,
    rebornFixture,
  };
  