mirror of
				https://github.com/Instadapp/dsa-connectors.git
				synced 2024-07-29 22:37:00 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2709 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2709 lines
		
	
	
		
			91 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const hre = require("hardhat");
 | 
						|
const { expect } = require("chai");
 | 
						|
 | 
						|
// Instadapp deployment and testing helpers
 | 
						|
const buildDSAv2 = require("../../scripts/buildDSAv2");
 | 
						|
const encodeSpells = require("../../scripts/encodeSpells.js");
 | 
						|
 | 
						|
// Liquity smart contracts
 | 
						|
const contracts = require("./liquity.contracts");
 | 
						|
 | 
						|
// Liquity helpers
 | 
						|
const helpers = require("./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 () => {
 | 
						|
    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);
 | 
						|
        });
 | 
						|
      });
 | 
						|
    });
 | 
						|
  });
 | 
						|
});
 |