mirror of
				https://github.com/Instadapp/dsa-connectors.git
				synced 2024-07-29 22:37:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			344 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			344 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import hre from "hardhat";
 | 
						|
import { ethers } from "hardhat";
 | 
						|
import hardhatConfig from "../../../hardhat.config";
 | 
						|
 | 
						|
// Instadapp deployment and testing helpers
 | 
						|
import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"
 | 
						|
import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"
 | 
						|
import { encodeSpells } from "../../../scripts/tests/encodeSpells"
 | 
						|
import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"
 | 
						|
 | 
						|
// Instadapp addresses/ABIs
 | 
						|
import { addresses } from "../../../scripts/tests/mainnet/addresses";
 | 
						|
import { abis } from "../../../scripts/constant/abis";
 | 
						|
 | 
						|
// Instadapp Liquity Connector artifacts
 | 
						|
import { ConnectV2Liquity__factory, ConnectV2Basic__factory } from "../../../typechain";
 | 
						|
 | 
						|
// Instadapp uses a fake address to represent native ETH
 | 
						|
import { constants } from "../../../scripts/constant/constant";
 | 
						|
import type { Signer, Contract, BigNumber } from "ethers";
 | 
						|
const LIQUITY_CONNECTOR = "LIQUITY-v1-TEST";
 | 
						|
const LUSD_GAS_COMPENSATION = hre.ethers.utils.parseUnits("200", 18); // 200 LUSD gas compensation repaid after loan repayment
 | 
						|
const LIQUIDATABLE_TROVES_BLOCK_NUMBER = 12478159; // Deterministic block number for tests to run against, if you change this, tests will break.
 | 
						|
const JUSTIN_SUN_ADDRESS = "0x903d12bf2c57a29f32365917c706ce0e1a84cce3"; // LQTY whale address
 | 
						|
const LIQUIDATABLE_TROVE_ADDRESS = "0xafbeb4cb97f3b08ec2fe07ef0dac15d37013a347"; // Trove which is liquidatable at blockNumber: LIQUIDATABLE_TROVES_BLOCK_NUMBER
 | 
						|
// @ts-ignore
 | 
						|
const MAX_GAS = hardhatConfig.networks.hardhat.blockGasLimit ?? 12000000; // Maximum gas limit (12000000)
 | 
						|
const INSTADAPP_BASIC_V1_CONNECTOR = "Basic-v1";
 | 
						|
const ETH = constants.native_address
 | 
						|
 | 
						|
