diff --git a/test/mainnet/liquity/liquity.contracts.ts b/test/mainnet/liquity/liquity.contracts.ts index 44fa6534..f6a6df74 100644 --- a/test/mainnet/liquity/liquity.contracts.ts +++ b/test/mainnet/liquity/liquity.contracts.ts @@ -1,95 +1,95 @@ -// const TROVE_MANAGER_ADDRESS = "0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2"; -// const TROVE_MANAGER_ABI = [ -// "function getTroveColl(address _borrower) external view returns (uint)", -// "function getTroveDebt(address _borrower) external view returns (uint)", -// "function getTroveStatus(address _borrower) external view returns (uint)", -// "function redeemCollateral(uint _LUSDAmount, address _firstRedemptionHint, address _upperPartialRedemptionHint, address _lowerPartialRedemptionHint, uint _partialRedemptionHintNICR, uint _maxIterations, uint _maxFee) external returns (uint)", -// "function getNominalICR(address _borrower) external view returns (uint)", -// "function liquidate(address _borrower) external", -// "function liquidateTroves(uint _n) external", -// ]; +const TROVE_MANAGER_ADDRESS = "0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2"; +const TROVE_MANAGER_ABI = [ + "function getTroveColl(address _borrower) external view returns (uint)", + "function getTroveDebt(address _borrower) external view returns (uint)", + "function getTroveStatus(address _borrower) external view returns (uint)", + "function redeemCollateral(uint _LUSDAmount, address _firstRedemptionHint, address _upperPartialRedemptionHint, address _lowerPartialRedemptionHint, uint _partialRedemptionHintNICR, uint _maxIterations, uint _maxFee) external returns (uint)", + "function getNominalICR(address _borrower) external view returns (uint)", + "function liquidate(address _borrower) external", + "function liquidateTroves(uint _n) external", +]; -// const BORROWER_OPERATIONS_ADDRESS = -// "0x24179CD81c9e782A4096035f7eC97fB8B783e007"; -// const BORROWER_OPERATIONS_ABI = [ -// "function openTrove(uint256 _maxFee, uint256 _LUSDAmount, address _upperHint, address _lowerHint) external payable", -// "function closeTrove() external", -// ]; +const BORROWER_OPERATIONS_ADDRESS = + "0x24179CD81c9e782A4096035f7eC97fB8B783e007"; +const BORROWER_OPERATIONS_ABI = [ + "function openTrove(uint256 _maxFee, uint256 _LUSDAmount, address _upperHint, address _lowerHint) external payable", + "function closeTrove() external", +]; -// const LUSD_TOKEN_ADDRESS = "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0"; -// const LUSD_TOKEN_ABI = [ -// "function transfer(address _to, uint256 _value) public returns (bool success)", -// "function balanceOf(address account) external view returns (uint256)", -// "function approve(address spender, uint256 amount) external returns (bool)", -// ]; +const LUSD_TOKEN_ADDRESS = "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0"; +const LUSD_TOKEN_ABI = [ + "function transfer(address _to, uint256 _value) public returns (bool success)", + "function balanceOf(address account) external view returns (uint256)", + "function approve(address spender, uint256 amount) external returns (bool)", +]; -// const ACTIVE_POOL_ADDRESS = "0xDf9Eb223bAFBE5c5271415C75aeCD68C21fE3D7F"; -// const ACTIVE_POOL_ABI = ["function getLUSDDebt() external view returns (uint)"]; +const ACTIVE_POOL_ADDRESS = "0xDf9Eb223bAFBE5c5271415C75aeCD68C21fE3D7F"; +const ACTIVE_POOL_ABI = ["function getLUSDDebt() external view returns (uint)"]; -// const PRICE_FEED_ADDRESS = "0x4c517D4e2C851CA76d7eC94B805269Df0f2201De"; -// const PRICE_FEED_ABI = ["function fetchPrice() external returns (uint)"]; +const PRICE_FEED_ADDRESS = "0x4c517D4e2C851CA76d7eC94B805269Df0f2201De"; +const PRICE_FEED_ABI = ["function fetchPrice() external returns (uint)"]; -// const HINT_HELPERS_ADDRESS = "0xE84251b93D9524E0d2e621Ba7dc7cb3579F997C0"; -// const HINT_HELPERS_ABI = [ -// "function getRedemptionHints(uint _LUSDamount, uint _price, uint _maxIterations) external view returns (address firstRedemptionHint, uint partialRedemptionHintNICR, uint truncatedLUSDamount)", -// "function getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed) view returns (address hintAddress, uint diff, uint latestRandomSeed)", -// "function computeNominalCR(uint _coll, uint _debt) external pure returns (uint)", -// ]; +const HINT_HELPERS_ADDRESS = "0xE84251b93D9524E0d2e621Ba7dc7cb3579F997C0"; +const HINT_HELPERS_ABI = [ + "function getRedemptionHints(uint _LUSDamount, uint _price, uint _maxIterations) external view returns (address firstRedemptionHint, uint partialRedemptionHintNICR, uint truncatedLUSDamount)", + "function getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed) view returns (address hintAddress, uint diff, uint latestRandomSeed)", + "function computeNominalCR(uint _coll, uint _debt) external pure returns (uint)", +]; -// const SORTED_TROVES_ADDRESS = "0x8FdD3fbFEb32b28fb73555518f8b361bCeA741A6"; -// const SORTED_TROVES_ABI = [ -// "function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address)", -// "function getLast() external view returns (address)", -// ]; +const SORTED_TROVES_ADDRESS = "0x8FdD3fbFEb32b28fb73555518f8b361bCeA741A6"; +const SORTED_TROVES_ABI = [ + "function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address)", + "function getLast() external view returns (address)", +]; -// const STABILITY_POOL_ADDRESS = "0x66017D22b0f8556afDd19FC67041899Eb65a21bb"; -// const STABILITY_POOL_ABI = [ -// "function getCompoundedLUSDDeposit(address _depositor) external view returns (uint)", -// "function getDepositorETHGain(address _depositor) external view returns (uint)", -// "function getDepositorLQTYGain(address _depositor) external view returns (uint)", -// ]; +const STABILITY_POOL_ADDRESS = "0x66017D22b0f8556afDd19FC67041899Eb65a21bb"; +const STABILITY_POOL_ABI = [ + "function getCompoundedLUSDDeposit(address _depositor) external view returns (uint)", + "function getDepositorETHGain(address _depositor) external view returns (uint)", + "function getDepositorLQTYGain(address _depositor) external view returns (uint)", +]; -// const STAKING_ADDRESS = "0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d"; -// const STAKING_ABI = [ -// "function stake(uint _LQTYamount) external", -// "function unstake(uint _LQTYamount) external", -// "function getPendingETHGain(address _user) external view returns (uint)", -// "function getPendingLUSDGain(address _user) external view returns (uint)", -// ]; +const STAKING_ADDRESS = "0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d"; +const STAKING_ABI = [ + "function stake(uint _LQTYamount) external", + "function unstake(uint _LQTYamount) external", + "function getPendingETHGain(address _user) external view returns (uint)", + "function getPendingLUSDGain(address _user) external view returns (uint)", +]; -// const LQTY_TOKEN_ADDRESS = "0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D"; -// const LQTY_TOKEN_ABI = [ -// "function balanceOf(address account) external view returns (uint256)", -// "function transfer(address _to, uint256 _value) public returns (bool success)", -// "function approve(address spender, uint256 amount) external returns (bool)", -// ]; +const LQTY_TOKEN_ADDRESS = "0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D"; +const LQTY_TOKEN_ABI = [ + "function balanceOf(address account) external view returns (uint256)", + "function transfer(address _to, uint256 _value) public returns (bool success)", + "function approve(address spender, uint256 amount) external returns (bool)", +]; -// const COLL_SURPLUS_ADDRESS = "0x3D32e8b97Ed5881324241Cf03b2DA5E2EBcE5521"; -// const COLL_SURPLUS_ABI = [ -// "function getCollateral(address _account) external view returns (uint)", -// ]; +const COLL_SURPLUS_ADDRESS = "0x3D32e8b97Ed5881324241Cf03b2DA5E2EBcE5521"; +const COLL_SURPLUS_ABI = [ + "function getCollateral(address _account) external view returns (uint)", +]; -// module.exports = { -// TROVE_MANAGER_ADDRESS, -// TROVE_MANAGER_ABI, -// BORROWER_OPERATIONS_ADDRESS, -// BORROWER_OPERATIONS_ABI, -// LUSD_TOKEN_ADDRESS, -// LUSD_TOKEN_ABI, -// STABILITY_POOL_ADDRESS, -// STABILITY_POOL_ABI, -// ACTIVE_POOL_ADDRESS, -// ACTIVE_POOL_ABI, -// PRICE_FEED_ADDRESS, -// PRICE_FEED_ABI, -// HINT_HELPERS_ADDRESS, -// HINT_HELPERS_ABI, -// SORTED_TROVES_ADDRESS, -// SORTED_TROVES_ABI, -// STAKING_ADDRESS, -// STAKING_ABI, -// LQTY_TOKEN_ADDRESS, -// LQTY_TOKEN_ABI, -// COLL_SURPLUS_ADDRESS, -// COLL_SURPLUS_ABI, -// }; +module.exports = { + TROVE_MANAGER_ADDRESS, + TROVE_MANAGER_ABI, + BORROWER_OPERATIONS_ADDRESS, + BORROWER_OPERATIONS_ABI, + LUSD_TOKEN_ADDRESS, + LUSD_TOKEN_ABI, + STABILITY_POOL_ADDRESS, + STABILITY_POOL_ABI, + ACTIVE_POOL_ADDRESS, + ACTIVE_POOL_ABI, + PRICE_FEED_ADDRESS, + PRICE_FEED_ABI, + HINT_HELPERS_ADDRESS, + HINT_HELPERS_ABI, + SORTED_TROVES_ADDRESS, + SORTED_TROVES_ABI, + STAKING_ADDRESS, + STAKING_ABI, + LQTY_TOKEN_ADDRESS, + LQTY_TOKEN_ABI, + COLL_SURPLUS_ADDRESS, + COLL_SURPLUS_ABI, +}; diff --git a/test/mainnet/liquity/liquity.helpers.ts b/test/mainnet/liquity/liquity.helpers.ts index 26ff0851..bab90124 100644 --- a/test/mainnet/liquity/liquity.helpers.ts +++ b/test/mainnet/liquity/liquity.helpers.ts @@ -1,343 +1,343 @@ -// import hre from "hardhat"; -// import { ethers } from "hardhat"; -// import hardhatConfig from "../../../hardhat.config"; +import hre from "hardhat"; +import { ethers } from "hardhat"; +import hardhatConfig from "../../../hardhat.config"; -// // Instadapp deployment and testing helpers -// import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector.js" -// import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" -// import { encodeSpells } from "../../../scripts/tests/encodeSpells.js" -// import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" +// Instadapp deployment and testing helpers +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector.js" +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells.js" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" -// // Instadapp instadappAddresses/ABIs -// import { instadappAddresses } from "../../../scripts/important/addresses"; -// import { instadappAbi } from "../../../scripts/constant/abis"; +// Instadapp instadappAddresses/ABIs +import { instadappAddresses } from "../../../scripts/important/addresses"; +import { instadappAbi } from "../../../scripts/constant/abis"; -// // Instadapp Liquity Connector artifacts -// import { ConnectV2Liquity__factory, ConnectV2Basic__factory } from "../../../typechain"; +// 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.js"; -// import type { Signer, Contract } from "ethers"; +// Instadapp uses a fake address to represent native ETH +import { constants } from "../../../scripts/constant/constant.js"; +import type { Signer, Contract } 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 -// // const MAX_GAS = hardhatConfig.networks.hardhat.blockGasLimit; // Maximum gas limit (12000000) -// const INSTADAPP_BASIC_V1_CONNECTOR = "Basic-v1"; +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 +// const MAX_GAS = hardhatConfig.networks.hardhat.blockGasLimit; // Maximum gas limit (12000000) +const INSTADAPP_BASIC_V1_CONNECTOR = "Basic-v1"; -// const openTroveSpell = async ( -// dsa: any, -// signer: Signer, -// depositAmount: any, -// borrowAmount: any, -// upperHint: any, -// lowerHint: any, -// maxFeePercentage: any -// ) => { -// let address = await signer.getAddress(); +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], -// ], -// }; + 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, -// }); -// }; + 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 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); +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, { -// gasPrice: 0, -// }); -// }; + return await token.connect(signer).transfer(to, amount, { + gasPrice: 0, + }); +}; -// const resetInitialState = async (walletAddress: any, contracts: any, isDebug = false) => { -// const liquity = await deployAndConnect(contracts, isDebug); -// const dsa = await buildDSAv2(walletAddress); +const resetInitialState = async (walletAddress: any, contracts: any, isDebug = false) => { + const liquity = await deployAndConnect(contracts, isDebug); + const dsa = await buildDSAv2(walletAddress); -// return [liquity, dsa]; -// }; + 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 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); -// let liquity = { -// troveManager: Contract, -// borrowerOperations: null, -// stabilityPool: null, -// lusdToken: null, -// lqtyToken: null, -// activePool: null, -// priceFeed: null, -// hintHelpers: null, -// sortedTroves: null, -// staking: null, -// collSurplus: null, -// }; +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); + let liquity = { + troveManager: Contract, + borrowerOperations: null, + stabilityPool: null, + lusdToken: null, + lqtyToken: null, + activePool: null, + priceFeed: null, + hintHelpers: null, + sortedTroves: null, + staking: null, + collSurplus: null, + }; -// const masterSigner = await getMasterSigner(); -// const instaConnectorsV2 = await ethers.getContractAt( -// instadappAbi.core.connectorsV2, -// instadappAddresses.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 masterSigner = await getMasterSigner(); + const instaConnectorsV2 = await ethers.getContractAt( + instadappAbi.core.connectorsV2, + instadappAddresses.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); + 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.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.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.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.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.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.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.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.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.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 -// ); + 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; -// }; + return liquity; +}; -// const getTroveInsertionHints = async (depositAmount, borrowAmount, liquity: any) => { -// const nominalCR = await liquity.hintHelpers.computeNominalCR( -// depositAmount, -// borrowAmount -// ); +const getTroveInsertionHints = async (depositAmount, borrowAmount, 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 { + 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, -// } -// ); + const { + 0: upperHint, + 1: lowerHint, + } = await liquity.sortedTroves.findInsertPosition( + nominalCR, + hintAddress, + hintAddress, + { + gasLimit: MAX_GAS, + } + ); -// return { -// upperHint, -// lowerHint, -// }; -// }; + return { + upperHint, + lowerHint, + }; +}; -// let randomSeed = 4223; +let randomSeed = 4223; -// const getRedemptionHints = async (amount, liquity) => { -// const ethPrice = await liquity.priceFeed.callStatic.fetchPrice(); -// const [ -// firstRedemptionHint, -// partialRedemptionHintNicr, -// ] = await liquity.hintHelpers.getRedemptionHints(amount, ethPrice, 0); +const getRedemptionHints = async (amount, liquity) => { + 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 { + 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, -// } -// ); + const { + 0: upperHint, + 1: lowerHint, + } = await liquity.sortedTroves.findInsertPosition( + partialRedemptionHintNicr, + hintAddress, + hintAddress, + { + gasLimit: MAX_GAS, + } + ); -// return { -// partialRedemptionHintNicr, -// firstRedemptionHint, -// upperHint, -// lowerHint, -// }; -// }; + return { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + }; +}; -// const redeem = async (amount, from, wallet, liquity) => { -// 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 +const redeem = async (amount, from, wallet, liquity) => { + 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 -// } -// ); -// }; + return await liquity.troveManager + .connect(wallet) + .redeemCollateral( + amount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: MAX_GAS, // permit max gas + } + ); +}; -// module.exports = { -// 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_ADDRESS, -// }; +module.exports = { + 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_ADDRESS, +}; diff --git a/test/mainnet/liquity/liquity.test.ts b/test/mainnet/liquity/liquity.test.ts index 151c1881..fd5ccd69 100644 --- a/test/mainnet/liquity/liquity.test.ts +++ b/test/mainnet/liquity/liquity.test.ts @@ -1,2719 +1,2719 @@ -// import hre from "hardhat"; -// import { expect } from "chai"; - -// // Instadapp deployment and testing helpers -// import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; -// import { encodeSpells } from "../../../scripts/tests/encodeSpells.js"; - -// // Liquity smart contracts -// import contracts from "./liquity.contracts"; - -// // Liquity helpers -// import helpers from "./liquity.helpers"; - -// describe("Liquity", () => { -// const { waffle, ethers } = hre; -// const { provider } = waffle; - -// // Waffle test account 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 (holds 1000 ETH) -// const userWallet = provider.getWallets()[0]; -// let dsa = null; -// let liquity = null; - -// before(async () => { -// await hre.network.provider.request({ -// method: "hardhat_reset", -// params: [ -// { -// forking: { -// jsonRpcUrl: hre.config.networks.hardhat.forking.url, -// blockNumber: 13300000, -// }, -// }, -// ], -// }); -// liquity = await helpers.deployAndConnect(contracts, true); -// expect(liquity.troveManager.address).to.exist; -// expect(liquity.borrowerOperations.address).to.exist; -// expect(liquity.stabilityPool.address).to.exist; -// expect(liquity.lusdToken.address).to.exist; -// expect(liquity.lqtyToken.address).to.exist; -// expect(liquity.activePool.address).to.exist; -// expect(liquity.priceFeed.address).to.exist; -// expect(liquity.hintHelpers.address).to.exist; -// expect(liquity.sortedTroves.address).to.exist; -// expect(liquity.staking.address).to.exist; -// }); - -// beforeEach(async () => { -// // Build a new DSA before each test so we start each test from the same default state -// dsa = await buildDSAv2(userWallet.address); -// expect(dsa.address).to.exist; -// }); - -// describe("Main (Connector)", () => { -// describe("Trove", () => { -// describe("open()", () => { -// it("opens a Trove", async () => { -// const depositAmount = ethers.utils.parseEther("5"); // 5 ETH -// const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const originalUserBalance = await ethers.provider.getBalance( -// userWallet.address -// ); -// const originalDsaBalance = await ethers.provider.getBalance( -// dsa.address -// ); - -// const openTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "open", -// args: [ -// depositAmount, -// maxFeePercentage, -// borrowAmount, -// upperHint, -// lowerHint, -// [0, 0], -// [0, 0], -// ], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([openTroveSpell]), userWallet.address, { -// value: depositAmount, -// gasPrice: 0, -// }); - -// const userBalance = await ethers.provider.getBalance( -// userWallet.address -// ); -// const dsaEthBalance = await ethers.provider.getBalance(dsa.address); -// const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// expect(userBalance).eq( -// originalUserBalance.sub(depositAmount), -// "User's Ether balance should decrease by the amount they deposited" -// ); - -// expect(dsaEthBalance).to.eq( -// originalDsaBalance, -// "User's DSA account Ether should not change after borrowing" -// ); - -// expect( -// dsaLusdBalance, -// "DSA account should now hold the amount the user borrowed" -// ).to.eq(borrowAmount); - -// expect(troveDebt).to.gt( -// borrowAmount, -// "Trove debt should equal the borrowed amount plus fee" -// ); - -// expect(troveCollateral).to.eq( -// depositAmount, -// "Trove collateral should equal the deposited amount" -// ); -// }); - -// it("opens a Trove using ETH collected from a previous spell", async () => { -// const depositAmount = ethers.utils.parseEther("5"); // 5 ETH -// const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const originalUserBalance = await ethers.provider.getBalance( -// userWallet.address -// ); -// const originalDsaBalance = await ethers.provider.getBalance( -// dsa.address -// ); -// const depositId = 1; // Choose an ID to store and retrieve the deposited ETH - -// const depositEthSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "deposit", -// args: [helpers.ETH_ADDRESS, depositAmount, 0, depositId], -// }; - -// const openTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "open", -// args: [ -// 0, // When pulling ETH from a previous spell it doesn't matter what deposit value we put in this param -// maxFeePercentage, -// borrowAmount, -// upperHint, -// lowerHint, -// [depositId, 0], -// [0, 0], -// ], -// }; - -// const spells = [depositEthSpell, openTroveSpell]; -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address, { -// value: depositAmount, -// gasPrice: 0, -// }); - -// const userBalance = await ethers.provider.getBalance( -// userWallet.address -// ); -// const dsaEthBalance = await ethers.provider.getBalance(dsa.address); -// const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// expect(userBalance).eq( -// originalUserBalance.sub(depositAmount), -// "User's Ether balance should decrease by the amount they deposited" -// ); - -// expect(dsaEthBalance).to.eq( -// originalDsaBalance, -// "DSA balance should not change" -// ); - -// expect( -// dsaLusdBalance, -// "DSA account should now hold the amount the user borrowed" -// ).to.eq(borrowAmount); - -// expect(troveDebt).to.gt( -// borrowAmount, -// "Trove debt should equal the borrowed amount plus fee" -// ); - -// expect(troveCollateral).to.eq( -// depositAmount, -// "Trove collateral should equal the deposited amount" -// ); -// }); - -// it("opens a Trove and stores the debt for other spells to use", async () => { -// const depositAmount = ethers.utils.parseEther("5"); // 5 ETH -// const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const originalUserBalance = await ethers.provider.getBalance( -// userWallet.address -// ); -// const originalDsaBalance = await ethers.provider.getBalance( -// dsa.address -// ); -// const borrowId = 1; - -// const openTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "open", -// args: [ -// depositAmount, -// maxFeePercentage, -// borrowAmount, -// upperHint, -// lowerHint, -// [0, 0], -// [borrowId, 0], -// ], -// }; - -// const withdrawLusdSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [ -// contracts.LUSD_TOKEN_ADDRESS, -// 0, // Amount comes from the previous spell's setId -// dsa.address, -// borrowId, -// 0, -// ], -// }; - -// const spells = [openTroveSpell, withdrawLusdSpell]; -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address, { -// value: depositAmount, -// gasPrice: 0, -// }); - -// const userBalance = await ethers.provider.getBalance( -// userWallet.address -// ); -// const dsaEthBalance = await ethers.provider.getBalance(dsa.address); -// const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// expect(userBalance).eq( -// originalUserBalance.sub(depositAmount), -// "User's Ether balance should decrease by the amount they deposited" -// ); - -// expect(dsaEthBalance).to.eq( -// originalDsaBalance, -// "User's DSA account Ether should not change after borrowing" -// ); - -// expect( -// dsaLusdBalance, -// "DSA account should now hold the amount the user borrowed" -// ).to.eq(borrowAmount); - -// expect(troveDebt).to.gt( -// borrowAmount, -// "Trove debt should equal the borrowed amount plus fee" -// ); - -// expect(troveCollateral).to.eq( -// depositAmount, -// "Trove collateral should equal the deposited amount" -// ); -// }); - -// it("returns Instadapp event name and data", async () => { -// const depositAmount = ethers.utils.parseEther("5"); -// const borrowAmount = ethers.utils.parseUnits("2000", 18); -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; - -// const openTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "open", -// args: [ -// depositAmount, -// maxFeePercentage, -// borrowAmount, -// upperHint, -// lowerHint, -// [0, 0], -// [0, 0], -// ], -// }; - -// const openTx = await dsa.cast( -// ...encodeSpells([openTroveSpell]), -// userWallet.address, -// { -// value: depositAmount, -// } -// ); -// const receipt = await openTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// expect(castLogEvent.eventNames[0]).eq( -// "LogOpen(address,uint256,uint256,uint256,uint256[],uint256[])" -// ); -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// [ -// "address", -// "uint256", -// "uint256", -// "uint256", -// "uint256[]", -// "uint256[]", -// ], -// [ -// dsa.address, -// maxFeePercentage, -// depositAmount, -// borrowAmount, -// [0, 0], -// [0, 0], -// ] -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("close()", () => { -// it("closes a Trove", async () => { -// const depositAmount = ethers.utils.parseEther("5"); -// const borrowAmount = ethers.utils.parseUnits("2000", 18); -// // Create a dummy Trove -// await helpers.createDsaTrove( -// dsa, -// userWallet, -// liquity, -// depositAmount, -// borrowAmount -// ); - -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); - -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// // Send DSA account enough LUSD (from Stability Pool) to close their Trove -// const extraLusdRequiredToCloseTrove = troveDebtBefore.sub( -// borrowAmount -// ); - -// await helpers.sendToken( -// liquity.lusdToken, -// extraLusdRequiredToCloseTrove, -// contracts.STABILITY_POOL_ADDRESS, -// dsa.address -// ); - -// const originalDsaLusdBalance = await liquity.lusdToken.balanceOf( -// dsa.address -// ); - -// expect( -// originalDsaLusdBalance, -// "DSA account should now hold the LUSD amount required to pay off the Trove debt" -// ).to.eq(troveDebtBefore); - -// const closeTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "close", -// args: [0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([closeTroveSpell]), userWallet.address); - -// const dsaEthBalance = await ethers.provider.getBalance(dsa.address); -// const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); - -// expect( -// troveCollateral, -// "Trove collateral should equal 0 after close" -// ).to.eq(0); - -// expect( -// dsaEthBalance, -// "DSA account should now hold the Trove's ETH collateral" -// ).to.eq(troveCollateralBefore); - -// expect( -// dsaLusdBalance, -// "DSA account should now hold the gas compensation amount of LUSD as it paid off the Trove debt" -// ).to.eq(helpers.LUSD_GAS_COMPENSATION); -// }); - -// it("closes a Trove using LUSD obtained from a previous spell", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// // Send user enough LUSD to repay the loan, we'll use a deposit and withdraw spell to obtain it -// await helpers.sendToken( -// liquity.lusdToken, -// troveDebtBefore, -// contracts.STABILITY_POOL_ADDRESS, -// userWallet.address -// ); - -// // Allow DSA to spend user's LUSD -// await liquity.lusdToken -// .connect(userWallet) -// .approve(dsa.address, troveDebtBefore); - -// // Simulate a spell which would have pulled LUSD from somewhere (e.g. Uniswap) into InstaMemory -// // In this case we're simply running a deposit spell from the user's EOA -// const depositLusdSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "deposit", -// args: [contracts.LUSD_TOKEN_ADDRESS, troveDebtBefore, 0, 0], -// }; - -// const closeTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "close", -// args: [0], -// }; -// const spells = [depositLusdSpell, closeTroveSpell]; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address); - -// const dsaEthBalance = await ethers.provider.getBalance(dsa.address); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); - -// expect( -// troveCollateral, -// "Trove collateral should equal 0 after close" -// ).to.eq(0); - -// expect( -// dsaEthBalance, -// "DSA account should now hold the Trove's ETH collateral" -// ).to.eq(troveCollateralBefore); -// }); - -// it("closes a Trove and stores the released collateral for other spells to use", async () => { -// const depositAmount = ethers.utils.parseEther("5"); -// const borrowAmount = ethers.utils.parseUnits("2000", 18); -// // Create a dummy Trove -// await helpers.createDsaTrove( -// dsa, -// userWallet, -// liquity, -// depositAmount, -// borrowAmount -// ); - -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// // Send DSA account enough LUSD (from Stability Pool) to close their Trove -// const extraLusdRequiredToCloseTrove = troveDebtBefore.sub( -// borrowAmount -// ); -// await helpers.sendToken( -// liquity.lusdToken, -// extraLusdRequiredToCloseTrove, -// contracts.STABILITY_POOL_ADDRESS, -// dsa.address -// ); -// const originalDsaLusdBalance = await liquity.lusdToken.balanceOf( -// dsa.address -// ); - -// expect( -// originalDsaLusdBalance, -// "DSA account should now hold the LUSD amount required to pay off the Trove debt" -// ).to.eq(troveDebtBefore); - -// const collateralWithdrawId = 1; - -// const closeTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "close", -// args: [collateralWithdrawId], -// }; - -// const withdrawEthSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [ -// helpers.ETH_ADDRESS, -// 0, // amount comes from the previous spell's setId -// dsa.address, -// collateralWithdrawId, -// 0, -// ], -// }; - -// await dsa -// .connect(userWallet) -// .cast( -// ...encodeSpells([closeTroveSpell, withdrawEthSpell]), -// userWallet.address -// ); - -// const dsaEthBalance = await ethers.provider.getBalance(dsa.address); -// const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); - -// expect( -// troveCollateral, -// "Trove collateral should equal 0 after close" -// ).to.eq(0); - -// expect( -// dsaEthBalance, -// "DSA account should now hold the Trove's ETH collateral" -// ).to.eq(troveCollateralBefore); - -// expect( -// dsaLusdBalance, -// "DSA account should now hold the gas compensation amount of LUSD as it paid off the Trove debt" -// ).to.eq(helpers.LUSD_GAS_COMPENSATION); -// }); - -// it("returns Instadapp event name and data", async () => { -// const depositAmount = ethers.utils.parseEther("5"); -// const borrowAmount = ethers.utils.parseUnits("2000", 18); -// // Create a dummy Trove -// await helpers.createDsaTrove( -// dsa, -// userWallet, -// liquity, -// depositAmount, -// borrowAmount -// ); -// await helpers.sendToken( -// liquity.lusdToken, -// ethers.utils.parseUnits("2500", 18), -// contracts.STABILITY_POOL_ADDRESS, -// dsa.address -// ); - -// const closeTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "close", -// args: [0], -// }; - -// const closeTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([closeTroveSpell]), userWallet.address); - -// const receipt = await closeTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256"], -// [dsa.address, 0] -// ); -// expect(castLogEvent.eventNames[0]).eq("LogClose(address,uint256)"); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("deposit()", () => { -// it("deposits ETH into a Trove", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// const topupAmount = ethers.utils.parseEther("1"); -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const depositEthSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "deposit", -// args: [topupAmount, upperHint, lowerHint, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([depositEthSpell]), userWallet.address, { -// value: topupAmount, -// }); - -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// const expectedTroveCollateral = troveCollateralBefore.add( -// topupAmount -// ); - -// expect( -// troveCollateral, -// `Trove collateral should have increased by ${topupAmount} ETH` -// ).to.eq(expectedTroveCollateral); -// }); - -// it("deposits using ETH gained from a previous spell", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// const topupAmount = ethers.utils.parseEther("1"); -// const depositId = 1; -// const depositEthSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "deposit", -// args: [helpers.ETH_ADDRESS, topupAmount, 0, depositId], -// }; - -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const depositEthToTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "deposit", -// args: [0, upperHint, lowerHint, depositId, 0], -// }; -// const spells = [depositEthSpell, depositEthToTroveSpell]; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address, { -// value: topupAmount, -// }); - -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// const expectedTroveCollateral = troveCollateralBefore.add( -// topupAmount -// ); - -// expect( -// troveCollateral, -// `Trove collateral should have increased by ${topupAmount} ETH` -// ).to.eq(expectedTroveCollateral); -// }); - -// it("returns Instadapp event name and data", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const topupAmount = ethers.utils.parseEther("1"); -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const depositEthSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "deposit", -// args: [topupAmount, upperHint, lowerHint, 0, 0], -// }; - -// const depositTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([depositEthSpell]), userWallet.address, { -// value: topupAmount, -// }); - -// const receipt = await depositTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256", "uint256", "uint256"], -// [dsa.address, topupAmount, 0, 0] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogDeposit(address,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("withdraw()", () => { -// it("withdraws ETH from a Trove", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const withdrawAmount = ethers.utils.parseEther("1"); -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const withdrawEthSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "withdraw", -// args: [withdrawAmount, upperHint, lowerHint, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([withdrawEthSpell]), userWallet.address); - -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const expectedTroveCollateral = troveCollateralBefore.sub( -// withdrawAmount -// ); - -// expect( -// troveCollateral, -// `Trove collateral should have decreased by ${withdrawAmount} ETH` -// ).to.eq(expectedTroveCollateral); -// }); - -// it("withdraws ETH from a Trove and stores the ETH for other spells to use", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const originalUserEthBalance = await ethers.provider.getBalance( -// userWallet.address -// ); - -// const withdrawAmount = ethers.utils.parseEther("1"); -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const withdrawId = 1; -// const withdrawEthFromTroveSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "withdraw", -// args: [withdrawAmount, upperHint, lowerHint, 0, withdrawId], -// }; - -// const withdrawEthSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [helpers.ETH_ADDRESS, 0, userWallet.address, withdrawId, 0], -// }; -// const spells = [withdrawEthFromTroveSpell, withdrawEthSpell]; -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address, { -// gasPrice: 0, // Remove gas costs so we can check balances have changed correctly -// }); - -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const expectedTroveCollateral = troveCollateralBefore.sub( -// withdrawAmount -// ); -// const userEthBalance = await ethers.provider.getBalance( -// userWallet.address -// ); - -// expect( -// troveCollateral, -// `Trove collateral should have decreased by ${withdrawAmount} ETH` -// ).to.eq(expectedTroveCollateral); - -// expect( -// userEthBalance, -// `User ETH balance should have increased by ${withdrawAmount} ETH` -// ).to.eq(originalUserEthBalance.add(withdrawAmount)); -// }); - -// it("returns Instadapp event name and data", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const withdrawAmount = ethers.utils.parseEther("1"); -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const withdrawEthSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "withdraw", -// args: [withdrawAmount, upperHint, lowerHint, 0, 0], -// }; - -// const withdrawTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([withdrawEthSpell]), userWallet.address); - -// const receipt = await withdrawTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256", "uint256", "uint256"], -// [dsa.address, withdrawAmount, 0, 0] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogWithdraw(address,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("borrow()", () => { -// it("borrows LUSD from a Trove", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); - -// const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee -// const borrowSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "borrow", -// args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, 0, 0], -// }; - -// // Borrow more LUSD from the Trove -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([borrowSpell]), userWallet.address); - -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const expectedTroveDebt = troveDebtBefore.add(borrowAmount); - -// expect( -// troveDebt, -// `Trove debt should have increased by at least ${borrowAmount} ETH` -// ).to.gte(expectedTroveDebt); -// }); - -// it("borrows LUSD from a Trove and stores the LUSD for other spells to use", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); - -// const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee -// const borrowId = 1; -// const borrowSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "borrow", -// args: [ -// maxFeePercentage, -// borrowAmount, -// upperHint, -// lowerHint, -// 0, -// borrowId, -// ], -// }; -// const withdrawSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [ -// liquity.lusdToken.address, -// 0, -// userWallet.address, -// borrowId, -// 0, -// ], -// }; -// const spells = [borrowSpell, withdrawSpell]; - -// // Borrow more LUSD from the Trove -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address); - -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const expectedTroveDebt = troveDebtBefore.add(borrowAmount); -// const userLusdBalance = await liquity.lusdToken.balanceOf( -// userWallet.address -// ); - -// expect( -// troveDebt, -// `Trove debt should have increased by at least ${borrowAmount} ETH` -// ).to.gte(expectedTroveDebt); - -// expect( -// userLusdBalance, -// `User LUSD balance should equal the borrowed LUSD due to the second withdraw spell` -// ).eq(borrowAmount); -// }); - -// it("returns Instadapp event name and data", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee -// const borrowSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "borrow", -// args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, 0, 0], -// }; - -// const borrowTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([borrowSpell]), userWallet.address); - -// const receipt = await borrowTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256", "uint256", "uint256"], -// [dsa.address, borrowAmount, 0, 0] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogBorrow(address,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("repay()", () => { -// it("repays LUSD to a Trove", async () => { -// const depositAmount = ethers.utils.parseEther("5"); -// const borrowAmount = ethers.utils.parseUnits("2500", 18); - -// // Create a dummy Trove -// await helpers.createDsaTrove( -// dsa, -// userWallet, -// liquity, -// depositAmount, -// borrowAmount -// ); - -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// // DSA account is holding 2500 LUSD from opening a Trove, so we use some of that to repay -// const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD - -// const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( -// depositAmount, -// borrowAmount, -// liquity -// ); -// const repaySpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "repay", -// args: [repayAmount, upperHint, lowerHint, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([repaySpell]), userWallet.address); - -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const expectedTroveDebt = troveDebtBefore.sub(repayAmount); - -// expect( -// troveDebt, -// `Trove debt should have decreased by ${repayAmount} ETH` -// ).to.eq(expectedTroveDebt); -// }); - -// it("repays LUSD to a Trove using LUSD collected from a previous spell", async () => { -// const depositAmount = ethers.utils.parseEther("5"); -// const borrowAmount = ethers.utils.parseUnits("2500", 18); - -// // Create a dummy Trove -// await helpers.createDsaTrove( -// dsa, -// userWallet, -// liquity, -// depositAmount, -// borrowAmount -// ); - -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); - -// const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD -// const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( -// depositAmount, -// borrowAmount, -// liquity -// ); - -// // Drain the DSA's LUSD balance so that we ensure we are repaying using LUSD from a previous spell -// await helpers.sendToken( -// liquity.lusdToken, -// borrowAmount, -// dsa.address, -// userWallet.address -// ); - -// // Allow DSA to spend user's LUSD -// await liquity.lusdToken -// .connect(userWallet) -// .approve(dsa.address, repayAmount); - -// const lusdDepositId = 1; -// const depositSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "deposit", -// args: [liquity.lusdToken.address, repayAmount, 0, lusdDepositId], -// }; -// const borrowSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "repay", -// args: [0, upperHint, lowerHint, lusdDepositId, 0], -// }; - -// const spells = [depositSpell, borrowSpell]; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address); - -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const expectedTroveDebt = troveDebtBefore.sub(repayAmount); - -// expect( -// troveDebt, -// `Trove debt should have decreased by ${repayAmount} ETH` -// ).to.eq(expectedTroveDebt); -// }); - -// it("returns Instadapp event name and data", async () => { -// // Create a dummy Trove -// const depositAmount = ethers.utils.parseEther("5"); -// const borrowAmount = ethers.utils.parseUnits("2500", 18); -// await helpers.createDsaTrove( -// dsa, -// userWallet, -// liquity, -// depositAmount, -// borrowAmount -// ); - -// const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD -// const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( -// depositAmount, -// borrowAmount, -// liquity -// ); - -// const borrowSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "repay", -// args: [repayAmount, upperHint, lowerHint, 0, 0], -// }; - -// const repayTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([borrowSpell]), userWallet.address, { -// value: repayAmount, -// }); - -// const receipt = await repayTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256", "uint256", "uint256"], -// [dsa.address, repayAmount, 0, 0] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogRepay(address,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("adjust()", () => { -// it("adjusts a Trove: deposit ETH and borrow LUSD", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const depositAmount = ethers.utils.parseEther("1"); // 1 ETH -// const borrowAmount = ethers.utils.parseUnits("500", 18); // 500 LUSD -// const withdrawAmount = 0; -// const repayAmount = 0; -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - -// const adjustSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "adjust", -// args: [ -// maxFeePercentage, -// depositAmount, -// withdrawAmount, -// borrowAmount, -// repayAmount, -// upperHint, -// lowerHint, -// [0, 0, 0, 0], -// [0, 0, 0, 0], -// ], -// }; - -// // Adjust Trove by depositing ETH and borrowing LUSD -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([adjustSpell]), userWallet.address, { -// value: depositAmount, -// gasLimit: helpers.MAX_GAS, -// }); - -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const expectedTroveColl = troveCollateralBefore.add(depositAmount); -// const expectedTroveDebt = troveDebtBefore.add(borrowAmount); - -// expect( -// troveCollateral, -// `Trove collateral should have increased by ${depositAmount} ETH` -// ).to.eq(expectedTroveColl); - -// expect( -// troveDebt, -// `Trove debt should have increased by at least ${borrowAmount} ETH` -// ).to.gte(expectedTroveDebt); -// }); - -// it("adjusts a Trove: withdraw ETH and repay LUSD", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const depositAmount = 0; -// const borrowAmount = 0; -// const withdrawAmount = ethers.utils.parseEther("1"); // 1 ETH; -// const repayAmount = ethers.utils.parseUnits("10", 18); // 10 LUSD; -// const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( -// troveCollateralBefore.sub(withdrawAmount), -// troveDebtBefore.sub(repayAmount), -// liquity -// ); -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - -// const adjustSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "adjust", -// args: [ -// maxFeePercentage, -// depositAmount, -// withdrawAmount, -// borrowAmount, -// repayAmount, -// upperHint, -// lowerHint, -// [0, 0, 0, 0], -// [0, 0, 0, 0], -// ], -// }; - -// // Adjust Trove by withdrawing ETH and repaying LUSD -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([adjustSpell]), userWallet.address, { -// value: depositAmount, -// gasLimit: helpers.MAX_GAS, -// }); - -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const expectedTroveColl = troveCollateralBefore.sub(withdrawAmount); -// const expectedTroveDebt = troveDebtBefore.sub(repayAmount); - -// expect( -// troveCollateral, -// `Trove collateral should have increased by ${depositAmount} ETH` -// ).to.eq(expectedTroveColl); - -// expect( -// troveDebt, -// `Trove debt should have decreased by at least ${repayAmount} LUSD` -// ).to.gte(expectedTroveDebt); -// }); - -// it("adjusts a Trove: deposit ETH and repay LUSD using previous spells", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const troveDebtBefore = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const depositAmount = ethers.utils.parseEther("1"); // 1 ETH -// const borrowAmount = 0; -// const withdrawAmount = 0; -// const repayAmount = ethers.utils.parseUnits("10", 18); // 10 lUSD -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - -// const ethDepositId = 1; -// const lusdRepayId = 2; - -// const depositEthSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "deposit", -// args: [helpers.ETH_ADDRESS, depositAmount, 0, ethDepositId], -// }; - -// const depositLusdSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "deposit", -// args: [liquity.lusdToken.address, repayAmount, 0, lusdRepayId], -// }; - -// const adjustSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "adjust", -// args: [ -// maxFeePercentage, -// 0, // Deposit amount comes from a previous spell's storage slot -// withdrawAmount, -// borrowAmount, -// 0, // Repay amount comes from a previous spell's storage slot -// upperHint, -// lowerHint, -// [ethDepositId, 0, 0, lusdRepayId], -// [0, 0, 0, 0], -// ], -// }; -// const spells = [depositEthSpell, depositLusdSpell, adjustSpell]; - -// // Send user some LUSD so they can repay -// await helpers.sendToken( -// liquity.lusdToken, -// repayAmount, -// helpers.JUSTIN_SUN_ADDRESS, -// userWallet.address -// ); - -// // Allow DSA to spend user's LUSD -// await liquity.lusdToken -// .connect(userWallet) -// .approve(dsa.address, repayAmount); - -// // Adjust Trove by depositing ETH and repaying LUSD -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address, { -// value: depositAmount, -// gasLimit: helpers.MAX_GAS, -// }); - -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const troveDebt = await liquity.troveManager.getTroveDebt( -// dsa.address -// ); -// const expectedTroveColl = troveCollateralBefore.add(depositAmount); -// const expectedTroveDebt = troveDebtBefore.sub(repayAmount); - -// expect( -// troveCollateral, -// `Trove collateral should have increased by ${depositAmount} ETH` -// ).to.eq(expectedTroveColl); - -// expect( -// troveDebt, -// `Trove debt (${troveDebtBefore}) should have decreased by at least ${repayAmount} LUSD` -// ).to.eq(expectedTroveDebt); -// }); - -// it("adjusts a Trove: withdraw ETH, borrow LUSD, and store the amounts for other spells", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const userEthBalanceBefore = await ethers.provider.getBalance( -// userWallet.address -// ); -// const userLusdBalanceBefore = await liquity.lusdToken.balanceOf( -// userWallet.address -// ); - -// const depositAmount = 0; -// const borrowAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD -// const withdrawAmount = ethers.utils.parseEther("1"); // 1 ETH -// const repayAmount = 0; -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - -// const ethWithdrawId = 1; -// const lusdBorrowId = 2; - -// const adjustSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "adjust", -// args: [ -// maxFeePercentage, -// depositAmount, -// withdrawAmount, -// borrowAmount, -// repayAmount, -// upperHint, -// lowerHint, -// [0, 0, 0, 0], -// [0, ethWithdrawId, lusdBorrowId, 0], -// ], -// }; - -// const withdrawEthSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [ -// helpers.ETH_ADDRESS, -// 0, -// userWallet.address, -// ethWithdrawId, -// 0, -// ], -// }; - -// const withdrawLusdSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [ -// liquity.lusdToken.address, -// 0, -// userWallet.address, -// lusdBorrowId, -// 0, -// ], -// }; - -// const spells = [adjustSpell, withdrawEthSpell, withdrawLusdSpell]; - -// // Adjust Trove by withdrawing ETH and borrowing LUSD -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address, { -// gasLimit: helpers.MAX_GAS, -// gasPrice: 0, -// }); - -// const userEthBalanceAfter = await ethers.provider.getBalance( -// userWallet.address -// ); -// const userLusdBalanceAfter = await liquity.lusdToken.balanceOf( -// userWallet.address -// ); -// expect(userEthBalanceAfter).eq( -// userEthBalanceBefore.add(withdrawAmount) -// ); -// expect(userLusdBalanceAfter).eq( -// userLusdBalanceBefore.add(borrowAmount) -// ); -// }); - -// it("returns Instadapp event name and data", async () => { -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// const depositAmount = ethers.utils.parseEther("1"); // 1 ETH -// const borrowAmount = ethers.utils.parseUnits("500", 18); // 500 LUSD -// const withdrawAmount = 0; -// const repayAmount = 0; -// const upperHint = ethers.constants.AddressZero; -// const lowerHint = ethers.constants.AddressZero; -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - -// const adjustSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "adjust", -// args: [ -// maxFeePercentage, -// depositAmount, -// withdrawAmount, -// borrowAmount, -// repayAmount, -// upperHint, -// lowerHint, -// [0, 0, 0, 0], -// [0, 0, 0, 0], -// ], -// }; - -// const adjustTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([adjustSpell]), userWallet.address, { -// value: depositAmount, -// gasLimit: helpers.MAX_GAS, -// }); - -// const receipt = await adjustTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// [ -// "address", -// "uint256", -// "uint256", -// "uint256", -// "uint256", -// "uint256", -// "uint256[]", -// "uint256[]", -// ], -// [ -// dsa.address, -// maxFeePercentage, -// depositAmount, -// withdrawAmount, -// borrowAmount, -// repayAmount, -// [0, 0, 0, 0], -// [0, 0, 0, 0], -// ] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogAdjust(address,uint256,uint256,uint256,uint256,uint256,uint256[],uint256[])" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("claimCollateralFromRedemption()", () => { -// it("claims collateral from a redeemed Trove", async () => { -// // Create a low collateralized Trove -// const depositAmount = ethers.utils.parseEther("1.5"); -// const borrowAmount = ethers.utils.parseUnits("2500", 18); - -// await helpers.createDsaTrove( -// dsa, -// userWallet, -// liquity, -// depositAmount, -// borrowAmount -// ); - -// // Redeem lots of LUSD to cause the Trove to become redeemed -// const redeemAmount = ethers.utils.parseUnits("10000000", 18); -// await helpers.sendToken( -// liquity.lusdToken, -// redeemAmount, -// contracts.STABILITY_POOL_ADDRESS, -// userWallet.address -// ); -// const { -// partialRedemptionHintNicr, -// firstRedemptionHint, -// upperHint, -// lowerHint, -// } = await helpers.getRedemptionHints(redeemAmount, liquity); -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - -// await liquity.troveManager -// .connect(userWallet) -// .redeemCollateral( -// redeemAmount, -// firstRedemptionHint, -// upperHint, -// lowerHint, -// partialRedemptionHintNicr, -// 0, -// maxFeePercentage, -// { -// gasLimit: helpers.MAX_GAS, // permit max gas -// } -// ); - -// const remainingEthCollateral = await liquity.collSurplus.getCollateral( -// dsa.address -// ); - -// // Claim the remaining collateral from the redeemed Trove -// const claimCollateralFromRedemptionSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "claimCollateralFromRedemption", -// args: [0], -// }; - -// await dsa -// .connect(userWallet) -// .cast( -// ...encodeSpells([claimCollateralFromRedemptionSpell]), -// userWallet.address -// ); - -// const ethBalance = await ethers.provider.getBalance(dsa.address); - -// expect(ethBalance).to.eq(remainingEthCollateral); -// }); - -// it("returns Instadapp event name and data", async () => { -// // Create a low collateralized Trove -// const depositAmount = ethers.utils.parseEther("1.5"); -// const borrowAmount = ethers.utils.parseUnits("2500", 18); - -// await helpers.createDsaTrove( -// dsa, -// userWallet, -// liquity, -// depositAmount, -// borrowAmount -// ); - -// // Redeem lots of LUSD to cause the Trove to become redeemed -// const redeemAmount = ethers.utils.parseUnits("10000000", 18); -// const setId = 0; -// await helpers.sendToken( -// liquity.lusdToken, -// redeemAmount, -// contracts.STABILITY_POOL_ADDRESS, -// userWallet.address -// ); -// const { -// partialRedemptionHintNicr, -// firstRedemptionHint, -// upperHint, -// lowerHint, -// } = await helpers.getRedemptionHints(redeemAmount, liquity); -// const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - -// await liquity.troveManager -// .connect(userWallet) -// .redeemCollateral( -// redeemAmount, -// firstRedemptionHint, -// upperHint, -// lowerHint, -// partialRedemptionHintNicr, -// 0, -// maxFeePercentage, -// { -// gasLimit: helpers.MAX_GAS, // permit max gas -// } -// ); -// const claimAmount = await liquity.collSurplus.getCollateral( -// dsa.address -// ); - -// const claimCollateralFromRedemptionSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "claimCollateralFromRedemption", -// args: [setId], -// }; - -// const claimTx = await dsa -// .connect(userWallet) -// .cast( -// ...encodeSpells([claimCollateralFromRedemptionSpell]), -// userWallet.address -// ); - -// const receipt = await claimTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256", "uint256"], -// [dsa.address, claimAmount, setId] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogClaimCollateralFromRedemption(address,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); -// }); - -// describe("Stability Pool", () => { -// describe("stabilityDeposit()", () => { -// it("deposits into Stability Pool", async () => { -// const amount = ethers.utils.parseUnits("100", 18); -// const frontendTag = ethers.constants.AddressZero; - -// await helpers.sendToken( -// liquity.lusdToken, -// amount, -// contracts.STABILITY_POOL_ADDRESS, -// dsa.address -// ); - -// const stabilityDepositSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityDeposit", -// args: [amount, frontendTag, 0, 0, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); - -// const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( -// dsa.address -// ); -// expect(depositedAmount).to.eq(amount); -// }); - -// it("deposits into Stability Pool using LUSD collected from a previous spell", async () => { -// const amount = ethers.utils.parseUnits("100", 18); -// const frontendTag = ethers.constants.AddressZero; - -// await helpers.sendToken( -// liquity.lusdToken, -// amount, -// contracts.STABILITY_POOL_ADDRESS, -// userWallet.address -// ); -// const lusdDepositId = 1; - -// const depositLusdSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "deposit", -// args: [liquity.lusdToken.address, amount, 0, lusdDepositId], -// }; -// const stabilityDepositSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityDeposit", -// args: [0, frontendTag, lusdDepositId, 0, 0, 0], -// }; -// const spells = [depositLusdSpell, stabilityDepositSpell]; - -// // Allow DSA to spend user's LUSD -// await liquity.lusdToken -// .connect(userWallet) -// .approve(dsa.address, amount); - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address); - -// const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( -// dsa.address -// ); -// expect(depositedAmount).to.eq(amount); -// }); - -// it("returns Instadapp event name and data", async () => { -// const amount = ethers.utils.parseUnits("100", 18); -// const halfAmount = amount.div(2); -// const frontendTag = ethers.constants.AddressZero; -// const getDepositId = 0; -// const setDepositId = 0; -// const setEthGainId = 0; -// const setLqtyGainId = 0; - -// await helpers.sendToken( -// liquity.lusdToken, -// amount, -// contracts.STABILITY_POOL_ADDRESS, -// dsa.address -// ); - -// const stabilityDepositSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityDeposit", -// args: [ -// halfAmount, -// frontendTag, -// getDepositId, -// setDepositId, -// setEthGainId, -// setLqtyGainId, -// ], -// }; - -// // Create a Stability deposit for this DSA -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); - -// // Liquidate a Trove to cause an ETH gain -// await liquity.troveManager.connect(userWallet).liquidateTroves(1, { -// gasLimit: helpers.MAX_GAS, -// }); - -// // Fast forward in time so we have an LQTY gain -// await provider.send("evm_increaseTime", [600]); -// await provider.send("evm_mine"); - -// // Create a Stability Pool deposit with a differen DSA so that LQTY gains can be calculated -// // See: https://github.com/liquity/dev/#lqty-reward-events-and-payouts -// const tempDsa = await buildDSAv2(userWallet.address); -// await helpers.sendToken( -// liquity.lusdToken, -// amount, -// contracts.STABILITY_POOL_ADDRESS, -// tempDsa.address -// ); -// await tempDsa -// .connect(userWallet) -// .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); - -// const ethGain = await liquity.stabilityPool.getDepositorETHGain( -// dsa.address -// ); -// const lqtyGain = await liquity.stabilityPool.getDepositorLQTYGain( -// dsa.address -// ); - -// // Top up the user's deposit so that we can track their ETH and LQTY gain -// const depositAgainTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); - -// const receipt = await depositAgainTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// [ -// "address", -// "uint256", -// "uint256", -// "uint256", -// "address", -// "uint256", -// "uint256", -// "uint256", -// "uint256", -// ], -// [ -// dsa.address, -// halfAmount, -// ethGain, -// lqtyGain, -// frontendTag, -// getDepositId, -// setDepositId, -// setEthGainId, -// setLqtyGainId, -// ] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogStabilityDeposit(address,uint256,uint256,uint256,address,uint256,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("stabilityWithdraw()", () => { -// it("withdraws from Stability Pool", async () => { -// // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool -// [liquity, dsa] = await helpers.resetInitialState( -// userWallet.address, -// contracts -// ); - -// // The current block number has liquidatable Troves. -// // Remove them otherwise Stability Pool withdrawals are disabled -// await liquity.troveManager.connect(userWallet).liquidateTroves(90, { -// gasLimit: helpers.MAX_GAS, -// }); - -// const amount = ethers.utils.parseUnits("100", 18); -// const frontendTag = ethers.constants.AddressZero; - -// await helpers.sendToken( -// liquity.lusdToken, -// amount, -// contracts.STABILITY_POOL_ADDRESS, -// dsa.address -// ); - -// const stabilityDepositSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityDeposit", -// args: [amount, frontendTag, 0, 0, 0, 0], -// }; - -// // Withdraw half of the deposit -// const stabilityWithdrawSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityWithdraw", -// args: [amount.div(2), 0, 0, 0, 0], -// }; -// const spells = [stabilityDepositSpell, stabilityWithdrawSpell]; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address); - -// const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( -// dsa.address -// ); -// const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); - -// expect(depositedAmount).to.eq(amount.div(2)); -// expect(dsaLusdBalance).to.eq(amount.div(2)); -// }); - -// it("withdraws from Stability Pool and stores the LUSD for other spells", async () => { -// // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool -// [liquity, dsa] = await helpers.resetInitialState( -// userWallet.address, -// contracts -// ); - -// // The current block number has liquidatable Troves. -// // Remove them otherwise Stability Pool withdrawals are disabled -// await liquity.troveManager.connect(userWallet).liquidateTroves(90, { -// gasLimit: helpers.MAX_GAS, -// }); -// const amount = ethers.utils.parseUnits("100", 18); -// const frontendTag = ethers.constants.AddressZero; -// const withdrawId = 1; - -// await helpers.sendToken( -// liquity.lusdToken, -// amount, -// contracts.STABILITY_POOL_ADDRESS, -// dsa.address -// ); - -// const stabilityDepositSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityDeposit", -// args: [amount, frontendTag, 0, 0, 0, 0], -// }; - -// // Withdraw half of the deposit -// const stabilityWithdrawSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityWithdraw", -// args: [amount.div(2), 0, 0, 0, withdrawId], -// }; - -// const withdrawLusdSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [ -// liquity.lusdToken.address, -// 0, -// userWallet.address, -// withdrawId, -// 0, -// ], -// }; - -// const spells = [ -// stabilityDepositSpell, -// stabilityWithdrawSpell, -// withdrawLusdSpell, -// ]; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address); - -// const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( -// dsa.address -// ); -// const walletLusdBalance = await liquity.lusdToken.balanceOf( -// dsa.address -// ); - -// expect(depositedAmount).to.eq(amount.div(2)); -// expect(walletLusdBalance).to.eq(amount.div(2)); -// }); - -// it("returns Instadapp event name and data", async () => { -// // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool -// [liquity, dsa] = await helpers.resetInitialState( -// userWallet.address, -// contracts -// ); - -// const amount = ethers.utils.parseUnits("100", 18); -// const frontendTag = ethers.constants.AddressZero; - -// await helpers.sendToken( -// liquity.lusdToken, -// amount, -// contracts.STABILITY_POOL_ADDRESS, -// dsa.address -// ); - -// const stabilityDepositSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityDeposit", -// args: [amount, frontendTag, 0, 0, 0, 0], -// }; - -// // Withdraw half of the deposit -// const withdrawAmount = amount.div(2); -// const getWithdrawId = 0; -// const setWithdrawId = 0; -// const setEthGainId = 0; -// const setLqtyGainId = 0; - -// // Create a Stability Pool deposit -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); - -// // The current block number has liquidatable Troves. -// // Remove them otherwise Stability Pool withdrawals are disabled -// await liquity.troveManager.connect(userWallet).liquidateTroves(90, { -// gasLimit: helpers.MAX_GAS, -// }); - -// // Fast forward in time so we have an LQTY gain -// await provider.send("evm_increaseTime", [600]); -// await provider.send("evm_mine"); - -// // Create another Stability Pool deposit so that LQTY gains are realized -// // See: https://github.com/liquity/dev/#lqty-reward-events-and-payouts -// const tempDsa = await buildDSAv2(userWallet.address); -// await helpers.sendToken( -// liquity.lusdToken, -// amount, -// contracts.STABILITY_POOL_ADDRESS, -// tempDsa.address -// ); -// await tempDsa -// .connect(userWallet) -// .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); - -// const ethGain = await liquity.stabilityPool.getDepositorETHGain( -// dsa.address -// ); -// const lqtyGain = await liquity.stabilityPool.getDepositorLQTYGain( -// dsa.address -// ); - -// const stabilityWithdrawSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityWithdraw", -// args: [ -// withdrawAmount, -// getWithdrawId, -// setWithdrawId, -// setEthGainId, -// setLqtyGainId, -// ], -// }; - -// const withdrawTx = await dsa -// .connect(userWallet) -// .cast( -// ...encodeSpells([stabilityWithdrawSpell]), -// userWallet.address -// ); - -// const receipt = await withdrawTx.wait(); -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// [ -// "address", -// "uint256", -// "uint256", -// "uint256", -// "uint256", -// "uint256", -// "uint256", -// "uint256", -// ], -// [ -// dsa.address, -// withdrawAmount, -// ethGain, -// lqtyGain, -// getWithdrawId, -// setWithdrawId, -// setEthGainId, -// setLqtyGainId, -// ] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogStabilityWithdraw(address,uint256,uint256,uint256,uint256,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("stabilityMoveEthGainToTrove()", () => { -// beforeEach(async () => { -// // Start these test from fresh so that we definitely have a liquidatable Trove within this block -// [liquity, dsa] = await helpers.resetInitialState( -// userWallet.address, -// contracts -// ); -// }); - -// it("moves ETH gain from Stability Pool to Trove", async () => { -// // Create a DSA owned Trove to capture ETH liquidation gains -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); -// const troveCollateralBefore = await liquity.troveManager.getTroveColl( -// dsa.address -// ); - -// // Create a Stability Deposit using the Trove's borrowed LUSD -// const amount = ethers.utils.parseUnits("100", 18); -// const frontendTag = ethers.constants.AddressZero; -// const stabilityDepositSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityDeposit", -// args: [amount, frontendTag, 0, 0, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); - -// // Liquidate a Trove to create an ETH gain for the new DSA Trove -// await liquity.troveManager -// .connect(userWallet) -// .liquidate(helpers.LIQUIDATABLE_TROVE_ADDRESS, { -// gasLimit: helpers.MAX_GAS, // permit max gas -// }); - -// const ethGainFromLiquidation = await liquity.stabilityPool.getDepositorETHGain( -// dsa.address -// ); - -// // Move ETH gain to Trove -// const moveEthGainSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityMoveEthGainToTrove", -// args: [ethers.constants.AddressZero, ethers.constants.AddressZero], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([moveEthGainSpell]), userWallet.address); - -// const ethGainAfterMove = await liquity.stabilityPool.getDepositorETHGain( -// dsa.address -// ); -// const troveCollateral = await liquity.troveManager.getTroveColl( -// dsa.address -// ); -// const expectedTroveCollateral = troveCollateralBefore.add( -// ethGainFromLiquidation -// ); -// expect(ethGainAfterMove).to.eq(0); -// expect(troveCollateral).to.eq(expectedTroveCollateral); -// }); - -// it("returns Instadapp event name and data", async () => { -// // Create a DSA owned Trove to capture ETH liquidation gains -// // Create a dummy Trove -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// // Create a Stability Deposit using the Trove's borrowed LUSD -// const amount = ethers.utils.parseUnits("100", 18); -// const frontendTag = ethers.constants.AddressZero; -// const stabilityDepositSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityDeposit", -// args: [amount, frontendTag, 0, 0, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); - -// // Liquidate a Trove to create an ETH gain for the new DSA Trove -// await liquity.troveManager -// .connect(userWallet) -// .liquidate(helpers.LIQUIDATABLE_TROVE_ADDRESS, { -// gasLimit: helpers.MAX_GAS, // permit max gas -// }); - -// const ethGainFromLiquidation = await liquity.stabilityPool.getDepositorETHGain( -// dsa.address -// ); - -// // Move ETH gain to Trove -// const moveEthGainSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stabilityMoveEthGainToTrove", -// args: [ethers.constants.AddressZero, ethers.constants.AddressZero], -// }; - -// const moveEthGainTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([moveEthGainSpell]), userWallet.address); - -// const receipt = await moveEthGainTx.wait(); - -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256"], -// [dsa.address, ethGainFromLiquidation] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogStabilityMoveEthGainToTrove(address,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); -// }); - -// describe("Staking", () => { -// describe("stake()", () => { -// it("stakes LQTY", async () => { -// const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( -// contracts.STAKING_ADDRESS -// ); - -// const amount = ethers.utils.parseUnits("1", 18); -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// dsa.address -// ); - -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [amount, 0, 0, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stakeSpell]), userWallet.address); - -// const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); -// expect(lqtyBalance).to.eq(0); - -// const totalStakingBalance = await liquity.lqtyToken.balanceOf( -// contracts.STAKING_ADDRESS -// ); -// expect(totalStakingBalance).to.eq( -// totalStakingBalanceBefore.add(amount) -// ); -// }); - -// it("stakes LQTY using LQTY obtained from a previous spell", async () => { -// const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( -// contracts.STAKING_ADDRESS -// ); - -// const amount = ethers.utils.parseUnits("1", 18); -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// userWallet.address -// ); - -// const lqtyDepositId = 1; -// const depositSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "deposit", -// args: [liquity.lqtyToken.address, amount, 0, lqtyDepositId], -// }; -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [0, lqtyDepositId, 0, 0, 0], -// }; -// const spells = [depositSpell, stakeSpell]; - -// // Allow DSA to spend user's LQTY -// await liquity.lqtyToken -// .connect(userWallet) -// .approve(dsa.address, amount); - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address); - -// const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); -// expect(lqtyBalance).to.eq(0); - -// const totalStakingBalance = await liquity.lqtyToken.balanceOf( -// contracts.STAKING_ADDRESS -// ); -// expect(totalStakingBalance).to.eq( -// totalStakingBalanceBefore.add(amount) -// ); -// }); - -// it("returns Instadapp event name and data", async () => { -// const amount = ethers.utils.parseUnits("1", 18); -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// dsa.address -// ); - -// const getStakeId = 0; -// const setStakeId = 0; -// const setEthGainId = 0; -// const setLusdGainId = 0; -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [amount, getStakeId, setStakeId, setEthGainId, setLusdGainId], -// }; - -// const stakeTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stakeSpell]), userWallet.address); - -// const receipt = await stakeTx.wait(); - -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256", "uint256", "uint256", "uint256", "uint256"], -// [ -// dsa.address, -// amount, -// getStakeId, -// setStakeId, -// setEthGainId, -// setLusdGainId, -// ] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogStake(address,uint256,uint256,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("unstake()", () => { -// it("unstakes LQTY", async () => { -// const amount = ethers.utils.parseUnits("1", 18); -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// dsa.address -// ); - -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [amount, 0, 0, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stakeSpell]), userWallet.address); - -// const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( -// contracts.STAKING_ADDRESS -// ); -// const unstakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "unstake", -// args: [amount, 0, 0, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([unstakeSpell]), userWallet.address); - -// const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); -// expect(lqtyBalance).to.eq(amount); - -// const totalStakingBalance = await liquity.lqtyToken.balanceOf( -// contracts.STAKING_ADDRESS -// ); -// expect(totalStakingBalance).to.eq( -// totalStakingBalanceBefore.sub(amount) -// ); -// }); - -// it("unstakes LQTY and stores the LQTY for other spells", async () => { -// const amount = ethers.utils.parseUnits("1", 18); -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// dsa.address -// ); - -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [amount, 0, 0, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stakeSpell]), userWallet.address); - -// const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( -// contracts.STAKING_ADDRESS -// ); -// const withdrawId = 1; -// const unstakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "unstake", -// args: [amount, 0, withdrawId, 0, 0], -// }; - -// const withdrawLqtySpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [ -// liquity.lqtyToken.address, -// 0, -// userWallet.address, -// withdrawId, -// 0, -// ], -// }; -// const spells = [unstakeSpell, withdrawLqtySpell]; -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address); - -// const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); -// const totalStakingBalance = await liquity.lqtyToken.balanceOf( -// contracts.STAKING_ADDRESS -// ); -// const userLqtyBalance = await liquity.lqtyToken.balanceOf( -// userWallet.address -// ); -// expect(lqtyBalance).to.eq(0); -// expect(totalStakingBalance).to.eq( -// totalStakingBalanceBefore.sub(amount) -// ); -// expect(userLqtyBalance).to.eq(amount); -// }); - -// it("returns Instadapp event name and data", async () => { -// const amount = ethers.utils.parseUnits("1", 18); -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// dsa.address -// ); - -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [amount, 0, 0, 0, 0], -// }; - -// await dsa -// .connect(userWallet) -// .cast(...encodeSpells([stakeSpell]), userWallet.address); - -// const getUnstakeId = 0; -// const setUnstakeId = 0; -// const setEthGainId = 0; -// const setLusdGainId = 0; -// const unstakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "unstake", -// args: [ -// amount, -// getUnstakeId, -// setUnstakeId, -// setEthGainId, -// setLusdGainId, -// ], -// }; - -// const unstakeTx = await dsa -// .connect(userWallet) -// .cast(...encodeSpells([unstakeSpell]), userWallet.address); - -// const receipt = await unstakeTx.wait(); - -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256", "uint256", "uint256", "uint256", "uint256"], -// [ -// dsa.address, -// amount, -// getUnstakeId, -// setUnstakeId, -// setEthGainId, -// setLusdGainId, -// ] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogUnstake(address,uint256,uint256,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); - -// describe("claimStakingGains()", () => { -// it("claims gains from staking", async () => { -// const stakerDsa = await buildDSAv2(userWallet.address); -// const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY - -// // Stake lots of LQTY -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// stakerDsa.address -// ); -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [amount, 0, 0, 0, 0], -// }; -// await stakerDsa -// .connect(userWallet) -// .cast(...encodeSpells([stakeSpell]), userWallet.address); - -// // Open a Trove to cause an ETH issuance gain for stakers -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// // Redeem some ETH to cause an LUSD redemption gain for stakers -// await helpers.redeem( -// ethers.utils.parseUnits("1000", 18), -// contracts.STABILITY_POOL_ADDRESS, -// userWallet, -// liquity -// ); - -// const setEthGainId = 0; -// const setLusdGainId = 0; -// const ethGain = await liquity.staking.getPendingETHGain( -// stakerDsa.address -// ); -// const lusdGain = await liquity.staking.getPendingLUSDGain( -// stakerDsa.address -// ); - -// const claimStakingGainsSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "claimStakingGains", -// args: [setEthGainId, setLusdGainId], -// }; - -// const ethBalanceBefore = await ethers.provider.getBalance( -// stakerDsa.address -// ); - -// // Claim gains -// await stakerDsa -// .connect(userWallet) -// .cast( -// ...encodeSpells([claimStakingGainsSpell]), -// userWallet.address -// ); - -// const ethBalanceAfter = await ethers.provider.getBalance( -// stakerDsa.address -// ); -// const lusdBalanceAfter = await liquity.lusdToken.balanceOf( -// stakerDsa.address -// ); -// expect(ethBalanceAfter).to.eq(ethBalanceBefore.add(ethGain)); -// expect(lusdBalanceAfter).to.eq(lusdGain); -// }); - -// it("claims gains from staking and stores them for other spells", async () => { -// const stakerDsa = await buildDSAv2(userWallet.address); -// const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY - -// // Stake lots of LQTY -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// stakerDsa.address -// ); -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [amount, 0, 0, 0, 0], -// }; -// await stakerDsa -// .connect(userWallet) -// .cast(...encodeSpells([stakeSpell]), userWallet.address); - -// // Open a Trove to cause an ETH issuance gain for stakers -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// // Redeem some ETH to cause an LUSD redemption gain for stakers -// await helpers.redeem( -// ethers.utils.parseUnits("1000", 18), -// contracts.STABILITY_POOL_ADDRESS, -// userWallet, -// liquity -// ); - -// const ethGain = await liquity.staking.getPendingETHGain( -// stakerDsa.address -// ); -// const lusdGain = await liquity.staking.getPendingLUSDGain( -// stakerDsa.address -// ); -// const lusdBalanceBefore = await liquity.lusdToken.balanceOf( -// userWallet.address -// ); -// const ethBalanceBefore = await ethers.provider.getBalance( -// userWallet.address -// ); -// const ethGainId = 111; -// const lusdGainId = 222; - -// const claimStakingGainsSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "claimStakingGains", -// args: [ethGainId, lusdGainId], -// }; - -// const withdrawEthSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [helpers.ETH_ADDRESS, 0, userWallet.address, ethGainId, 0], -// }; - -// const withdrawLusdSpell = { -// connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, -// method: "withdraw", -// args: [ -// liquity.lusdToken.address, -// 0, -// userWallet.address, -// lusdGainId, -// 0, -// ], -// }; - -// const spells = [ -// claimStakingGainsSpell, -// withdrawEthSpell, -// withdrawLusdSpell, -// ]; - -// // Claim gains -// await stakerDsa -// .connect(userWallet) -// .cast(...encodeSpells(spells), userWallet.address, { -// gasPrice: 0, -// }); - -// const ethBalanceAfter = await ethers.provider.getBalance( -// userWallet.address -// ); -// const lusdBalanceAfter = await liquity.lusdToken.balanceOf( -// userWallet.address -// ); - -// expect( -// ethBalanceAfter, -// "User's ETH balance should have increased by the issuance gain from staking" -// ).to.eq(ethBalanceBefore.add(ethGain)); -// expect( -// lusdBalanceAfter, -// "User's LUSD balance should have increased by the redemption gain from staking" -// ).to.eq(lusdBalanceBefore.add(lusdGain)); -// }); - -// it("returns Instadapp event name and data", async () => { -// const stakerDsa = await buildDSAv2(userWallet.address); -// const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY - -// // Stake lots of LQTY -// await helpers.sendToken( -// liquity.lqtyToken, -// amount, -// helpers.JUSTIN_SUN_ADDRESS, -// stakerDsa.address -// ); -// const stakeSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "stake", -// args: [amount, 0, 0, 0, 0], -// }; -// await stakerDsa -// .connect(userWallet) -// .cast(...encodeSpells([stakeSpell]), userWallet.address); - -// // Open a Trove to cause an ETH issuance gain for stakers -// await helpers.createDsaTrove(dsa, userWallet, liquity); - -// // Redeem some ETH to cause an LUSD redemption gain for stakers -// await helpers.redeem( -// ethers.utils.parseUnits("1000", 18), -// contracts.STABILITY_POOL_ADDRESS, -// userWallet, -// liquity -// ); - -// const setEthGainId = 0; -// const setLusdGainId = 0; -// const ethGain = await liquity.staking.getPendingETHGain( -// stakerDsa.address -// ); -// const lusdGain = await liquity.staking.getPendingLUSDGain( -// stakerDsa.address -// ); - -// const claimStakingGainsSpell = { -// connector: helpers.LIQUITY_CONNECTOR, -// method: "claimStakingGains", -// args: [setEthGainId, setLusdGainId], -// }; - -// // Claim gains -// const claimGainsTx = await stakerDsa -// .connect(userWallet) -// .cast( -// ...encodeSpells([claimStakingGainsSpell]), -// userWallet.address -// ); - -// const receipt = await claimGainsTx.wait(); - -// const castLogEvent = receipt.events.find((e) => e.event === "LogCast") -// .args; -// const expectedEventParams = ethers.utils.defaultAbiCoder.encode( -// ["address", "uint256", "uint256", "uint256", "uint256"], -// [stakerDsa.address, ethGain, lusdGain, setEthGainId, setLusdGainId] -// ); -// expect(castLogEvent.eventNames[0]).eq( -// "LogClaimStakingGains(address,uint256,uint256,uint256,uint256)" -// ); -// expect(castLogEvent.eventParams[0]).eq(expectedEventParams); -// }); -// }); -// }); -// }); -// }); +import hre from "hardhat"; +import { expect } from "chai"; + +// Instadapp deployment and testing helpers +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells.js"; + +// Liquity smart contracts +import contracts from "./liquity.contracts"; + +// Liquity helpers +import helpers from "./liquity.helpers"; + +describe("Liquity", () => { + const { waffle, ethers } = hre; + const { provider } = waffle; + + // Waffle test account 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 (holds 1000 ETH) + const userWallet = provider.getWallets()[0]; + let dsa = null; + let liquity = null; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13300000, + }, + }, + ], + }); + liquity = await helpers.deployAndConnect(contracts, true); + expect(liquity.troveManager.address).to.exist; + expect(liquity.borrowerOperations.address).to.exist; + expect(liquity.stabilityPool.address).to.exist; + expect(liquity.lusdToken.address).to.exist; + expect(liquity.lqtyToken.address).to.exist; + expect(liquity.activePool.address).to.exist; + expect(liquity.priceFeed.address).to.exist; + expect(liquity.hintHelpers.address).to.exist; + expect(liquity.sortedTroves.address).to.exist; + expect(liquity.staking.address).to.exist; + }); + + beforeEach(async () => { + // Build a new DSA before each test so we start each test from the same default state + dsa = await buildDSAv2(userWallet.address); + expect(dsa.address).to.exist; + }); + + describe("Main (Connector)", () => { + describe("Trove", () => { + describe("open()", () => { + it("opens a Trove", async () => { + const depositAmount = ethers.utils.parseEther("5"); // 5 ETH + const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const originalUserBalance = await ethers.provider.getBalance( + userWallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsa.address + ); + + const openTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [0, 0], + [0, 0], + ], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([openTroveSpell]), userWallet.address, { + value: depositAmount, + gasPrice: 0, + }); + + const userBalance = await ethers.provider.getBalance( + userWallet.address + ); + const dsaEthBalance = await ethers.provider.getBalance(dsa.address); + const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + expect(userBalance).eq( + originalUserBalance.sub(depositAmount), + "User's Ether balance should decrease by the amount they deposited" + ); + + expect(dsaEthBalance).to.eq( + originalDsaBalance, + "User's DSA account Ether should not change after borrowing" + ); + + expect( + dsaLusdBalance, + "DSA account should now hold the amount the user borrowed" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( + borrowAmount, + "Trove debt should equal the borrowed amount plus fee" + ); + + expect(troveCollateral).to.eq( + depositAmount, + "Trove collateral should equal the deposited amount" + ); + }); + + it("opens a Trove using ETH collected from a previous spell", async () => { + const depositAmount = ethers.utils.parseEther("5"); // 5 ETH + const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const originalUserBalance = await ethers.provider.getBalance( + userWallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsa.address + ); + const depositId = 1; // Choose an ID to store and retrieve the deposited ETH + + const depositEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [helpers.ETH_ADDRESS, depositAmount, 0, depositId], + }; + + const openTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "open", + args: [ + 0, // When pulling ETH from a previous spell it doesn't matter what deposit value we put in this param + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [depositId, 0], + [0, 0], + ], + }; + + const spells = [depositEthSpell, openTroveSpell]; + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + value: depositAmount, + gasPrice: 0, + }); + + const userBalance = await ethers.provider.getBalance( + userWallet.address + ); + const dsaEthBalance = await ethers.provider.getBalance(dsa.address); + const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + expect(userBalance).eq( + originalUserBalance.sub(depositAmount), + "User's Ether balance should decrease by the amount they deposited" + ); + + expect(dsaEthBalance).to.eq( + originalDsaBalance, + "DSA balance should not change" + ); + + expect( + dsaLusdBalance, + "DSA account should now hold the amount the user borrowed" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( + borrowAmount, + "Trove debt should equal the borrowed amount plus fee" + ); + + expect(troveCollateral).to.eq( + depositAmount, + "Trove collateral should equal the deposited amount" + ); + }); + + it("opens a Trove and stores the debt for other spells to use", async () => { + const depositAmount = ethers.utils.parseEther("5"); // 5 ETH + const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const originalUserBalance = await ethers.provider.getBalance( + userWallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsa.address + ); + const borrowId = 1; + + const openTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [0, 0], + [borrowId, 0], + ], + }; + + const withdrawLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + contracts.LUSD_TOKEN_ADDRESS, + 0, // Amount comes from the previous spell's setId + dsa.address, + borrowId, + 0, + ], + }; + + const spells = [openTroveSpell, withdrawLusdSpell]; + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + value: depositAmount, + gasPrice: 0, + }); + + const userBalance = await ethers.provider.getBalance( + userWallet.address + ); + const dsaEthBalance = await ethers.provider.getBalance(dsa.address); + const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + expect(userBalance).eq( + originalUserBalance.sub(depositAmount), + "User's Ether balance should decrease by the amount they deposited" + ); + + expect(dsaEthBalance).to.eq( + originalDsaBalance, + "User's DSA account Ether should not change after borrowing" + ); + + expect( + dsaLusdBalance, + "DSA account should now hold the amount the user borrowed" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( + borrowAmount, + "Trove debt should equal the borrowed amount plus fee" + ); + + expect(troveCollateral).to.eq( + depositAmount, + "Trove collateral should equal the deposited amount" + ); + }); + + it("returns Instadapp event name and data", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + + const openTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [0, 0], + [0, 0], + ], + }; + + const openTx = await dsa.cast( + ...encodeSpells([openTroveSpell]), + userWallet.address, + { + value: depositAmount, + } + ); + const receipt = await openTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + expect(castLogEvent.eventNames[0]).eq( + "LogOpen(address,uint256,uint256,uint256,uint256[],uint256[])" + ); + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint256", + "uint256", + "uint256", + "uint256[]", + "uint256[]", + ], + [ + dsa.address, + maxFeePercentage, + depositAmount, + borrowAmount, + [0, 0], + [0, 0], + ] + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("close()", () => { + it("closes a Trove", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + // Create a dummy Trove + await helpers.createDsaTrove( + dsa, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + + // Send DSA account enough LUSD (from Stability Pool) to close their Trove + const extraLusdRequiredToCloseTrove = troveDebtBefore.sub( + borrowAmount + ); + + await helpers.sendToken( + liquity.lusdToken, + extraLusdRequiredToCloseTrove, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const originalDsaLusdBalance = await liquity.lusdToken.balanceOf( + dsa.address + ); + + expect( + originalDsaLusdBalance, + "DSA account should now hold the LUSD amount required to pay off the Trove debt" + ).to.eq(troveDebtBefore); + + const closeTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "close", + args: [0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([closeTroveSpell]), userWallet.address); + + const dsaEthBalance = await ethers.provider.getBalance(dsa.address); + const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); + + expect( + troveCollateral, + "Trove collateral should equal 0 after close" + ).to.eq(0); + + expect( + dsaEthBalance, + "DSA account should now hold the Trove's ETH collateral" + ).to.eq(troveCollateralBefore); + + expect( + dsaLusdBalance, + "DSA account should now hold the gas compensation amount of LUSD as it paid off the Trove debt" + ).to.eq(helpers.LUSD_GAS_COMPENSATION); + }); + + it("closes a Trove using LUSD obtained from a previous spell", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + + // Send user enough LUSD to repay the loan, we'll use a deposit and withdraw spell to obtain it + await helpers.sendToken( + liquity.lusdToken, + troveDebtBefore, + contracts.STABILITY_POOL_ADDRESS, + userWallet.address + ); + + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(userWallet) + .approve(dsa.address, troveDebtBefore); + + // Simulate a spell which would have pulled LUSD from somewhere (e.g. Uniswap) into InstaMemory + // In this case we're simply running a deposit spell from the user's EOA + const depositLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [contracts.LUSD_TOKEN_ADDRESS, troveDebtBefore, 0, 0], + }; + + const closeTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "close", + args: [0], + }; + const spells = [depositLusdSpell, closeTroveSpell]; + + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const dsaEthBalance = await ethers.provider.getBalance(dsa.address); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); + + expect( + troveCollateral, + "Trove collateral should equal 0 after close" + ).to.eq(0); + + expect( + dsaEthBalance, + "DSA account should now hold the Trove's ETH collateral" + ).to.eq(troveCollateralBefore); + }); + + it("closes a Trove and stores the released collateral for other spells to use", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + // Create a dummy Trove + await helpers.createDsaTrove( + dsa, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + + // Send DSA account enough LUSD (from Stability Pool) to close their Trove + const extraLusdRequiredToCloseTrove = troveDebtBefore.sub( + borrowAmount + ); + await helpers.sendToken( + liquity.lusdToken, + extraLusdRequiredToCloseTrove, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + const originalDsaLusdBalance = await liquity.lusdToken.balanceOf( + dsa.address + ); + + expect( + originalDsaLusdBalance, + "DSA account should now hold the LUSD amount required to pay off the Trove debt" + ).to.eq(troveDebtBefore); + + const collateralWithdrawId = 1; + + const closeTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "close", + args: [collateralWithdrawId], + }; + + const withdrawEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + helpers.ETH_ADDRESS, + 0, // amount comes from the previous spell's setId + dsa.address, + collateralWithdrawId, + 0, + ], + }; + + await dsa + .connect(userWallet) + .cast( + ...encodeSpells([closeTroveSpell, withdrawEthSpell]), + userWallet.address + ); + + const dsaEthBalance = await ethers.provider.getBalance(dsa.address); + const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); + + expect( + troveCollateral, + "Trove collateral should equal 0 after close" + ).to.eq(0); + + expect( + dsaEthBalance, + "DSA account should now hold the Trove's ETH collateral" + ).to.eq(troveCollateralBefore); + + expect( + dsaLusdBalance, + "DSA account should now hold the gas compensation amount of LUSD as it paid off the Trove debt" + ).to.eq(helpers.LUSD_GAS_COMPENSATION); + }); + + it("returns Instadapp event name and data", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + // Create a dummy Trove + await helpers.createDsaTrove( + dsa, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + await helpers.sendToken( + liquity.lusdToken, + ethers.utils.parseUnits("2500", 18), + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const closeTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "close", + args: [0], + }; + + const closeTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([closeTroveSpell]), userWallet.address); + + const receipt = await closeTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256"], + [dsa.address, 0] + ); + expect(castLogEvent.eventNames[0]).eq("LogClose(address,uint256)"); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("deposit()", () => { + it("deposits ETH into a Trove", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + + const topupAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const depositEthSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "deposit", + args: [topupAmount, upperHint, lowerHint, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([depositEthSpell]), userWallet.address, { + value: topupAmount, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + const expectedTroveCollateral = troveCollateralBefore.add( + topupAmount + ); + + expect( + troveCollateral, + `Trove collateral should have increased by ${topupAmount} ETH` + ).to.eq(expectedTroveCollateral); + }); + + it("deposits using ETH gained from a previous spell", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + + const topupAmount = ethers.utils.parseEther("1"); + const depositId = 1; + const depositEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [helpers.ETH_ADDRESS, topupAmount, 0, depositId], + }; + + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const depositEthToTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "deposit", + args: [0, upperHint, lowerHint, depositId, 0], + }; + const spells = [depositEthSpell, depositEthToTroveSpell]; + + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + value: topupAmount, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + const expectedTroveCollateral = troveCollateralBefore.add( + topupAmount + ); + + expect( + troveCollateral, + `Trove collateral should have increased by ${topupAmount} ETH` + ).to.eq(expectedTroveCollateral); + }); + + it("returns Instadapp event name and data", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const topupAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const depositEthSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "deposit", + args: [topupAmount, upperHint, lowerHint, 0, 0], + }; + + const depositTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([depositEthSpell]), userWallet.address, { + value: topupAmount, + }); + + const receipt = await depositTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256"], + [dsa.address, topupAmount, 0, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogDeposit(address,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("withdraw()", () => { + it("withdraws ETH from a Trove", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + const withdrawAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const withdrawEthSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "withdraw", + args: [withdrawAmount, upperHint, lowerHint, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([withdrawEthSpell]), userWallet.address); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const expectedTroveCollateral = troveCollateralBefore.sub( + withdrawAmount + ); + + expect( + troveCollateral, + `Trove collateral should have decreased by ${withdrawAmount} ETH` + ).to.eq(expectedTroveCollateral); + }); + + it("withdraws ETH from a Trove and stores the ETH for other spells to use", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + const originalUserEthBalance = await ethers.provider.getBalance( + userWallet.address + ); + + const withdrawAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const withdrawId = 1; + const withdrawEthFromTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "withdraw", + args: [withdrawAmount, upperHint, lowerHint, 0, withdrawId], + }; + + const withdrawEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [helpers.ETH_ADDRESS, 0, userWallet.address, withdrawId, 0], + }; + const spells = [withdrawEthFromTroveSpell, withdrawEthSpell]; + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + gasPrice: 0, // Remove gas costs so we can check balances have changed correctly + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const expectedTroveCollateral = troveCollateralBefore.sub( + withdrawAmount + ); + const userEthBalance = await ethers.provider.getBalance( + userWallet.address + ); + + expect( + troveCollateral, + `Trove collateral should have decreased by ${withdrawAmount} ETH` + ).to.eq(expectedTroveCollateral); + + expect( + userEthBalance, + `User ETH balance should have increased by ${withdrawAmount} ETH` + ).to.eq(originalUserEthBalance.add(withdrawAmount)); + }); + + it("returns Instadapp event name and data", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const withdrawAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const withdrawEthSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "withdraw", + args: [withdrawAmount, upperHint, lowerHint, 0, 0], + }; + + const withdrawTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([withdrawEthSpell]), userWallet.address); + + const receipt = await withdrawTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256"], + [dsa.address, withdrawAmount, 0, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogWithdraw(address,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("borrow()", () => { + it("borrows LUSD from a Trove", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + + const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "borrow", + args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, 0, 0], + }; + + // Borrow more LUSD from the Trove + await dsa + .connect(userWallet) + .cast(...encodeSpells([borrowSpell]), userWallet.address); + + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveDebt = troveDebtBefore.add(borrowAmount); + + expect( + troveDebt, + `Trove debt should have increased by at least ${borrowAmount} ETH` + ).to.gte(expectedTroveDebt); + }); + + it("borrows LUSD from a Trove and stores the LUSD for other spells to use", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + + const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const borrowId = 1; + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "borrow", + args: [ + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + 0, + borrowId, + ], + }; + const withdrawSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lusdToken.address, + 0, + userWallet.address, + borrowId, + 0, + ], + }; + const spells = [borrowSpell, withdrawSpell]; + + // Borrow more LUSD from the Trove + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveDebt = troveDebtBefore.add(borrowAmount); + const userLusdBalance = await liquity.lusdToken.balanceOf( + userWallet.address + ); + + expect( + troveDebt, + `Trove debt should have increased by at least ${borrowAmount} ETH` + ).to.gte(expectedTroveDebt); + + expect( + userLusdBalance, + `User LUSD balance should equal the borrowed LUSD due to the second withdraw spell` + ).eq(borrowAmount); + }); + + it("returns Instadapp event name and data", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "borrow", + args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, 0, 0], + }; + + const borrowTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([borrowSpell]), userWallet.address); + + const receipt = await borrowTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256"], + [dsa.address, borrowAmount, 0, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogBorrow(address,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("repay()", () => { + it("repays LUSD to a Trove", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + + // Create a dummy Trove + await helpers.createDsaTrove( + dsa, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + // DSA account is holding 2500 LUSD from opening a Trove, so we use some of that to repay + const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + depositAmount, + borrowAmount, + liquity + ); + const repaySpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "repay", + args: [repayAmount, upperHint, lowerHint, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([repaySpell]), userWallet.address); + + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveDebt = troveDebtBefore.sub(repayAmount); + + expect( + troveDebt, + `Trove debt should have decreased by ${repayAmount} ETH` + ).to.eq(expectedTroveDebt); + }); + + it("repays LUSD to a Trove using LUSD collected from a previous spell", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + + // Create a dummy Trove + await helpers.createDsaTrove( + dsa, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + + const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + depositAmount, + borrowAmount, + liquity + ); + + // Drain the DSA's LUSD balance so that we ensure we are repaying using LUSD from a previous spell + await helpers.sendToken( + liquity.lusdToken, + borrowAmount, + dsa.address, + userWallet.address + ); + + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(userWallet) + .approve(dsa.address, repayAmount); + + const lusdDepositId = 1; + const depositSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [liquity.lusdToken.address, repayAmount, 0, lusdDepositId], + }; + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "repay", + args: [0, upperHint, lowerHint, lusdDepositId, 0], + }; + + const spells = [depositSpell, borrowSpell]; + + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveDebt = troveDebtBefore.sub(repayAmount); + + expect( + troveDebt, + `Trove debt should have decreased by ${repayAmount} ETH` + ).to.eq(expectedTroveDebt); + }); + + it("returns Instadapp event name and data", async () => { + // Create a dummy Trove + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + await helpers.createDsaTrove( + dsa, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + depositAmount, + borrowAmount, + liquity + ); + + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "repay", + args: [repayAmount, upperHint, lowerHint, 0, 0], + }; + + const repayTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([borrowSpell]), userWallet.address, { + value: repayAmount, + }); + + const receipt = await repayTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256"], + [dsa.address, repayAmount, 0, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogRepay(address,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("adjust()", () => { + it("adjusts a Trove: deposit ETH and borrow LUSD", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const depositAmount = ethers.utils.parseEther("1"); // 1 ETH + const borrowAmount = ethers.utils.parseUnits("500", 18); // 500 LUSD + const withdrawAmount = 0; + const repayAmount = 0; + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + [0, 0, 0, 0], + [0, 0, 0, 0], + ], + }; + + // Adjust Trove by depositing ETH and borrowing LUSD + await dsa + .connect(userWallet) + .cast(...encodeSpells([adjustSpell]), userWallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveColl = troveCollateralBefore.add(depositAmount); + const expectedTroveDebt = troveDebtBefore.add(borrowAmount); + + expect( + troveCollateral, + `Trove collateral should have increased by ${depositAmount} ETH` + ).to.eq(expectedTroveColl); + + expect( + troveDebt, + `Trove debt should have increased by at least ${borrowAmount} ETH` + ).to.gte(expectedTroveDebt); + }); + + it("adjusts a Trove: withdraw ETH and repay LUSD", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const depositAmount = 0; + const borrowAmount = 0; + const withdrawAmount = ethers.utils.parseEther("1"); // 1 ETH; + const repayAmount = ethers.utils.parseUnits("10", 18); // 10 LUSD; + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + troveCollateralBefore.sub(withdrawAmount), + troveDebtBefore.sub(repayAmount), + liquity + ); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + [0, 0, 0, 0], + [0, 0, 0, 0], + ], + }; + + // Adjust Trove by withdrawing ETH and repaying LUSD + await dsa + .connect(userWallet) + .cast(...encodeSpells([adjustSpell]), userWallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveColl = troveCollateralBefore.sub(withdrawAmount); + const expectedTroveDebt = troveDebtBefore.sub(repayAmount); + + expect( + troveCollateral, + `Trove collateral should have increased by ${depositAmount} ETH` + ).to.eq(expectedTroveColl); + + expect( + troveDebt, + `Trove debt should have decreased by at least ${repayAmount} LUSD` + ).to.gte(expectedTroveDebt); + }); + + it("adjusts a Trove: deposit ETH and repay LUSD using previous spells", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const depositAmount = ethers.utils.parseEther("1"); // 1 ETH + const borrowAmount = 0; + const withdrawAmount = 0; + const repayAmount = ethers.utils.parseUnits("10", 18); // 10 lUSD + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const ethDepositId = 1; + const lusdRepayId = 2; + + const depositEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [helpers.ETH_ADDRESS, depositAmount, 0, ethDepositId], + }; + + const depositLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [liquity.lusdToken.address, repayAmount, 0, lusdRepayId], + }; + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + 0, // Deposit amount comes from a previous spell's storage slot + withdrawAmount, + borrowAmount, + 0, // Repay amount comes from a previous spell's storage slot + upperHint, + lowerHint, + [ethDepositId, 0, 0, lusdRepayId], + [0, 0, 0, 0], + ], + }; + const spells = [depositEthSpell, depositLusdSpell, adjustSpell]; + + // Send user some LUSD so they can repay + await helpers.sendToken( + liquity.lusdToken, + repayAmount, + helpers.JUSTIN_SUN_ADDRESS, + userWallet.address + ); + + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(userWallet) + .approve(dsa.address, repayAmount); + + // Adjust Trove by depositing ETH and repaying LUSD + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveColl = troveCollateralBefore.add(depositAmount); + const expectedTroveDebt = troveDebtBefore.sub(repayAmount); + + expect( + troveCollateral, + `Trove collateral should have increased by ${depositAmount} ETH` + ).to.eq(expectedTroveColl); + + expect( + troveDebt, + `Trove debt (${troveDebtBefore}) should have decreased by at least ${repayAmount} LUSD` + ).to.eq(expectedTroveDebt); + }); + + it("adjusts a Trove: withdraw ETH, borrow LUSD, and store the amounts for other spells", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const userEthBalanceBefore = await ethers.provider.getBalance( + userWallet.address + ); + const userLusdBalanceBefore = await liquity.lusdToken.balanceOf( + userWallet.address + ); + + const depositAmount = 0; + const borrowAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + const withdrawAmount = ethers.utils.parseEther("1"); // 1 ETH + const repayAmount = 0; + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const ethWithdrawId = 1; + const lusdBorrowId = 2; + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + [0, 0, 0, 0], + [0, ethWithdrawId, lusdBorrowId, 0], + ], + }; + + const withdrawEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + helpers.ETH_ADDRESS, + 0, + userWallet.address, + ethWithdrawId, + 0, + ], + }; + + const withdrawLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lusdToken.address, + 0, + userWallet.address, + lusdBorrowId, + 0, + ], + }; + + const spells = [adjustSpell, withdrawEthSpell, withdrawLusdSpell]; + + // Adjust Trove by withdrawing ETH and borrowing LUSD + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + gasLimit: helpers.MAX_GAS, + gasPrice: 0, + }); + + const userEthBalanceAfter = await ethers.provider.getBalance( + userWallet.address + ); + const userLusdBalanceAfter = await liquity.lusdToken.balanceOf( + userWallet.address + ); + expect(userEthBalanceAfter).eq( + userEthBalanceBefore.add(withdrawAmount) + ); + expect(userLusdBalanceAfter).eq( + userLusdBalanceBefore.add(borrowAmount) + ); + }); + + it("returns Instadapp event name and data", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + const depositAmount = ethers.utils.parseEther("1"); // 1 ETH + const borrowAmount = ethers.utils.parseUnits("500", 18); // 500 LUSD + const withdrawAmount = 0; + const repayAmount = 0; + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + [0, 0, 0, 0], + [0, 0, 0, 0], + ], + }; + + const adjustTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([adjustSpell]), userWallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + const receipt = await adjustTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256[]", + "uint256[]", + ], + [ + dsa.address, + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + [0, 0, 0, 0], + [0, 0, 0, 0], + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogAdjust(address,uint256,uint256,uint256,uint256,uint256,uint256[],uint256[])" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("claimCollateralFromRedemption()", () => { + it("claims collateral from a redeemed Trove", async () => { + // Create a low collateralized Trove + const depositAmount = ethers.utils.parseEther("1.5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + + await helpers.createDsaTrove( + dsa, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + // Redeem lots of LUSD to cause the Trove to become redeemed + const redeemAmount = ethers.utils.parseUnits("10000000", 18); + await helpers.sendToken( + liquity.lusdToken, + redeemAmount, + contracts.STABILITY_POOL_ADDRESS, + userWallet.address + ); + const { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + } = await helpers.getRedemptionHints(redeemAmount, liquity); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + await liquity.troveManager + .connect(userWallet) + .redeemCollateral( + redeemAmount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: helpers.MAX_GAS, // permit max gas + } + ); + + const remainingEthCollateral = await liquity.collSurplus.getCollateral( + dsa.address + ); + + // Claim the remaining collateral from the redeemed Trove + const claimCollateralFromRedemptionSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimCollateralFromRedemption", + args: [0], + }; + + await dsa + .connect(userWallet) + .cast( + ...encodeSpells([claimCollateralFromRedemptionSpell]), + userWallet.address + ); + + const ethBalance = await ethers.provider.getBalance(dsa.address); + + expect(ethBalance).to.eq(remainingEthCollateral); + }); + + it("returns Instadapp event name and data", async () => { + // Create a low collateralized Trove + const depositAmount = ethers.utils.parseEther("1.5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + + await helpers.createDsaTrove( + dsa, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + // Redeem lots of LUSD to cause the Trove to become redeemed + const redeemAmount = ethers.utils.parseUnits("10000000", 18); + const setId = 0; + await helpers.sendToken( + liquity.lusdToken, + redeemAmount, + contracts.STABILITY_POOL_ADDRESS, + userWallet.address + ); + const { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + } = await helpers.getRedemptionHints(redeemAmount, liquity); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + await liquity.troveManager + .connect(userWallet) + .redeemCollateral( + redeemAmount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: helpers.MAX_GAS, // permit max gas + } + ); + const claimAmount = await liquity.collSurplus.getCollateral( + dsa.address + ); + + const claimCollateralFromRedemptionSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimCollateralFromRedemption", + args: [setId], + }; + + const claimTx = await dsa + .connect(userWallet) + .cast( + ...encodeSpells([claimCollateralFromRedemptionSpell]), + userWallet.address + ); + + const receipt = await claimTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256"], + [dsa.address, claimAmount, setId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogClaimCollateralFromRedemption(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + }); + + describe("Stability Pool", () => { + describe("stabilityDeposit()", () => { + it("deposits into Stability Pool", async () => { + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsa.address + ); + expect(depositedAmount).to.eq(amount); + }); + + it("deposits into Stability Pool using LUSD collected from a previous spell", async () => { + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + userWallet.address + ); + const lusdDepositId = 1; + + const depositLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [liquity.lusdToken.address, amount, 0, lusdDepositId], + }; + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [0, frontendTag, lusdDepositId, 0, 0, 0], + }; + const spells = [depositLusdSpell, stabilityDepositSpell]; + + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(userWallet) + .approve(dsa.address, amount); + + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsa.address + ); + expect(depositedAmount).to.eq(amount); + }); + + it("returns Instadapp event name and data", async () => { + const amount = ethers.utils.parseUnits("100", 18); + const halfAmount = amount.div(2); + const frontendTag = ethers.constants.AddressZero; + const getDepositId = 0; + const setDepositId = 0; + const setEthGainId = 0; + const setLqtyGainId = 0; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [ + halfAmount, + frontendTag, + getDepositId, + setDepositId, + setEthGainId, + setLqtyGainId, + ], + }; + + // Create a Stability deposit for this DSA + await dsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + // Liquidate a Trove to cause an ETH gain + await liquity.troveManager.connect(userWallet).liquidateTroves(1, { + gasLimit: helpers.MAX_GAS, + }); + + // Fast forward in time so we have an LQTY gain + await provider.send("evm_increaseTime", [600]); + await provider.send("evm_mine"); + + // Create a Stability Pool deposit with a differen DSA so that LQTY gains can be calculated + // See: https://github.com/liquity/dev/#lqty-reward-events-and-payouts + const tempDsa = await buildDSAv2(userWallet.address); + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + tempDsa.address + ); + await tempDsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + const ethGain = await liquity.stabilityPool.getDepositorETHGain( + dsa.address + ); + const lqtyGain = await liquity.stabilityPool.getDepositorLQTYGain( + dsa.address + ); + + // Top up the user's deposit so that we can track their ETH and LQTY gain + const depositAgainTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + const receipt = await depositAgainTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint256", + "uint256", + "uint256", + "address", + "uint256", + "uint256", + "uint256", + "uint256", + ], + [ + dsa.address, + halfAmount, + ethGain, + lqtyGain, + frontendTag, + getDepositId, + setDepositId, + setEthGainId, + setLqtyGainId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStabilityDeposit(address,uint256,uint256,uint256,address,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("stabilityWithdraw()", () => { + it("withdraws from Stability Pool", async () => { + // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool + [liquity, dsa] = await helpers.resetInitialState( + userWallet.address, + contracts + ); + + // The current block number has liquidatable Troves. + // Remove them otherwise Stability Pool withdrawals are disabled + await liquity.troveManager.connect(userWallet).liquidateTroves(90, { + gasLimit: helpers.MAX_GAS, + }); + + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + // Withdraw half of the deposit + const stabilityWithdrawSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityWithdraw", + args: [amount.div(2), 0, 0, 0, 0], + }; + const spells = [stabilityDepositSpell, stabilityWithdrawSpell]; + + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsa.address + ); + const dsaLusdBalance = await liquity.lusdToken.balanceOf(dsa.address); + + expect(depositedAmount).to.eq(amount.div(2)); + expect(dsaLusdBalance).to.eq(amount.div(2)); + }); + + it("withdraws from Stability Pool and stores the LUSD for other spells", async () => { + // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool + [liquity, dsa] = await helpers.resetInitialState( + userWallet.address, + contracts + ); + + // The current block number has liquidatable Troves. + // Remove them otherwise Stability Pool withdrawals are disabled + await liquity.troveManager.connect(userWallet).liquidateTroves(90, { + gasLimit: helpers.MAX_GAS, + }); + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + const withdrawId = 1; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + // Withdraw half of the deposit + const stabilityWithdrawSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityWithdraw", + args: [amount.div(2), 0, 0, 0, withdrawId], + }; + + const withdrawLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lusdToken.address, + 0, + userWallet.address, + withdrawId, + 0, + ], + }; + + const spells = [ + stabilityDepositSpell, + stabilityWithdrawSpell, + withdrawLusdSpell, + ]; + + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsa.address + ); + const walletLusdBalance = await liquity.lusdToken.balanceOf( + dsa.address + ); + + expect(depositedAmount).to.eq(amount.div(2)); + expect(walletLusdBalance).to.eq(amount.div(2)); + }); + + it("returns Instadapp event name and data", async () => { + // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool + [liquity, dsa] = await helpers.resetInitialState( + userWallet.address, + contracts + ); + + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + // Withdraw half of the deposit + const withdrawAmount = amount.div(2); + const getWithdrawId = 0; + const setWithdrawId = 0; + const setEthGainId = 0; + const setLqtyGainId = 0; + + // Create a Stability Pool deposit + await dsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + // The current block number has liquidatable Troves. + // Remove them otherwise Stability Pool withdrawals are disabled + await liquity.troveManager.connect(userWallet).liquidateTroves(90, { + gasLimit: helpers.MAX_GAS, + }); + + // Fast forward in time so we have an LQTY gain + await provider.send("evm_increaseTime", [600]); + await provider.send("evm_mine"); + + // Create another Stability Pool deposit so that LQTY gains are realized + // See: https://github.com/liquity/dev/#lqty-reward-events-and-payouts + const tempDsa = await buildDSAv2(userWallet.address); + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + tempDsa.address + ); + await tempDsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + const ethGain = await liquity.stabilityPool.getDepositorETHGain( + dsa.address + ); + const lqtyGain = await liquity.stabilityPool.getDepositorLQTYGain( + dsa.address + ); + + const stabilityWithdrawSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityWithdraw", + args: [ + withdrawAmount, + getWithdrawId, + setWithdrawId, + setEthGainId, + setLqtyGainId, + ], + }; + + const withdrawTx = await dsa + .connect(userWallet) + .cast( + ...encodeSpells([stabilityWithdrawSpell]), + userWallet.address + ); + + const receipt = await withdrawTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + ], + [ + dsa.address, + withdrawAmount, + ethGain, + lqtyGain, + getWithdrawId, + setWithdrawId, + setEthGainId, + setLqtyGainId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStabilityWithdraw(address,uint256,uint256,uint256,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("stabilityMoveEthGainToTrove()", () => { + beforeEach(async () => { + // Start these test from fresh so that we definitely have a liquidatable Trove within this block + [liquity, dsa] = await helpers.resetInitialState( + userWallet.address, + contracts + ); + }); + + it("moves ETH gain from Stability Pool to Trove", async () => { + // Create a DSA owned Trove to capture ETH liquidation gains + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsa.address + ); + + // Create a Stability Deposit using the Trove's borrowed LUSD + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + // Liquidate a Trove to create an ETH gain for the new DSA Trove + await liquity.troveManager + .connect(userWallet) + .liquidate(helpers.LIQUIDATABLE_TROVE_ADDRESS, { + gasLimit: helpers.MAX_GAS, // permit max gas + }); + + const ethGainFromLiquidation = await liquity.stabilityPool.getDepositorETHGain( + dsa.address + ); + + // Move ETH gain to Trove + const moveEthGainSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityMoveEthGainToTrove", + args: [ethers.constants.AddressZero, ethers.constants.AddressZero], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([moveEthGainSpell]), userWallet.address); + + const ethGainAfterMove = await liquity.stabilityPool.getDepositorETHGain( + dsa.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const expectedTroveCollateral = troveCollateralBefore.add( + ethGainFromLiquidation + ); + expect(ethGainAfterMove).to.eq(0); + expect(troveCollateral).to.eq(expectedTroveCollateral); + }); + + it("returns Instadapp event name and data", async () => { + // Create a DSA owned Trove to capture ETH liquidation gains + // Create a dummy Trove + await helpers.createDsaTrove(dsa, userWallet, liquity); + + // Create a Stability Deposit using the Trove's borrowed LUSD + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + // Liquidate a Trove to create an ETH gain for the new DSA Trove + await liquity.troveManager + .connect(userWallet) + .liquidate(helpers.LIQUIDATABLE_TROVE_ADDRESS, { + gasLimit: helpers.MAX_GAS, // permit max gas + }); + + const ethGainFromLiquidation = await liquity.stabilityPool.getDepositorETHGain( + dsa.address + ); + + // Move ETH gain to Trove + const moveEthGainSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityMoveEthGainToTrove", + args: [ethers.constants.AddressZero, ethers.constants.AddressZero], + }; + + const moveEthGainTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([moveEthGainSpell]), userWallet.address); + + const receipt = await moveEthGainTx.wait(); + + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256"], + [dsa.address, ethGainFromLiquidation] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStabilityMoveEthGainToTrove(address,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + }); + + describe("Staking", () => { + describe("stake()", () => { + it("stakes LQTY", async () => { + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsa.address + ); + + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); + expect(lqtyBalance).to.eq(0); + + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.add(amount) + ); + }); + + it("stakes LQTY using LQTY obtained from a previous spell", async () => { + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + userWallet.address + ); + + const lqtyDepositId = 1; + const depositSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [liquity.lqtyToken.address, amount, 0, lqtyDepositId], + }; + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [0, lqtyDepositId, 0, 0, 0], + }; + const spells = [depositSpell, stakeSpell]; + + // Allow DSA to spend user's LQTY + await liquity.lqtyToken + .connect(userWallet) + .approve(dsa.address, amount); + + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); + expect(lqtyBalance).to.eq(0); + + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.add(amount) + ); + }); + + it("returns Instadapp event name and data", async () => { + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsa.address + ); + + const getStakeId = 0; + const setStakeId = 0; + const setEthGainId = 0; + const setLusdGainId = 0; + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, getStakeId, setStakeId, setEthGainId, setLusdGainId], + }; + + const stakeTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const receipt = await stakeTx.wait(); + + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256", "uint256", "uint256"], + [ + dsa.address, + amount, + getStakeId, + setStakeId, + setEthGainId, + setLusdGainId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStake(address,uint256,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("unstake()", () => { + it("unstakes LQTY", async () => { + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsa.address + ); + + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + const unstakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "unstake", + args: [amount, 0, 0, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([unstakeSpell]), userWallet.address); + + const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); + expect(lqtyBalance).to.eq(amount); + + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.sub(amount) + ); + }); + + it("unstakes LQTY and stores the LQTY for other spells", async () => { + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsa.address + ); + + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + const withdrawId = 1; + const unstakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "unstake", + args: [amount, 0, withdrawId, 0, 0], + }; + + const withdrawLqtySpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lqtyToken.address, + 0, + userWallet.address, + withdrawId, + 0, + ], + }; + const spells = [unstakeSpell, withdrawLqtySpell]; + await dsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + const userLqtyBalance = await liquity.lqtyToken.balanceOf( + userWallet.address + ); + expect(lqtyBalance).to.eq(0); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.sub(amount) + ); + expect(userLqtyBalance).to.eq(amount); + }); + + it("returns Instadapp event name and data", async () => { + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsa.address + ); + + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + + await dsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const getUnstakeId = 0; + const setUnstakeId = 0; + const setEthGainId = 0; + const setLusdGainId = 0; + const unstakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "unstake", + args: [ + amount, + getUnstakeId, + setUnstakeId, + setEthGainId, + setLusdGainId, + ], + }; + + const unstakeTx = await dsa + .connect(userWallet) + .cast(...encodeSpells([unstakeSpell]), userWallet.address); + + const receipt = await unstakeTx.wait(); + + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256", "uint256", "uint256"], + [ + dsa.address, + amount, + getUnstakeId, + setUnstakeId, + setEthGainId, + setLusdGainId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogUnstake(address,uint256,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("claimStakingGains()", () => { + it("claims gains from staking", async () => { + const stakerDsa = await buildDSAv2(userWallet.address); + const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY + + // Stake lots of LQTY + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + stakerDsa.address + ); + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + await stakerDsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + // Open a Trove to cause an ETH issuance gain for stakers + await helpers.createDsaTrove(dsa, userWallet, liquity); + + // Redeem some ETH to cause an LUSD redemption gain for stakers + await helpers.redeem( + ethers.utils.parseUnits("1000", 18), + contracts.STABILITY_POOL_ADDRESS, + userWallet, + liquity + ); + + const setEthGainId = 0; + const setLusdGainId = 0; + const ethGain = await liquity.staking.getPendingETHGain( + stakerDsa.address + ); + const lusdGain = await liquity.staking.getPendingLUSDGain( + stakerDsa.address + ); + + const claimStakingGainsSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimStakingGains", + args: [setEthGainId, setLusdGainId], + }; + + const ethBalanceBefore = await ethers.provider.getBalance( + stakerDsa.address + ); + + // Claim gains + await stakerDsa + .connect(userWallet) + .cast( + ...encodeSpells([claimStakingGainsSpell]), + userWallet.address + ); + + const ethBalanceAfter = await ethers.provider.getBalance( + stakerDsa.address + ); + const lusdBalanceAfter = await liquity.lusdToken.balanceOf( + stakerDsa.address + ); + expect(ethBalanceAfter).to.eq(ethBalanceBefore.add(ethGain)); + expect(lusdBalanceAfter).to.eq(lusdGain); + }); + + it("claims gains from staking and stores them for other spells", async () => { + const stakerDsa = await buildDSAv2(userWallet.address); + const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY + + // Stake lots of LQTY + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + stakerDsa.address + ); + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + await stakerDsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + // Open a Trove to cause an ETH issuance gain for stakers + await helpers.createDsaTrove(dsa, userWallet, liquity); + + // Redeem some ETH to cause an LUSD redemption gain for stakers + await helpers.redeem( + ethers.utils.parseUnits("1000", 18), + contracts.STABILITY_POOL_ADDRESS, + userWallet, + liquity + ); + + const ethGain = await liquity.staking.getPendingETHGain( + stakerDsa.address + ); + const lusdGain = await liquity.staking.getPendingLUSDGain( + stakerDsa.address + ); + const lusdBalanceBefore = await liquity.lusdToken.balanceOf( + userWallet.address + ); + const ethBalanceBefore = await ethers.provider.getBalance( + userWallet.address + ); + const ethGainId = 111; + const lusdGainId = 222; + + const claimStakingGainsSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimStakingGains", + args: [ethGainId, lusdGainId], + }; + + const withdrawEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [helpers.ETH_ADDRESS, 0, userWallet.address, ethGainId, 0], + }; + + const withdrawLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lusdToken.address, + 0, + userWallet.address, + lusdGainId, + 0, + ], + }; + + const spells = [ + claimStakingGainsSpell, + withdrawEthSpell, + withdrawLusdSpell, + ]; + + // Claim gains + await stakerDsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + gasPrice: 0, + }); + + const ethBalanceAfter = await ethers.provider.getBalance( + userWallet.address + ); + const lusdBalanceAfter = await liquity.lusdToken.balanceOf( + userWallet.address + ); + + expect( + ethBalanceAfter, + "User's ETH balance should have increased by the issuance gain from staking" + ).to.eq(ethBalanceBefore.add(ethGain)); + expect( + lusdBalanceAfter, + "User's LUSD balance should have increased by the redemption gain from staking" + ).to.eq(lusdBalanceBefore.add(lusdGain)); + }); + + it("returns Instadapp event name and data", async () => { + const stakerDsa = await buildDSAv2(userWallet.address); + const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY + + // Stake lots of LQTY + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + stakerDsa.address + ); + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + await stakerDsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + // Open a Trove to cause an ETH issuance gain for stakers + await helpers.createDsaTrove(dsa, userWallet, liquity); + + // Redeem some ETH to cause an LUSD redemption gain for stakers + await helpers.redeem( + ethers.utils.parseUnits("1000", 18), + contracts.STABILITY_POOL_ADDRESS, + userWallet, + liquity + ); + + const setEthGainId = 0; + const setLusdGainId = 0; + const ethGain = await liquity.staking.getPendingETHGain( + stakerDsa.address + ); + const lusdGain = await liquity.staking.getPendingLUSDGain( + stakerDsa.address + ); + + const claimStakingGainsSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimStakingGains", + args: [setEthGainId, setLusdGainId], + }; + + // Claim gains + const claimGainsTx = await stakerDsa + .connect(userWallet) + .cast( + ...encodeSpells([claimStakingGainsSpell]), + userWallet.address + ); + + const receipt = await claimGainsTx.wait(); + + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256", "uint256"], + [stakerDsa.address, ethGain, lusdGain, setEthGainId, setLusdGainId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogClaimStakingGains(address,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + }); + }); +}); diff --git a/test/mainnet/pooltogether/pooltogether.test.ts b/test/mainnet/pooltogether/pooltogether.test.ts index 272f4460..48644259 100644 --- a/test/mainnet/pooltogether/pooltogether.test.ts +++ b/test/mainnet/pooltogether/pooltogether.test.ts @@ -1,1225 +1,1225 @@ -// import { expect } from "chai"; -// import hre from "hardhat"; -// const { web3, deployments, waffle, ethers } = hre; -// const { provider, deployContract } = waffle; - -// import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; -// import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; -// import { encodeSpells } from "../../../scripts/tests/encodeSpells"; -// import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; - -// import { addresses } from "../../../scripts/constant/addresses"; -// import { abis } from "../../../scripts/constant/abis"; -// import { constants } from "../../../scripts/constant/constant"; -// import { tokens } from "../../../scripts/constant/tokens"; -// import type { Signer, Contract } from "ethers"; - -// import { -// ConnectV2Compound__factory, -// ConnectV2PoolTogether__factory, -// ConnectV2UniswapV2__factory, -// } from "../../../typechain"; - -// const DAI_TOKEN_ADDR = tokens.dai.address; // DAI Token - -// // PoolTogether Address: https://docs.pooltogether.com/resources/networks/ethereum -// const DAI_PRIZE_POOL_ADDR = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a"; // DAI Prize Pool -// const PT_DAI_TICKET_ADDR = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF"; // PT DAI Ticket -// const DAI_POOL_FAUCET_ADDR = "0xF362ce295F2A4eaE4348fFC8cDBCe8d729ccb8Eb"; // DAI POOL Faucet -// const POOL_TOKEN_ADDRESS = "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e"; // POOL Tocken -// const TOKEN_FAUCET_PROXY_FACTORY_ADDR = -// "0xE4E9cDB3E139D7E8a41172C20b6Ed17b6750f117"; // TokenFaucetProxyFactory for claimAll -// const DAI_POD_ADDR = "0x2f994e2E4F3395649eeE8A89092e63Ca526dA829"; // DAI Pod -// const UNISWAP_POOLETHLP_PRIZE_POOL_ADDR = -// "0x3AF7072D29Adde20FC7e173a7CB9e45307d2FB0A"; // Uniswap Pool/ETH LP PrizePool -// const UNISWAP_POOLETHLP_FAUCET_ADDR = -// "0x9A29401EF1856b669f55Ae5b24505b3B6fAEb370"; // Uniswap Pool/ETH LP Faucet -// const UNISWAP_POOLETHLP_TOKEN_ADDR = -// "0x85cb0bab616fe88a89a35080516a8928f38b518b"; // Uniswap Pool/ETH Token -// const PT_UNISWAP_POOLETHLP_TICKET_ADDR = -// "0xeb8928ee92efb06c44d072a24c2bcb993b61e543"; // Pool Together Uniswap Pool/ETH LP Ticket -// const POOL_PRIZE_POOL_ADDR = "0x396b4489da692788e327e2e4b2b0459a5ef26791"; // POOL Prize Pool -// const PT_POOL_TICKET_ADDR = "0x27d22a7648e955e510a40bdb058333e9190d12d4"; // Pool Together POOL Ticket -// const WETH_ADDR = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; // WETH -// const DAI_POD_TOKEN_DROP = "0xc5209623E3dFdf9C0cCbe497c8012883C4147731"; - -// // Community WETH Prize Pool (Rari): https://reference-app.pooltogether.com/pools/mainnet/0xa88ca010b32a54d446fc38091ddbca55750cbfc3/manage#stats -// const WETH_PRIZE_POOL_ADDR = "0xa88ca010b32a54d446fc38091ddbca55750cbfc3"; // Community WETH Prize Pool (Rari) -// const WETH_POOL_TICKET_ADDR = "0x9b5c30aeb9ce2a6a121cea9a85bc0d662f6d9b40"; // Community WETH Prize Pool Ticket (Rari) - -// const prizePoolABI = [ -// "function calculateEarlyExitFee( address from, address controlledToken, uint256 amount) external returns ( uint256 exitFee, uint256 burnedCredit)", -// ]; - -// const podABI = [ -// "function getEarlyExitFee(uint256 amount) external returns (uint256)", -// "function balanceOfUnderlying(address user) external view returns (uint256 amount)", -// "function drop() public returns (uint256)", -// "function balanceOf(address account) external view returns (uint256)", -// ]; - -// const POD_FACTORY_ADDRESS = "0x4e3a9f9fbafb2ec49727cffa2a411f7a0c1c4ce1"; -// const podFactoryABI = [ -// "function create( address _prizePool, address _ticket, address _faucet, address _manager, uint8 _decimals) external returns (address pod)", -// ]; - -// const tokenDropABI = [ -// "function claim(address user) external returns (uint256)", -// ]; - -// describe("PoolTogether", function() { -// const connectorName = "COMPOUND-TEST-A"; -// const uniswapConnectorName = "UNISWAP-TEST-A"; -// const ptConnectorName = "POOLTOGETHER-TEST-A"; - -// let dsaWallet0: any; -// let masterSigner: Signer; -// let instaConnectorsV2: Contract; -// let connector: any; -// let ptConnector: any; -// let uniswapConnector: any; - -// const wallets = provider.getWallets(); -// const [wallet0, wallet1, wallet2, wallet3] = wallets; -// before(async () => { -// masterSigner = await getMasterSigner(); -// instaConnectorsV2 = await ethers.getContractAt( -// abis.core.connectorsV2, -// addresses.core.connectorsV2 -// ); - -// // Deploy and enable Compound Connector -// connector = await deployAndEnableConnector({ -// connectorName, -// contractArtifact: ConnectV2Compound__factory, -// signer: masterSigner, -// connectors: instaConnectorsV2, -// }); - -// // Deploy and enable Pool Together Connector -// ptConnector = await deployAndEnableConnector({ -// connectorName: ptConnectorName, -// contractArtifact: ConnectV2PoolTogether__factory, -// signer: masterSigner, -// connectors: instaConnectorsV2, -// }); - -// // Deploy and enable Uniswap Connector -// uniswapConnector = await deployAndEnableConnector({ -// connectorName: uniswapConnectorName, -// contractArtifact: ConnectV2UniswapV2__factory, -// signer: masterSigner, -// connectors: instaConnectorsV2, -// }); -// }); - -// it("Should have contracts deployed.", async function() { -// expect(!!instaConnectorsV2.address).to.be.true; -// expect(!!connector.address).to.be.true; -// expect(!!ptConnector.address).to.be.true; -// expect(!!uniswapConnector.address).to.be.true; -// expect(!!(await masterSigner.getAddress())).to.be.true; -// }); - -// describe("DSA wallet setup", function() { -// it("Should build DSA v2", async function() { -// dsaWallet0 = await buildDSAv2(wallet0.address); -// expect(!!dsaWallet0.address).to.be.true; -// }); - -// it("Deposit 10 ETH into DSA wallet", async function() { -// await wallet0.sendTransaction({ -// to: dsaWallet0.address, -// value: ethers.utils.parseEther("10"), -// }); -// expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( -// ethers.utils.parseEther("10") -// ); -// }); -// }); - -// describe("Main - DAI Prize Pool Test", function() { -// it("Should deposit 1 ETH in Compound", async function() { -// const amount = ethers.utils.parseEther("1"); // 1 ETH -// const spells = [ -// { -// connector: connectorName, -// method: "deposit", -// args: ["ETH-A", amount, 0, 0], -// }, -// ]; - -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); -// expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( -// ethers.utils.parseEther("9") -// ); -// }); - -// it("Should borrow 100 DAI from Compound and deposit DAI into DAI Prize Pool", async function() { -// const amount = ethers.utils.parseEther("100"); // 100 DAI -// const setId = "83478237"; -// const spells = [ -// { -// connector: connectorName, -// method: "borrow", -// args: ["DAI-A", amount, 0, setId], -// }, -// { -// connector: ptConnectorName, -// method: "depositTo", -// args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, setId, 0], -// }, -// ]; -// // Before Spell -// let daiToken = await ethers.getContractAt( -// abis.basic.erc20, -// DAI_TOKEN_ADDR -// ); -// let daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI balance is 0`).to.be.eq( -// ethers.utils.parseEther("0") -// ); - -// let cToken = await ethers.getContractAt( -// abis.basic.erc20, -// PT_DAI_TICKET_ADDR -// ); -// const balance = await cToken.balanceOf(dsaWallet0.address); -// expect(balance, `PoolTogether DAI Ticket balance is 0`).to.be.eq(0); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect( -// daiBalance, -// `Expect DAI balance to still equal 0 since it was deposited into Prize Pool` -// ).to.be.eq(0); - -// const balanceAfter = await cToken.balanceOf(dsaWallet0.address); -// expect( -// balanceAfter, -// `PoolTogether DAI Ticket balance equals 100` -// ).to.be.eq(ethers.utils.parseEther("100")); - -// // ETH used for transaction -// expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( -// ethers.utils.parseEther("9") -// ); -// }); - -// it("Should wait 11 days, withdraw all PrizePool, get back 100 DAI, and claim POOL", async function() { -// const amount = ethers.utils.parseEther("100"); // 100 DAI - -// let prizePoolContract = new ethers.Contract( -// DAI_PRIZE_POOL_ADDR, -// prizePoolABI, -// ethers.provider -// ); -// let earlyExitFee = await prizePoolContract.callStatic[ -// "calculateEarlyExitFee" -// ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); -// expect( -// earlyExitFee.exitFee, -// "Exit Fee equal to 1 DAI because starts at 10%" -// ).to.be.eq(ethers.utils.parseEther("1")); - -// const spells = [ -// { -// connector: ptConnectorName, -// method: "withdrawInstantlyFrom", -// args: [ -// DAI_PRIZE_POOL_ADDR, -// amount, -// PT_DAI_TICKET_ADDR, -// earlyExitFee.exitFee, -// 0, -// 0, -// ], -// }, -// { -// connector: ptConnectorName, -// method: "claim", -// args: [DAI_POOL_FAUCET_ADDR, 0], -// }, -// ]; - -// // Before spell -// let daiToken = await ethers.getContractAt( -// abis.basic.erc20, -// DAI_TOKEN_ADDR -// ); -// let daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI balance equals 0`).to.be.eq( -// ethers.utils.parseEther("0") -// ); - -// let cToken = await ethers.getContractAt( -// abis.basic.erc20, -// PT_DAI_TICKET_ADDR -// ); -// const balance = await cToken.balanceOf(dsaWallet0.address); -// expect(balance, `PoolTogether Dai Ticket is 100`).to.be.eq( -// ethers.utils.parseEther("100") -// ); - -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `POOL Token equals 0`).to.be.eq( -// ethers.utils.parseEther("0") -// ); - -// // Increase time by 11 days so we get back all DAI without early withdrawal fee -// await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); - -// earlyExitFee = await prizePoolContract.callStatic[ -// "calculateEarlyExitFee" -// ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); -// expect( -// earlyExitFee.exitFee, -// "Exit Fee equal to 0 DAI because past 10 days" -// ).to.be.eq(0); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect( -// daiBalance, -// `DAI balance to be equal to 100, because of no early withdrawal fee` -// ).to.be.eq(ethers.utils.parseEther("100")); - -// const balanceAfter = await cToken.balanceOf(dsaWallet0.address); -// expect(balanceAfter, `PoolTogether Dai Ticket to equal 0`).to.be.eq(0); - -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// expect( -// poolBalanceAfter, -// `POOL Token Balance to be greater than 0` -// ).to.be.gt(ethers.utils.parseEther("0")); -// }); - -// it("Should deposit and withdraw all PrizePool, get back less than 100 DAI", async function() { -// const amount = ethers.utils.parseEther("100"); // 100 DAI -// const exitFee = ethers.utils.parseEther("1"); // 1 DAI is 10% of 100 DAI -// const spells = [ -// { -// connector: ptConnectorName, -// method: "depositTo", -// args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, 0, 0], -// }, -// { -// connector: ptConnectorName, -// method: "withdrawInstantlyFrom", -// args: [ -// DAI_PRIZE_POOL_ADDR, -// amount, -// PT_DAI_TICKET_ADDR, -// exitFee, -// 0, -// 0, -// ], -// }, -// ]; - -// // Before spell -// let daiToken = await ethers.getContractAt( -// abis.basic.erc20, -// DAI_TOKEN_ADDR -// ); -// let daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI Balance equals 0`).to.be.eq( -// ethers.utils.parseEther("100") -// ); - -// let cToken = await ethers.getContractAt( -// abis.basic.erc20, -// PT_DAI_TICKET_ADDR -// ); -// const balance = await cToken.balanceOf(dsaWallet0.address); -// expect(balance, `PoolTogether DAI Ticket equals 0`).to.be.eq(0); - -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `PoolTogether Token greater than 0`).to.be.gt(0); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect( -// daiBalance, -// `DAI balance to be less than 100, because of early withdrawal fee` -// ).to.be.lt(ethers.utils.parseEther("100")); - -// const balanceAfter = await cToken.balanceOf(dsaWallet0.address); -// expect(balanceAfter, `PoolTogether Dai Ticket to equal 0`).to.be.eq(0); - -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalanceAfter, `POOL Token Balance to greater than 0`).to.be.gt( -// ethers.utils.parseEther("0") -// ); -// }); - -// it("Should deposit, wait 11 days, and withdraw all PrizePool, get 99 DAI, and claim all POOL using claimAll", async function() { -// const amount = ethers.utils.parseEther("99"); // 99 DAI -// const depositSpells = [ -// { -// connector: ptConnectorName, -// method: "depositTo", -// args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, 0, 0], -// }, -// ]; - -// // Before spell -// let daiToken = await ethers.getContractAt( -// abis.basic.erc20, -// DAI_TOKEN_ADDR -// ); -// let daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI balance less than 100`).to.be.lt( -// ethers.utils.parseEther("100") -// ); - -// let cToken = await ethers.getContractAt( -// abis.basic.erc20, -// PT_DAI_TICKET_ADDR -// ); -// const balance = await cToken.balanceOf(dsaWallet0.address); -// expect(balance, `PoolTogether DAI Ticket equal 0`).to.be.eq(0); - -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `POOL Token is greater than 0`).to.be.gt( -// ethers.utils.parseEther("0") -// ); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(depositSpells), wallet1.address); -// const receipt = await tx.wait(); - -// const prizePoolContract = new ethers.Contract( -// DAI_PRIZE_POOL_ADDR, -// prizePoolABI, -// ethers.provider -// ); -// let earlyExitFee = await prizePoolContract.callStatic[ -// "calculateEarlyExitFee" -// ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); -// expect( -// earlyExitFee.exitFee, -// "Exit Fee equal to .99 DAI because starts at 10%" -// ).to.be.eq(ethers.utils.parseEther(".99")); - -// // Increase time by 11 days so we get back all DAI without early withdrawal fee -// await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); - -// earlyExitFee = await prizePoolContract.callStatic[ -// "calculateEarlyExitFee" -// ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); -// expect( -// earlyExitFee.exitFee, -// "Exit Fee equal to 0 DAI because past 10 days" -// ).to.be.eq(0); - -// const withdrawSpells = [ -// { -// connector: ptConnectorName, -// method: "withdrawInstantlyFrom", -// args: [ -// DAI_PRIZE_POOL_ADDR, -// amount, -// PT_DAI_TICKET_ADDR, -// earlyExitFee.exitFee, -// 0, -// 0, -// ], -// }, -// { -// connector: ptConnectorName, -// method: "claimAll", -// args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, [DAI_POOL_FAUCET_ADDR]], -// }, -// ]; - -// // Run spell transaction -// const tx2 = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(withdrawSpells), wallet1.address); -// const receipt2 = await tx2.wait(); - -// // After spell -// daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI balance equals 99`).to.be.eq( -// ethers.utils.parseEther("99") -// ); - -// const balanceAfter = await cToken.balanceOf(dsaWallet0.address); -// expect(balanceAfter, `PoolTogether DAI Ticket equal 0`).to.be.eq(0); - -// // Expect -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalanceAfter, `Pool Token to be greateir than 0`).to.be.gt( -// ethers.utils.parseEther("0") -// ); -// }); -// }); - -// describe("Main - DAI Pod Test", function() { -// it("Should deposit 99 DAI in DAI Pod", async function() { -// const amount = ethers.utils.parseEther("99"); // 99 DAI -// const spells = [ -// { -// connector: ptConnectorName, -// method: "depositToPod", -// args: [DAI_TOKEN_ADDR, DAI_POD_ADDR, amount, 0, 0], -// }, -// ]; - -// // Before spell -// let daiToken = await ethers.getContractAt( -// abis.basic.erc20, -// DAI_TOKEN_ADDR -// ); -// let daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI balance equals 99`).to.be.eq( -// ethers.utils.parseEther("99") -// ); - -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `POOL Token greater than 0`).to.be.gte(0); - -// let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); -// const podBalance = await podToken.balanceOf(dsaWallet0.address); -// expect(podBalance, `Pod DAI Token equals 0`).to.be.eq(0); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI equals 0`).to.be.eq(0); - -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalanceAfter, `POOL Token greater than 0`).to.be.gt(0); - -// const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); -// expect(podBalanceAfter, `Pod DAI token greater than 0`).to.be.eq( -// ethers.utils.parseEther("99") -// ); -// }); - -// it("Should claim rewards from pod token drop", async function() { -// const spells = [ -// { -// connector: ptConnectorName, -// method: "claimPodTokenDrop", -// args: [DAI_POD_TOKEN_DROP, 0], -// }, -// ]; - -// const tokenDropContract = new ethers.Contract( -// DAI_POD_TOKEN_DROP, -// tokenDropABI, -// ethers.provider -// ); -// const podContract = new ethers.Contract( -// DAI_POD_ADDR, -// podABI, -// masterSigner -// ); - -// // drop(): Claim TokenDrop asset for PrizePool Pod and transfers token(s) to external Pod TokenDrop -// // dropt() also calls batch which, Deposit Pod float into PrizePool. Deposits the current float -// // amount into the PrizePool and claims current POOL rewards. -// const dropTx = await podContract.drop(); -// await dropTx.wait(); - -// // POOL Rewards able to claim from Pod Token Drop -// let claimAmount = await tokenDropContract.callStatic["claim"]( -// dsaWallet0.address -// ); - -// // Before spell -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// const total = claimAmount.add(poolBalance); -// expect(poolBalanceAfter, `POOL Token same as before spell`).to.be.eq( -// total -// ); -// }); - -// it("Should wait 11 days, withdraw all podTokens, get back 99 DAI", async function() { -// const amount = ethers.utils.parseEther("99"); // 99 DAI - -// const podContract = new ethers.Contract( -// DAI_POD_ADDR, -// podABI, -// ethers.provider -// ); -// let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); -// // maxFee depends on if token has been deposited to PrizePool yet -// // since we called drop in previous test case, the tokens were deposited to PrizePool -// expect( -// maxFee, -// "Exit Fee equal to .99 DAI because token still in float" -// ).to.be.eq(ethers.utils.parseEther(".99")); - -// const spells = [ -// { -// connector: ptConnectorName, -// method: "withdrawFromPod", -// args: [DAI_POD_ADDR, amount, maxFee, 0, 0], -// }, -// ]; - -// // Before spell -// let daiToken = await ethers.getContractAt( -// abis.basic.erc20, -// DAI_TOKEN_ADDR -// ); -// let daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI Balance equals 0`).to.be.eq(0); - -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `POOL Token balance greater than 0`).to.be.gt(0); - -// let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); -// const podBalance = await podToken.balanceOf(dsaWallet0.address); -// expect(podBalance, `Pod DAI Token equals 99`).to.be.eq( -// ethers.utils.parseEther("99") -// ); - -// // Increase time by 11 days so we get back all DAI without early withdrawal fee -// await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect( -// daiBalance, -// `DAI balance equals 99, because of no early withdrawal fee` -// ).to.be.eq(ethers.utils.parseEther("99")); - -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalanceAfter, `POOL Token to be greater than 0`).to.be.gt(0); - -// const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); -// expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq(0); -// }); - -// it("Should deposit and withdraw from pod, get back same amount of 99 DAI", async function() { -// const amount = ethers.utils.parseEther("99"); -// const maxFee = 0; // maxFee 0 since it doesn't give chance for Pod to actually deposit into PrizePool - -// const spells = [ -// { -// connector: ptConnectorName, -// method: "depositToPod", -// args: [DAI_TOKEN_ADDR, DAI_POD_ADDR, amount, 0, 0], -// }, -// { -// connector: ptConnectorName, -// method: "withdrawFromPod", -// args: [DAI_POD_ADDR, amount, maxFee, 0, 0], -// }, -// ]; - -// // Before spell -// let daiToken = await ethers.getContractAt( -// abis.basic.erc20, -// DAI_TOKEN_ADDR -// ); -// let daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect(daiBalance, `DAI equals 99`).to.be.eq( -// ethers.utils.parseEther("99") -// ); - -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); - -// // PodToken is 0 -// let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); -// const podBalance = await podToken.balanceOf(dsaWallet0.address); -// expect(podBalance, `Pod DAI Token equals 0`).to.be.eq(0); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// daiBalance = await daiToken.balanceOf(dsaWallet0.address); -// expect( -// daiBalance, -// `DAI balance to be equal to 99, because funds still in 'float` -// ).to.be.eq(ethers.utils.parseEther("99")); - -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalanceAfter, `POOL Token same as before spell`).to.be.eq( -// poolBalance -// ); - -// // Expect Pod Token Balance to equal 0 -// const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); -// expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq( -// ethers.utils.parseEther("0") -// ); -// }); -// }); - -// describe("Main - UNISWAP POOL/ETH Prize Pool Test", function() { -// it("Should use uniswap to swap ETH for POOL, deposit to POOL/ETH LP, deposit POOL/ETH LP to PrizePool", async function() { -// const amount = ethers.utils.parseEther("100"); // 100 POOL -// const slippage = ethers.utils.parseEther("0.03"); -// const setId = "83478237"; - -// const UniswapV2Router02ABI = [ -// "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)", -// ]; - -// // Get amount of ETH for 100 POOL from Uniswap -// const UniswapV2Router02 = await ethers.getContractAt( -// UniswapV2Router02ABI, -// "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" -// ); -// const amounts = await UniswapV2Router02.getAmountsOut(amount, [ -// POOL_TOKEN_ADDRESS, -// WETH_ADDR, -// ]); -// const unitAmount = ethers.utils.parseEther( -// ((amounts[1] * 1.03) / amounts[0]).toString() -// ); - -// const spells = [ -// { -// connector: uniswapConnectorName, -// method: "buy", -// args: [ -// POOL_TOKEN_ADDRESS, -// tokens.eth.address, -// amount, -// unitAmount, -// 0, -// setId, -// ], -// }, -// { -// connector: uniswapConnectorName, -// method: "deposit", -// args: [ -// POOL_TOKEN_ADDRESS, -// tokens.eth.address, -// amount, -// unitAmount, -// slippage, -// 0, -// setId, -// ], -// }, -// { -// connector: ptConnectorName, -// method: "depositTo", -// args: [ -// UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, -// 0, -// PT_UNISWAP_POOLETHLP_TICKET_ADDR, -// setId, -// 0, -// ], -// }, -// ]; - -// // Before Spell -// let ethBalance = await ethers.provider.getBalance(dsaWallet0.address); -// expect(ethBalance, `ETH Balance equals 9`).to.be.eq( -// ethers.utils.parseEther("9") -// ); - -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `POOL Token greater than 0`).to.be.gte(0); - -// let uniswapLPToken = await ethers.getContractAt( -// abis.basic.erc20, -// UNISWAP_POOLETHLP_TOKEN_ADDR -// ); -// const uniswapPoolEthBalance = await uniswapLPToken.balanceOf( -// dsaWallet0.address -// ); -// expect(uniswapPoolEthBalance, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); - -// let ptUniswapPoolEthToken = await ethers.getContractAt( -// abis.basic.erc20, -// PT_UNISWAP_POOLETHLP_TICKET_ADDR -// ); -// const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf( -// dsaWallet0.address -// ); -// expect( -// ptUniswapPoolEthBalance, -// `PoolTogether Uniswap POOL?ETH LP equals 0` -// ).to.be.eq(0); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// ethBalance = await ethers.provider.getBalance(dsaWallet0.address); -// expect(ethBalance, `ETH Balance less than 9`).to.be.lt( -// ethers.utils.parseEther("9") -// ); - -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalanceAfter, `POOL Token to be same after spell`).to.be.eq( -// poolBalance -// ); - -// const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf( -// dsaWallet0.address -// ); -// expect( -// uniswapPoolEthBalanceAfter, -// `Uniswap POOL/ETH LP equals 0` -// ).to.be.eq(0); - -// const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf( -// dsaWallet0.address -// ); -// expect( -// ptUniswapPoolEthBalanceAfter, -// `PT Uniswap POOL/ETH LP to greater than 0` -// ).to.be.gt(0); -// }); - -// it("Should withdraw all PrizePool, get back Uniswap LP, claim POOL, deposit claimed POOL into Pool PrizePool", async function() { -// let ptUniswapPoolEthToken = await ethers.getContractAt( -// abis.basic.erc20, -// PT_UNISWAP_POOLETHLP_TICKET_ADDR -// ); -// const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf( -// dsaWallet0.address -// ); -// const setId = "83478237"; - -// let uniswapPrizePoolContract = new ethers.Contract( -// UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, -// prizePoolABI, -// ethers.provider -// ); -// let earlyExitFee = await uniswapPrizePoolContract.callStatic[ -// "calculateEarlyExitFee" -// ]( -// dsaWallet0.address, -// PT_UNISWAP_POOLETHLP_TICKET_ADDR, -// ptUniswapPoolEthBalance -// ); -// expect( -// earlyExitFee.exitFee, -// "Exit Fee equals 0 because no early exit fee for this prize pool" -// ).to.be.eq(0); - -// const spells = [ -// { -// connector: ptConnectorName, -// method: "withdrawInstantlyFrom", -// args: [ -// UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, -// ptUniswapPoolEthBalance, -// PT_UNISWAP_POOLETHLP_TICKET_ADDR, -// earlyExitFee.exitFee, -// 0, -// 0, -// ], -// }, -// { -// connector: ptConnectorName, -// method: "claim", -// args: [UNISWAP_POOLETHLP_FAUCET_ADDR, setId], -// }, -// { -// connector: ptConnectorName, -// method: "depositTo", -// args: [POOL_PRIZE_POOL_ADDR, 0, PT_POOL_TICKET_ADDR, setId, 0], -// }, -// ]; - -// // Before spell -// let poolToken = await ethers.getContractAt( -// abis.basic.erc20, -// POOL_TOKEN_ADDRESS -// ); -// const poolBalance = await poolToken.balanceOf(dsaWallet0.address); -// expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); - -// // Uniswap POOL/ETH LP is 0 -// let uniswapLPToken = await ethers.getContractAt( -// abis.basic.erc20, -// UNISWAP_POOLETHLP_TOKEN_ADDR -// ); -// const uniswapPoolEthBalance = await uniswapLPToken.balanceOf( -// dsaWallet0.address -// ); -// expect(uniswapPoolEthBalance, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); - -// expect( -// ptUniswapPoolEthBalance, -// `PT Uniswap POOL/ETH LP greater than 0` -// ).to.be.gt(0); - -// let poolPoolTicket = await ethers.getContractAt( -// abis.basic.erc20, -// PT_POOL_TICKET_ADDR -// ); -// const poolPoolTicketBalance = await poolPoolTicket.balanceOf( -// dsaWallet0.address -// ); -// expect( -// poolPoolTicketBalance, -// `PoolTogether POOL Ticket equals 0` -// ).to.be.eq(0); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); -// expect( -// poolBalanceAfter, -// `Pool Token Balance equal to balance before spell` -// ).to.be.eq(poolBalance); - -// const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf( -// dsaWallet0.address -// ); -// expect( -// uniswapPoolEthBalanceAfter, -// `Uniswap POOL/ETH LP to greater than 0` -// ).to.be.gt(0); - -// const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf( -// dsaWallet0.address -// ); -// expect( -// ptUniswapPoolEthBalanceAfter, -// `PT Uniswap POOL/ETH LP equal 0` -// ).to.be.eq(0); - -// const poolPoolTicketBalanceAfter = await poolPoolTicket.balanceOf( -// dsaWallet0.address -// ); -// expect( -// poolPoolTicketBalanceAfter, -// `PoolTogether POOL Ticket greater than 0` -// ).to.be.gt(0); -// }); -// }); - -// describe("Main - WETH Prize Pool Test", function() { -// it("Deposit 1 ETH into WETH Prize Pool and withdraw immediately", async function() { -// const amount = ethers.utils.parseEther("1"); // 1 ETH -// const setId = "83478237"; -// const spells = [ -// { -// connector: ptConnectorName, -// method: "depositTo", -// args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, setId], -// }, -// { -// connector: ptConnectorName, -// method: "withdrawInstantlyFrom", -// args: [ -// WETH_PRIZE_POOL_ADDR, -// amount, -// WETH_POOL_TICKET_ADDR, -// amount, -// setId, -// 0, -// ], -// }, -// ]; -// // Before Spell -// const ethBalanceBefore = await ethers.provider.getBalance( -// dsaWallet0.address -// ); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After spell -// const ethBalanceAfter = await ethers.provider.getBalance( -// dsaWallet0.address -// ); - -// // ETH used for transaction -// expect( -// ethBalanceAfter, -// `ETH Balance less than before spell because of early withdrawal fee` -// ).to.be.lte(ethBalanceBefore); -// }); - -// it("Deposit 1 ETH into WETH Prize Pool, wait 14 days, then withdraw", async function() { -// const amount = ethers.utils.parseEther("1"); // 1 ETH -// const depositSpell = [ -// { -// connector: ptConnectorName, -// method: "depositTo", -// args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, 0], -// }, -// ]; - -// const withdrawSpell = [ -// { -// connector: ptConnectorName, -// method: "withdrawInstantlyFrom", -// args: [ -// WETH_PRIZE_POOL_ADDR, -// amount, -// WETH_POOL_TICKET_ADDR, -// amount, -// 0, -// 0, -// ], -// }, -// ]; - -// // Before Deposit Spell -// let ethBalanceBefore = await ethers.provider.getBalance( -// dsaWallet0.address -// ); - -// // Run deposit spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(depositSpell), wallet1.address); -// const receipt = await tx.wait(); - -// // After Deposit spell -// let ethBalanceAfter = await ethers.provider.getBalance( -// dsaWallet0.address -// ); - -// expect(ethBalanceAfter, `ETH Balance less than before spell`).to.be.lte( -// ethBalanceBefore -// ); - -// // Increase time by 11 days so we get back all ETH without early withdrawal fee -// await ethers.provider.send("evm_increaseTime", [14 * 24 * 60 * 60]); - -// // Run withdraw spell transaction -// const tx2 = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(withdrawSpell), wallet1.address); -// const receipt2 = await tx.wait(); - -// // After Deposit spell -// ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); - -// expect( -// ethBalanceAfter, -// `ETH Balance equal to before spell because no early exit fee` -// ).to.be.eq(ethBalanceBefore); -// }); -// }); - -// describe("Main - WETH Pod Test", function() { -// let podAddress: string; -// it("Should deposit 1 ETH in WETH Pod and get Pod Ticket", async function() { -// const amount = ethers.utils.parseEther("1"); - -// // Create Pod for WETH Prize Pool (Rari) -// const podFactoryContract = new ethers.Contract( -// POD_FACTORY_ADDRESS, -// podFactoryABI, -// masterSigner -// ); -// podAddress = await podFactoryContract.callStatic.create( -// WETH_PRIZE_POOL_ADDR, -// WETH_POOL_TICKET_ADDR, -// constants.address_zero, -// wallet0.address, -// 18 -// ); -// await podFactoryContract.create( -// WETH_PRIZE_POOL_ADDR, -// WETH_POOL_TICKET_ADDR, -// constants.address_zero, -// wallet0.address, -// 18 -// ); - -// const spells = [ -// { -// connector: ptConnectorName, -// method: "depositToPod", -// args: [WETH_ADDR, podAddress, amount, 0, 0], -// }, -// ]; - -// // Before Deposit Spell -// const podContract = new ethers.Contract( -// podAddress, -// podABI, -// ethers.provider -// ); -// let podBalanceBefore = await podContract.balanceOfUnderlying( -// dsaWallet0.address -// ); -// expect(podBalanceBefore, `Pod balance equal to 0`).to.be.eq(0); - -// let ethBalanceBefore = await ethers.provider.getBalance( -// dsaWallet0.address -// ); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After Deposit spell -// let ethBalanceAfter = await ethers.provider.getBalance( -// dsaWallet0.address -// ); -// expect(ethBalanceAfter, `ETH balance less than before`).to.be.lt( -// ethBalanceBefore -// ); - -// let podBalanceAfter = await podContract.balanceOfUnderlying( -// dsaWallet0.address -// ); -// expect(podBalanceAfter, `Pod balance equal to 1`).to.be.eq( -// ethers.utils.parseEther("1") -// ); -// }); - -// it("Should withdraw 1 Ticket from WETH Pod and get back ETH", async function() { -// const amount = ethers.utils.parseEther("1"); - -// const podContract = new ethers.Contract( -// podAddress, -// podABI, -// ethers.provider -// ); -// let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); -// expect( -// maxFee, -// "Exit Fee equal to 0 DAI because token still in float" -// ).to.be.eq(0); -// // maxFee depends on if token has been deposited to PrizePool yet - -// const spells = [ -// { -// connector: ptConnectorName, -// method: "withdrawFromPod", -// args: [podAddress, amount, maxFee, 0, 0], -// }, -// ]; - -// // Before Deposit Spell -// let podBalanceBefore = await podContract.balanceOfUnderlying( -// dsaWallet0.address -// ); -// expect(podBalanceBefore, `Pod balance equal to 1`).to.be.eq( -// ethers.utils.parseEther("1") -// ); - -// let ethBalanceBefore = await ethers.provider.getBalance( -// dsaWallet0.address -// ); - -// // Run spell transaction -// const tx = await dsaWallet0 -// .connect(wallet0) -// .cast(...encodeSpells(spells), wallet1.address); -// const receipt = await tx.wait(); - -// // After Deposit spell -// let ethBalanceAfter = await ethers.provider.getBalance( -// dsaWallet0.address -// ); -// expect(ethBalanceAfter, `ETH balance greater than before`).to.be.gt( -// ethBalanceBefore -// ); - -// let podBalanceAfter = await podContract.balanceOfUnderlying( -// dsaWallet0.address -// ); -// expect(podBalanceAfter, `Pod balance equal to 0`).to.be.eq( -// ethers.utils.parseEther("0") -// ); -// }); -// }); -// }); +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; + +import { addresses } from "../../../scripts/constant/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { constants } from "../../../scripts/constant/constant"; +import { tokens } from "../../../scripts/constant/tokens"; +import type { Signer, Contract } from "ethers"; + +import { + ConnectV2Compound__factory, + ConnectV2PoolTogether__factory, + ConnectV2UniswapV2__factory, +} from "../../../typechain"; + +const DAI_TOKEN_ADDR = tokens.dai.address; // DAI Token + +// PoolTogether Address: https://docs.pooltogether.com/resources/networks/ethereum +const DAI_PRIZE_POOL_ADDR = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a"; // DAI Prize Pool +const PT_DAI_TICKET_ADDR = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF"; // PT DAI Ticket +const DAI_POOL_FAUCET_ADDR = "0xF362ce295F2A4eaE4348fFC8cDBCe8d729ccb8Eb"; // DAI POOL Faucet +const POOL_TOKEN_ADDRESS = "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e"; // POOL Tocken +const TOKEN_FAUCET_PROXY_FACTORY_ADDR = + "0xE4E9cDB3E139D7E8a41172C20b6Ed17b6750f117"; // TokenFaucetProxyFactory for claimAll +const DAI_POD_ADDR = "0x2f994e2E4F3395649eeE8A89092e63Ca526dA829"; // DAI Pod +const UNISWAP_POOLETHLP_PRIZE_POOL_ADDR = + "0x3AF7072D29Adde20FC7e173a7CB9e45307d2FB0A"; // Uniswap Pool/ETH LP PrizePool +const UNISWAP_POOLETHLP_FAUCET_ADDR = + "0x9A29401EF1856b669f55Ae5b24505b3B6fAEb370"; // Uniswap Pool/ETH LP Faucet +const UNISWAP_POOLETHLP_TOKEN_ADDR = + "0x85cb0bab616fe88a89a35080516a8928f38b518b"; // Uniswap Pool/ETH Token +const PT_UNISWAP_POOLETHLP_TICKET_ADDR = + "0xeb8928ee92efb06c44d072a24c2bcb993b61e543"; // Pool Together Uniswap Pool/ETH LP Ticket +const POOL_PRIZE_POOL_ADDR = "0x396b4489da692788e327e2e4b2b0459a5ef26791"; // POOL Prize Pool +const PT_POOL_TICKET_ADDR = "0x27d22a7648e955e510a40bdb058333e9190d12d4"; // Pool Together POOL Ticket +const WETH_ADDR = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; // WETH +const DAI_POD_TOKEN_DROP = "0xc5209623E3dFdf9C0cCbe497c8012883C4147731"; + +// Community WETH Prize Pool (Rari): https://reference-app.pooltogether.com/pools/mainnet/0xa88ca010b32a54d446fc38091ddbca55750cbfc3/manage#stats +const WETH_PRIZE_POOL_ADDR = "0xa88ca010b32a54d446fc38091ddbca55750cbfc3"; // Community WETH Prize Pool (Rari) +const WETH_POOL_TICKET_ADDR = "0x9b5c30aeb9ce2a6a121cea9a85bc0d662f6d9b40"; // Community WETH Prize Pool Ticket (Rari) + +const prizePoolABI = [ + "function calculateEarlyExitFee( address from, address controlledToken, uint256 amount) external returns ( uint256 exitFee, uint256 burnedCredit)", +]; + +const podABI = [ + "function getEarlyExitFee(uint256 amount) external returns (uint256)", + "function balanceOfUnderlying(address user) external view returns (uint256 amount)", + "function drop() public returns (uint256)", + "function balanceOf(address account) external view returns (uint256)", +]; + +const POD_FACTORY_ADDRESS = "0x4e3a9f9fbafb2ec49727cffa2a411f7a0c1c4ce1"; +const podFactoryABI = [ + "function create( address _prizePool, address _ticket, address _faucet, address _manager, uint8 _decimals) external returns (address pod)", +]; + +const tokenDropABI = [ + "function claim(address user) external returns (uint256)", +]; + +describe("PoolTogether", function() { + const connectorName = "COMPOUND-TEST-A"; + const uniswapConnectorName = "UNISWAP-TEST-A"; + const ptConnectorName = "POOLTOGETHER-TEST-A"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let ptConnector: any; + let uniswapConnector: any; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + + // Deploy and enable Compound Connector + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Compound__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + + // Deploy and enable Pool Together Connector + ptConnector = await deployAndEnableConnector({ + connectorName: ptConnectorName, + contractArtifact: ConnectV2PoolTogether__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + + // Deploy and enable Uniswap Connector + uniswapConnector = await deployAndEnableConnector({ + connectorName: uniswapConnectorName, + contractArtifact: ConnectV2UniswapV2__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!ptConnector.address).to.be.true; + expect(!!uniswapConnector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit 10 ETH into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + + describe("Main - DAI Prize Pool Test", function() { + it("Should deposit 1 ETH in Compound", async function() { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", amount, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should borrow 100 DAI from Compound and deposit DAI into DAI Prize Pool", async function() { + const amount = ethers.utils.parseEther("100"); // 100 DAI + const setId = "83478237"; + const spells = [ + { + connector: connectorName, + method: "borrow", + args: ["DAI-A", amount, 0, setId], + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, setId, 0], + }, + ]; + // Before Spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance is 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_DAI_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether DAI Ticket balance is 0`).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `Expect DAI balance to still equal 0 since it was deposited into Prize Pool` + ).to.be.eq(0); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect( + balanceAfter, + `PoolTogether DAI Ticket balance equals 100` + ).to.be.eq(ethers.utils.parseEther("100")); + + // ETH used for transaction + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should wait 11 days, withdraw all PrizePool, get back 100 DAI, and claim POOL", async function() { + const amount = ethers.utils.parseEther("100"); // 100 DAI + + let prizePoolContract = new ethers.Contract( + DAI_PRIZE_POOL_ADDR, + prizePoolABI, + ethers.provider + ); + let earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 1 DAI because starts at 10%" + ).to.be.eq(ethers.utils.parseEther("1")); + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + DAI_PRIZE_POOL_ADDR, + amount, + PT_DAI_TICKET_ADDR, + earlyExitFee.exitFee, + 0, + 0, + ], + }, + { + connector: ptConnectorName, + method: "claim", + args: [DAI_POOL_FAUCET_ADDR, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance equals 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_DAI_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether Dai Ticket is 100`).to.be.eq( + ethers.utils.parseEther("100") + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token equals 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); + + earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 0 DAI because past 10 days" + ).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `DAI balance to be equal to 100, because of no early withdrawal fee` + ).to.be.eq(ethers.utils.parseEther("100")); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether Dai Ticket to equal 0`).to.be.eq(0); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect( + poolBalanceAfter, + `POOL Token Balance to be greater than 0` + ).to.be.gt(ethers.utils.parseEther("0")); + }); + + it("Should deposit and withdraw all PrizePool, get back less than 100 DAI", async function() { + const amount = ethers.utils.parseEther("100"); // 100 DAI + const exitFee = ethers.utils.parseEther("1"); // 1 DAI is 10% of 100 DAI + const spells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, 0, 0], + }, + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + DAI_PRIZE_POOL_ADDR, + amount, + PT_DAI_TICKET_ADDR, + exitFee, + 0, + 0, + ], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI Balance equals 0`).to.be.eq( + ethers.utils.parseEther("100") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_DAI_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether DAI Ticket equals 0`).to.be.eq(0); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `PoolTogether Token greater than 0`).to.be.gt(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `DAI balance to be less than 100, because of early withdrawal fee` + ).to.be.lt(ethers.utils.parseEther("100")); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether Dai Ticket to equal 0`).to.be.eq(0); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token Balance to greater than 0`).to.be.gt( + ethers.utils.parseEther("0") + ); + }); + + it("Should deposit, wait 11 days, and withdraw all PrizePool, get 99 DAI, and claim all POOL using claimAll", async function() { + const amount = ethers.utils.parseEther("99"); // 99 DAI + const depositSpells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, 0, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance less than 100`).to.be.lt( + ethers.utils.parseEther("100") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_DAI_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether DAI Ticket equal 0`).to.be.eq(0); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token is greater than 0`).to.be.gt( + ethers.utils.parseEther("0") + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(depositSpells), wallet1.address); + const receipt = await tx.wait(); + + const prizePoolContract = new ethers.Contract( + DAI_PRIZE_POOL_ADDR, + prizePoolABI, + ethers.provider + ); + let earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to .99 DAI because starts at 10%" + ).to.be.eq(ethers.utils.parseEther(".99")); + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); + + earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 0 DAI because past 10 days" + ).to.be.eq(0); + + const withdrawSpells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + DAI_PRIZE_POOL_ADDR, + amount, + PT_DAI_TICKET_ADDR, + earlyExitFee.exitFee, + 0, + 0, + ], + }, + { + connector: ptConnectorName, + method: "claimAll", + args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, [DAI_POOL_FAUCET_ADDR]], + }, + ]; + + // Run spell transaction + const tx2 = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(withdrawSpells), wallet1.address); + const receipt2 = await tx2.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance equals 99`).to.be.eq( + ethers.utils.parseEther("99") + ); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether DAI Ticket equal 0`).to.be.eq(0); + + // Expect + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `Pool Token to be greateir than 0`).to.be.gt( + ethers.utils.parseEther("0") + ); + }); + }); + + describe("Main - DAI Pod Test", function() { + it("Should deposit 99 DAI in DAI Pod", async function() { + const amount = ethers.utils.parseEther("99"); // 99 DAI + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [DAI_TOKEN_ADDR, DAI_POD_ADDR, amount, 0, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance equals 99`).to.be.eq( + ethers.utils.parseEther("99") + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gte(0); + + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); + const podBalance = await podToken.balanceOf(dsaWallet0.address); + expect(podBalance, `Pod DAI Token equals 0`).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI equals 0`).to.be.eq(0); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token greater than 0`).to.be.gt(0); + + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); + expect(podBalanceAfter, `Pod DAI token greater than 0`).to.be.eq( + ethers.utils.parseEther("99") + ); + }); + + it("Should claim rewards from pod token drop", async function() { + const spells = [ + { + connector: ptConnectorName, + method: "claimPodTokenDrop", + args: [DAI_POD_TOKEN_DROP, 0], + }, + ]; + + const tokenDropContract = new ethers.Contract( + DAI_POD_TOKEN_DROP, + tokenDropABI, + ethers.provider + ); + const podContract = new ethers.Contract( + DAI_POD_ADDR, + podABI, + masterSigner + ); + + // drop(): Claim TokenDrop asset for PrizePool Pod and transfers token(s) to external Pod TokenDrop + // dropt() also calls batch which, Deposit Pod float into PrizePool. Deposits the current float + // amount into the PrizePool and claims current POOL rewards. + const dropTx = await podContract.drop(); + await dropTx.wait(); + + // POOL Rewards able to claim from Pod Token Drop + let claimAmount = await tokenDropContract.callStatic["claim"]( + dsaWallet0.address + ); + + // Before spell + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + const total = claimAmount.add(poolBalance); + expect(poolBalanceAfter, `POOL Token same as before spell`).to.be.eq( + total + ); + }); + + it("Should wait 11 days, withdraw all podTokens, get back 99 DAI", async function() { + const amount = ethers.utils.parseEther("99"); // 99 DAI + + const podContract = new ethers.Contract( + DAI_POD_ADDR, + podABI, + ethers.provider + ); + let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); + // maxFee depends on if token has been deposited to PrizePool yet + // since we called drop in previous test case, the tokens were deposited to PrizePool + expect( + maxFee, + "Exit Fee equal to .99 DAI because token still in float" + ).to.be.eq(ethers.utils.parseEther(".99")); + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [DAI_POD_ADDR, amount, maxFee, 0, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI Balance equals 0`).to.be.eq(0); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token balance greater than 0`).to.be.gt(0); + + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); + const podBalance = await podToken.balanceOf(dsaWallet0.address); + expect(podBalance, `Pod DAI Token equals 99`).to.be.eq( + ethers.utils.parseEther("99") + ); + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `DAI balance equals 99, because of no early withdrawal fee` + ).to.be.eq(ethers.utils.parseEther("99")); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token to be greater than 0`).to.be.gt(0); + + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); + expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq(0); + }); + + it("Should deposit and withdraw from pod, get back same amount of 99 DAI", async function() { + const amount = ethers.utils.parseEther("99"); + const maxFee = 0; // maxFee 0 since it doesn't give chance for Pod to actually deposit into PrizePool + + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [DAI_TOKEN_ADDR, DAI_POD_ADDR, amount, 0, 0], + }, + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [DAI_POD_ADDR, amount, maxFee, 0, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI equals 99`).to.be.eq( + ethers.utils.parseEther("99") + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); + + // PodToken is 0 + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); + const podBalance = await podToken.balanceOf(dsaWallet0.address); + expect(podBalance, `Pod DAI Token equals 0`).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `DAI balance to be equal to 99, because funds still in 'float` + ).to.be.eq(ethers.utils.parseEther("99")); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token same as before spell`).to.be.eq( + poolBalance + ); + + // Expect Pod Token Balance to equal 0 + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); + expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + }); + }); + + describe("Main - UNISWAP POOL/ETH Prize Pool Test", function() { + it("Should use uniswap to swap ETH for POOL, deposit to POOL/ETH LP, deposit POOL/ETH LP to PrizePool", async function() { + const amount = ethers.utils.parseEther("100"); // 100 POOL + const slippage = ethers.utils.parseEther("0.03"); + const setId = "83478237"; + + const UniswapV2Router02ABI = [ + "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)", + ]; + + // Get amount of ETH for 100 POOL from Uniswap + const UniswapV2Router02 = await ethers.getContractAt( + UniswapV2Router02ABI, + "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" + ); + const amounts = await UniswapV2Router02.getAmountsOut(amount, [ + POOL_TOKEN_ADDRESS, + WETH_ADDR, + ]); + const unitAmount = ethers.utils.parseEther( + ((amounts[1] * 1.03) / amounts[0]).toString() + ); + + const spells = [ + { + connector: uniswapConnectorName, + method: "buy", + args: [ + POOL_TOKEN_ADDRESS, + tokens.eth.address, + amount, + unitAmount, + 0, + setId, + ], + }, + { + connector: uniswapConnectorName, + method: "deposit", + args: [ + POOL_TOKEN_ADDRESS, + tokens.eth.address, + amount, + unitAmount, + slippage, + 0, + setId, + ], + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [ + UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, + 0, + PT_UNISWAP_POOLETHLP_TICKET_ADDR, + setId, + 0, + ], + }, + ]; + + // Before Spell + let ethBalance = await ethers.provider.getBalance(dsaWallet0.address); + expect(ethBalance, `ETH Balance equals 9`).to.be.eq( + ethers.utils.parseEther("9") + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gte(0); + + let uniswapLPToken = await ethers.getContractAt( + abis.basic.erc20, + UNISWAP_POOLETHLP_TOKEN_ADDR + ); + const uniswapPoolEthBalance = await uniswapLPToken.balanceOf( + dsaWallet0.address + ); + expect(uniswapPoolEthBalance, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); + + let ptUniswapPoolEthToken = await ethers.getContractAt( + abis.basic.erc20, + PT_UNISWAP_POOLETHLP_TICKET_ADDR + ); + const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf( + dsaWallet0.address + ); + expect( + ptUniswapPoolEthBalance, + `PoolTogether Uniswap POOL?ETH LP equals 0` + ).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + ethBalance = await ethers.provider.getBalance(dsaWallet0.address); + expect(ethBalance, `ETH Balance less than 9`).to.be.lt( + ethers.utils.parseEther("9") + ); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token to be same after spell`).to.be.eq( + poolBalance + ); + + const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf( + dsaWallet0.address + ); + expect( + uniswapPoolEthBalanceAfter, + `Uniswap POOL/ETH LP equals 0` + ).to.be.eq(0); + + const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf( + dsaWallet0.address + ); + expect( + ptUniswapPoolEthBalanceAfter, + `PT Uniswap POOL/ETH LP to greater than 0` + ).to.be.gt(0); + }); + + it("Should withdraw all PrizePool, get back Uniswap LP, claim POOL, deposit claimed POOL into Pool PrizePool", async function() { + let ptUniswapPoolEthToken = await ethers.getContractAt( + abis.basic.erc20, + PT_UNISWAP_POOLETHLP_TICKET_ADDR + ); + const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf( + dsaWallet0.address + ); + const setId = "83478237"; + + let uniswapPrizePoolContract = new ethers.Contract( + UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, + prizePoolABI, + ethers.provider + ); + let earlyExitFee = await uniswapPrizePoolContract.callStatic[ + "calculateEarlyExitFee" + ]( + dsaWallet0.address, + PT_UNISWAP_POOLETHLP_TICKET_ADDR, + ptUniswapPoolEthBalance + ); + expect( + earlyExitFee.exitFee, + "Exit Fee equals 0 because no early exit fee for this prize pool" + ).to.be.eq(0); + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, + ptUniswapPoolEthBalance, + PT_UNISWAP_POOLETHLP_TICKET_ADDR, + earlyExitFee.exitFee, + 0, + 0, + ], + }, + { + connector: ptConnectorName, + method: "claim", + args: [UNISWAP_POOLETHLP_FAUCET_ADDR, setId], + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [POOL_PRIZE_POOL_ADDR, 0, PT_POOL_TICKET_ADDR, setId, 0], + }, + ]; + + // Before spell + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); + + // Uniswap POOL/ETH LP is 0 + let uniswapLPToken = await ethers.getContractAt( + abis.basic.erc20, + UNISWAP_POOLETHLP_TOKEN_ADDR + ); + const uniswapPoolEthBalance = await uniswapLPToken.balanceOf( + dsaWallet0.address + ); + expect(uniswapPoolEthBalance, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); + + expect( + ptUniswapPoolEthBalance, + `PT Uniswap POOL/ETH LP greater than 0` + ).to.be.gt(0); + + let poolPoolTicket = await ethers.getContractAt( + abis.basic.erc20, + PT_POOL_TICKET_ADDR + ); + const poolPoolTicketBalance = await poolPoolTicket.balanceOf( + dsaWallet0.address + ); + expect( + poolPoolTicketBalance, + `PoolTogether POOL Ticket equals 0` + ).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect( + poolBalanceAfter, + `Pool Token Balance equal to balance before spell` + ).to.be.eq(poolBalance); + + const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf( + dsaWallet0.address + ); + expect( + uniswapPoolEthBalanceAfter, + `Uniswap POOL/ETH LP to greater than 0` + ).to.be.gt(0); + + const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf( + dsaWallet0.address + ); + expect( + ptUniswapPoolEthBalanceAfter, + `PT Uniswap POOL/ETH LP equal 0` + ).to.be.eq(0); + + const poolPoolTicketBalanceAfter = await poolPoolTicket.balanceOf( + dsaWallet0.address + ); + expect( + poolPoolTicketBalanceAfter, + `PoolTogether POOL Ticket greater than 0` + ).to.be.gt(0); + }); + }); + + describe("Main - WETH Prize Pool Test", function() { + it("Deposit 1 ETH into WETH Prize Pool and withdraw immediately", async function() { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const setId = "83478237"; + const spells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, setId], + }, + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + WETH_PRIZE_POOL_ADDR, + amount, + WETH_POOL_TICKET_ADDR, + amount, + setId, + 0, + ], + }, + ]; + // Before Spell + const ethBalanceBefore = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + const ethBalanceAfter = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // ETH used for transaction + expect( + ethBalanceAfter, + `ETH Balance less than before spell because of early withdrawal fee` + ).to.be.lte(ethBalanceBefore); + }); + + it("Deposit 1 ETH into WETH Prize Pool, wait 14 days, then withdraw", async function() { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const depositSpell = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, 0], + }, + ]; + + const withdrawSpell = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + WETH_PRIZE_POOL_ADDR, + amount, + WETH_POOL_TICKET_ADDR, + amount, + 0, + 0, + ], + }, + ]; + + // Before Deposit Spell + let ethBalanceBefore = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // Run deposit spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(depositSpell), wallet1.address); + const receipt = await tx.wait(); + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance( + dsaWallet0.address + ); + + expect(ethBalanceAfter, `ETH Balance less than before spell`).to.be.lte( + ethBalanceBefore + ); + + // Increase time by 11 days so we get back all ETH without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [14 * 24 * 60 * 60]); + + // Run withdraw spell transaction + const tx2 = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(withdrawSpell), wallet1.address); + const receipt2 = await tx.wait(); + + // After Deposit spell + ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + + expect( + ethBalanceAfter, + `ETH Balance equal to before spell because no early exit fee` + ).to.be.eq(ethBalanceBefore); + }); + }); + + describe("Main - WETH Pod Test", function() { + let podAddress: string; + it("Should deposit 1 ETH in WETH Pod and get Pod Ticket", async function() { + const amount = ethers.utils.parseEther("1"); + + // Create Pod for WETH Prize Pool (Rari) + const podFactoryContract = new ethers.Contract( + POD_FACTORY_ADDRESS, + podFactoryABI, + masterSigner + ); + podAddress = await podFactoryContract.callStatic.create( + WETH_PRIZE_POOL_ADDR, + WETH_POOL_TICKET_ADDR, + constants.address_zero, + wallet0.address, + 18 + ); + await podFactoryContract.create( + WETH_PRIZE_POOL_ADDR, + WETH_POOL_TICKET_ADDR, + constants.address_zero, + wallet0.address, + 18 + ); + + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [WETH_ADDR, podAddress, amount, 0, 0], + }, + ]; + + // Before Deposit Spell + const podContract = new ethers.Contract( + podAddress, + podABI, + ethers.provider + ); + let podBalanceBefore = await podContract.balanceOfUnderlying( + dsaWallet0.address + ); + expect(podBalanceBefore, `Pod balance equal to 0`).to.be.eq(0); + + let ethBalanceBefore = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance( + dsaWallet0.address + ); + expect(ethBalanceAfter, `ETH balance less than before`).to.be.lt( + ethBalanceBefore + ); + + let podBalanceAfter = await podContract.balanceOfUnderlying( + dsaWallet0.address + ); + expect(podBalanceAfter, `Pod balance equal to 1`).to.be.eq( + ethers.utils.parseEther("1") + ); + }); + + it("Should withdraw 1 Ticket from WETH Pod and get back ETH", async function() { + const amount = ethers.utils.parseEther("1"); + + const podContract = new ethers.Contract( + podAddress, + podABI, + ethers.provider + ); + let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); + expect( + maxFee, + "Exit Fee equal to 0 DAI because token still in float" + ).to.be.eq(0); + // maxFee depends on if token has been deposited to PrizePool yet + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [podAddress, amount, maxFee, 0, 0], + }, + ]; + + // Before Deposit Spell + let podBalanceBefore = await podContract.balanceOfUnderlying( + dsaWallet0.address + ); + expect(podBalanceBefore, `Pod balance equal to 1`).to.be.eq( + ethers.utils.parseEther("1") + ); + + let ethBalanceBefore = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance( + dsaWallet0.address + ); + expect(ethBalanceAfter, `ETH balance greater than before`).to.be.gt( + ethBalanceBefore + ); + + let podBalanceAfter = await podContract.balanceOfUnderlying( + dsaWallet0.address + ); + expect(podBalanceAfter, `Pod balance equal to 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + }); + }); +});