mirror of
				https://github.com/Instadapp/dsa-connectors-2.0.git
				synced 2024-07-29 21:57:39 +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,
 | |
| };
 | 