const openTroveSpell = async (
 | 
						|
  dsa: any,
 | 
						|
  signer: Signer,
 | 
						|
  depositAmount: any,
 | 
						|
  borrowAmount: any,
 | 
						|
  upperHint: any,
 | 
						|
  lowerHint: any,
 | 
						|
  maxFeePercentage: any
 | 
						|
) => {
 | 
						|
  let address = await signer.getAddress();
 | 
						|
 | 
						|
  const openTroveSpell = {
 | 
						|
    connector: LIQUITY_CONNECTOR,
 | 
						|
    method: "open",
 | 
						|
    args: [
 | 
						|
      depositAmount,
 | 
						|
      maxFeePercentage,
 | 
						|
      borrowAmount,
 | 
						|
      upperHint,
 | 
						|
      lowerHint,
 | 
						|
      [0, 0],
 | 
						|
      [0, 0],
 | 
						|
    ],
 | 
						|
  };
 | 
						|
 | 
						|
  return await dsa
 | 
						|
    .connect(signer)
 | 
						|
    .cast(...encodeSpells([openTroveSpell]), address, {
 | 
						|
      value: depositAmount,
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
const createDsaTrove = async (
 | 
						|
  dsa: any,
 | 
						|
  signer: any,
 | 
						|
  liquity: any,
 | 
						|
  depositAmount = hre.ethers.utils.parseEther("5"),
 | 
						|
  borrowAmount = hre.ethers.utils.parseUnits("2000", 18)
 | 
						|
) => {
 | 
						|
  const maxFeePercentage = hre.ethers.utils.parseUnits("0.5", 18); // 0.5% max fee
 | 
						|
  const { upperHint, lowerHint } = await getTroveInsertionHints(
 | 
						|
    depositAmount,
 | 
						|
    borrowAmount,
 | 
						|
    liquity
 | 
						|
  );
 | 
						|
  return await openTroveSpell(
 | 
						|
    dsa,
 | 
						|
    signer,
 | 
						|
    depositAmount,
 | 
						|
    borrowAmount,
 | 
						|
    upperHint,
 | 
						|
    lowerHint,
 | 
						|
    maxFeePercentage
 | 
						|
  );
 | 
						|
};
 | 
						|
 | 
						|
const sendToken = async (token: any, amount: any, from: any, to: any) => {
 | 
						|
  await hre.network.provider.request({
 | 
						|
    method: "hardhat_impersonateAccount",
 | 
						|
    params: [from],
 | 
						|
  });
 | 
						|
  const signer = hre.ethers.provider.getSigner(from);
 | 
						|
 | 
						|
  return await token.connect(signer).transfer(to, amount);
 | 
						|
};
 | 
						|
 | 
						|
const resetInitialState = async (walletAddress: any, contracts: any, isDebug = false) => {
 | 
						|
  const liquity = await deployAndConnect(contracts, isDebug);
 | 
						|
  const dsa = await buildDSAv2(walletAddress);
 | 
						|
 | 
						|
  return [liquity, dsa];
 | 
						|
};
 | 
						|
 | 
						|
const resetHardhatBlockNumber = async (blockNumber: number) => {
 | 
						|
  return await hre.network.provider.request({
 | 
						|
    method: "hardhat_reset",
 | 
						|
    params: [
 | 
						|
      {
 | 
						|
        forking: {
 | 
						|
          // @ts-ignore
 | 
						|
          jsonRpcUrl: hre.config.networks.hardhat.forking.url,
 | 
						|
          blockNumber,
 | 
						|
        },
 | 
						|
      },
 | 
						|
    ],
 | 
						|
  });
 | 
						|
};
 | 
						|
 | 
						|
const deployAndConnect = async (contracts: any, isDebug = false) => {
 | 
						|
  // Pin Liquity tests to a particular block number to create deterministic state (Ether price etc.)
 | 
						|
  await resetHardhatBlockNumber(LIQUIDATABLE_TROVES_BLOCK_NUMBER);
 | 
						|
  type Liquidity = {
 | 
						|
    troveManager: Contract,
 | 
						|
    borrowerOperations: Contract,
 | 
						|
    stabilityPool: Contract,
 | 
						|
    lusdToken: Contract,
 | 
						|
    lqtyToken: Contract,
 | 
						|
    activePool: Contract,
 | 
						|
    priceFeed: Contract,
 | 
						|
    hintHelpers: Contract,
 | 
						|
    sortedTroves: Contract,
 | 
						|
    staking: Contract,
 | 
						|
    collSurplus: Contract,
 | 
						|
  };
 | 
						|
 | 
						|
  const liquity = {} as Liquidity
 | 
						|
 | 
						|
  const masterSigner = await getMasterSigner();
 | 
						|
  const instaConnectorsV2 = await ethers.getContractAt(
 | 
						|
    abis.core.connectorsV2,
 | 
						|
    addresses.core.connectorsV2
 | 
						|
  );
 | 
						|
  const connector = await deployAndEnableConnector({
 | 
						|
    connectorName: LIQUITY_CONNECTOR,
 | 
						|
    contractArtifact: ConnectV2Liquity__factory,
 | 
						|
    signer: masterSigner,
 | 
						|
    connectors: instaConnectorsV2,
 | 
						|
  });
 | 
						|
  isDebug &&
 | 
						|
    console.log(`${LIQUITY_CONNECTOR} Connector address`, connector.address);
 | 
						|
 | 
						|
  const basicConnector = await deployAndEnableConnector({
 | 
						|
    connectorName: "Basic-v1",
 | 
						|
    contractArtifact: ConnectV2Basic__factory,
 | 
						|
    signer: masterSigner,
 | 
						|
    connectors: instaConnectorsV2,
 | 
						|
  });
 | 
						|
  isDebug && console.log("Basic-v1 Connector address", basicConnector.address);
 | 
						|
 | 
						|
  liquity.troveManager = new ethers.Contract(
 | 
						|
    contracts.TROVE_MANAGER_ADDRESS,
 | 
						|
    contracts.TROVE_MANAGER_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.borrowerOperations = new ethers.Contract(
 | 
						|
    contracts.BORROWER_OPERATIONS_ADDRESS,
 | 
						|
    contracts.BORROWER_OPERATIONS_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.stabilityPool = new ethers.Contract(
 | 
						|
    contracts.STABILITY_POOL_ADDRESS,
 | 
						|
    contracts.STABILITY_POOL_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.lusdToken = new ethers.Contract(
 | 
						|
    contracts.LUSD_TOKEN_ADDRESS,
 | 
						|
    contracts.LUSD_TOKEN_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.lqtyToken = new ethers.Contract(
 | 
						|
    contracts.LQTY_TOKEN_ADDRESS,
 | 
						|
    contracts.LQTY_TOKEN_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.activePool = new ethers.Contract(
 | 
						|
    contracts.ACTIVE_POOL_ADDRESS,
 | 
						|
    contracts.ACTIVE_POOL_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.priceFeed = new ethers.Contract(
 | 
						|
    contracts.PRICE_FEED_ADDRESS,
 | 
						|
    contracts.PRICE_FEED_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.hintHelpers = new ethers.Contract(
 | 
						|
    contracts.HINT_HELPERS_ADDRESS,
 | 
						|
    contracts.HINT_HELPERS_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.sortedTroves = new ethers.Contract(
 | 
						|
    contracts.SORTED_TROVES_ADDRESS,
 | 
						|
    contracts.SORTED_TROVES_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  liquity.staking = new ethers.Contract(
 | 
						|
    contracts.STAKING_ADDRESS,
 | 
						|
    contracts.STAKING_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
  liquity.collSurplus = new ethers.Contract(
 | 
						|
    contracts.COLL_SURPLUS_ADDRESS,
 | 
						|
    contracts.COLL_SURPLUS_ABI,
 | 
						|
    ethers.provider
 | 
						|
  );
 | 
						|
 | 
						|
  return liquity;
 | 
						|
};
 | 
						|
 | 
						|
const getTroveInsertionHints = async (depositAmount: BigNumber, borrowAmount: BigNumber, liquity: any) => {
 | 
						|
  const nominalCR = await liquity.hintHelpers.computeNominalCR(
 | 
						|
    depositAmount,
 | 
						|
    borrowAmount
 | 
						|
  );
 | 
						|
 | 
						|
  const {
 | 
						|
    hintAddress,
 | 
						|
    latestRandomSeed,
 | 
						|
  } = await liquity.hintHelpers.getApproxHint(nominalCR, 50, 1298379, {
 | 
						|
    gasLimit: MAX_GAS,
 | 
						|
  });
 | 
						|
  randomSeed = latestRandomSeed;
 | 
						|
 | 
						|
  const {
 | 
						|
    0: upperHint,
 | 
						|
    1: lowerHint,
 | 
						|
  } = await liquity.sortedTroves.findInsertPosition(
 | 
						|
    nominalCR,
 | 
						|
    hintAddress,
 | 
						|
    hintAddress,
 | 
						|
    {
 | 
						|
      gasLimit: MAX_GAS,
 | 
						|
    }
 | 
						|
  );
 | 
						|
 | 
						|
  return {
 | 
						|
    upperHint,
 | 
						|
    lowerHint,
 | 
						|
  };
 | 
						|
};
 | 
						|
 | 
						|
let randomSeed = 4223;
 | 
						|
 | 
						|
const getRedemptionHints = async (amount: any, liquity: any) => {
 | 
						|
  const ethPrice = await liquity.priceFeed.callStatic.fetchPrice();
 | 
						|
  const [
 | 
						|
    firstRedemptionHint,
 | 
						|
    partialRedemptionHintNicr,
 | 
						|
  ] = await liquity.hintHelpers.getRedemptionHints(amount, ethPrice, 0);
 | 
						|
 | 
						|
  const {
 | 
						|
    hintAddress,
 | 
						|
    latestRandomSeed,
 | 
						|
  } = await liquity.hintHelpers.getApproxHint(
 | 
						|
    partialRedemptionHintNicr,
 | 
						|
    50,
 | 
						|
    randomSeed,
 | 
						|
    {
 | 
						|
      gasLimit: MAX_GAS,
 | 
						|
    }
 | 
						|
  );
 | 
						|
  randomSeed = latestRandomSeed;
 | 
						|
 | 
						|
  const {
 | 
						|
    0: upperHint,
 | 
						|
    1: lowerHint,
 | 
						|
  } = await liquity.sortedTroves.findInsertPosition(
 | 
						|
    partialRedemptionHintNicr,
 | 
						|
    hintAddress,
 | 
						|
    hintAddress,
 | 
						|
    {
 | 
						|
      gasLimit: MAX_GAS,
 | 
						|
    }
 | 
						|
  );
 | 
						|
 | 
						|
  return {
 | 
						|
    partialRedemptionHintNicr,
 | 
						|
    firstRedemptionHint,
 | 
						|
    upperHint,
 | 
						|
    lowerHint,
 | 
						|
  };
 | 
						|
};
 | 
						|
 | 
						|
const redeem = async (amount: any, from: any, wallet: { address: any; }, liquity: any) => {
 | 
						|
  await sendToken(liquity.lusdToken, amount, from, wallet.address);
 | 
						|
  const {
 | 
						|
    partialRedemptionHintNicr,
 | 
						|
    firstRedemptionHint,
 | 
						|
    upperHint,
 | 
						|
    lowerHint,
 | 
						|
  } = await getRedemptionHints(amount, liquity);
 | 
						|
  const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee
 | 
						|
 | 
						|
  return await liquity.troveManager
 | 
						|
    .connect(wallet)
 | 
						|
    .redeemCollateral(
 | 
						|
      amount,
 | 
						|
      firstRedemptionHint,
 | 
						|
      upperHint,
 | 
						|
      lowerHint,
 | 
						|
      partialRedemptionHintNicr,
 | 
						|
      0,
 | 
						|
      maxFeePercentage,
 | 
						|
      {
 | 
						|
        gasLimit: MAX_GAS, // permit max gas
 | 
						|
      }
 | 
						|
    );
 | 
						|
};
 | 
						|
 | 
						|
export default {
 | 
						|
  deployAndConnect,
 | 
						|
  resetInitialState,
 | 
						|
  createDsaTrove,
 | 
						|
  sendToken,
 | 
						|
  getTroveInsertionHints,
 | 
						|
  getRedemptionHints,
 | 
						|
  redeem,
 | 
						|
  LIQUITY_CONNECTOR,
 | 
						|
  LUSD_GAS_COMPENSATION,
 | 
						|
  JUSTIN_SUN_ADDRESS,
 | 
						|
  LIQUIDATABLE_TROVE_ADDRESS,
 | 
						|
  MAX_GAS,
 | 
						|
  INSTADAPP_BASIC_V1_CONNECTOR,
 | 
						|
  ETH,
 | 
						|
};
 |