From 97db94fe595330743385f26c30e15ab28cc18063 Mon Sep 17 00:00:00 2001 From: Edward Mulraney Date: Fri, 4 Jun 2021 12:18:16 +0100 Subject: [PATCH] add test for instadapp event return values. address PR comments: - add setUints to all applicable functions - use uint256 rather than uint for event data --- .../mainnet/connectors/liquity/events.sol | 6 +- .../mainnet/connectors/liquity/interface.sol | 7 + contracts/mainnet/connectors/liquity/main.sol | 103 +- .../{liquity.abi.js => liquity.contracts.js} | 7 + test/liquity/liquity.helpers.js | 67 +- test/liquity/liquity.test.js | 2640 +++++++++++------ 6 files changed, 1844 insertions(+), 986 deletions(-) rename test/liquity/{liquity.abi.js => liquity.contracts.js} (94%) diff --git a/contracts/mainnet/connectors/liquity/events.sol b/contracts/mainnet/connectors/liquity/events.sol index ce191ddd..196df9ee 100644 --- a/contracts/mainnet/connectors/liquity/events.sol +++ b/contracts/mainnet/connectors/liquity/events.sol @@ -28,15 +28,15 @@ contract Events { uint getRepayId, uint setBorrowId ); - event LogClaimCollateralFromRedemption(address indexed borrower); + event LogClaimCollateralFromRedemption(address indexed borrower, uint amount, uint setId); /* Stability Pool */ event LogStabilityDeposit(address indexed borrower, uint amount, address frontendTag, uint getId); event LogStabilityWithdraw(address indexed borrower, uint amount, uint setId); - event LogStabilityMoveEthGainToTrove(address indexed borrower); + event LogStabilityMoveEthGainToTrove(address indexed borrower, uint amount); /* Staking */ event LogStake(address indexed borrower, uint amount, uint getId); event LogUnstake(address indexed borrower, uint amount, uint setId); - event LogClaimGains(address indexed borrower); + event LogClaimStakingGains(address indexed borrower, uint ethAmount, uint lusdAmount); } diff --git a/contracts/mainnet/connectors/liquity/interface.sol b/contracts/mainnet/connectors/liquity/interface.sol index 53dabea3..72e91e98 100644 --- a/contracts/mainnet/connectors/liquity/interface.sol +++ b/contracts/mainnet/connectors/liquity/interface.sol @@ -52,9 +52,16 @@ interface StabilityPoolLike { function provideToSP(uint _amount, address _frontEndTag) external; function withdrawFromSP(uint _amount) external; function withdrawETHGainToTrove(address _upperHint, address _lowerHint) external; + function getDepositorETHGain(address _depositor) external view returns (uint); } interface StakingLike { function stake(uint _LQTYamount) external; function unstake(uint _LQTYamount) external; + function getPendingETHGain(address _user) external view returns (uint); + function getPendingLUSDGain(address _user) external view returns (uint); +} + +interface CollateralSurplusLike { + function getCollateral(address _account) external view returns (uint); } diff --git a/contracts/mainnet/connectors/liquity/main.sol b/contracts/mainnet/connectors/liquity/main.sol index 56db5302..4550bb1b 100644 --- a/contracts/mainnet/connectors/liquity/main.sol +++ b/contracts/mainnet/connectors/liquity/main.sol @@ -4,12 +4,17 @@ pragma solidity ^0.7.6; * @title Liquity. * @dev Lending & Borrowing. */ -import "hardhat/console.sol"; - -import { BorrowerOperationsLike, TroveManagerLike, StabilityPoolLike, StakingLike } from "./interface.sol"; +import { + BorrowerOperationsLike, + TroveManagerLike, + StabilityPoolLike, + StakingLike, + CollateralSurplusLike +} from "./interface.sol"; import { Stores } from "../../common/stores.sol"; import { Helpers } from "./helpers.sol"; import { Events } from "./events.sol"; +import "hardhat/console.sol"; abstract contract LiquityResolver is Events, Helpers { BorrowerOperationsLike internal constant borrowerOperations = @@ -20,7 +25,10 @@ abstract contract LiquityResolver is Events, Helpers { StabilityPoolLike(0x66017D22b0f8556afDd19FC67041899Eb65a21bb); StakingLike internal constant staking = StakingLike(0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d); - + CollateralSurplusLike internal constant collateralSurplus = + CollateralSurplusLike(0x3D32e8b97Ed5881324241Cf03b2DA5E2EBcE5521); + + // Prevents stack-too-deep error struct AdjustTrove { uint maxFeePercentage; uint withdrawAmount; @@ -30,10 +38,6 @@ abstract contract LiquityResolver is Events, Helpers { bool isBorrow; } - constructor() { - console.log("Liquity Connector contract deployed at", address(this)); - } - /* Begin: Trove */ /** @@ -56,7 +60,17 @@ abstract contract LiquityResolver is Events, Helpers { uint getId, uint setId ) external payable returns (string memory _eventName, bytes memory _eventParam) { - // User can either send ETH directly or have it collected from a previous spell + /* + User has three options for depositing ETH collateral to open a Trove: + - Send ETH directly with this function call + - Have ETH collected from a previous spell + - Have ETH collected from the DSA's existing balance + */ + + if (getId != 0 && depositAmount != 0) { + revert("open(): Cannot supply a depositAmount if a non-zero getId is supplied"); + } + depositAmount = getUint(getId, depositAmount); borrowerOperations.openTrove{value: depositAmount}( @@ -68,7 +82,7 @@ abstract contract LiquityResolver is Events, Helpers { // Allow other spells to use the borrowed amount setUint(setId, borrowAmount); - _eventName = "LogOpen(address,uint,uint,uint,uint,uint)"; + _eventName = "LogOpen(address,uint256,uint256,uint256,uint256,uint256)"; _eventParam = abi.encode(msg.sender, maxFeePercentage, depositAmount, borrowAmount, getId, setId); } @@ -83,7 +97,7 @@ abstract contract LiquityResolver is Events, Helpers { // Allow other spells to use the collateral released from the Trove setUint(setId, collateral); - _eventName = "LogClose(address,uint)"; + _eventName = "LogClose(address,uint256)"; _eventParam = abi.encode(msg.sender, setId); } @@ -101,9 +115,12 @@ abstract contract LiquityResolver is Events, Helpers { address lowerHint, uint getId ) external payable returns (string memory _eventName, bytes memory _eventParam) { + if (getId != 0 && amount != 0) { + revert("deposit(): Cannot supply an amount if a non-zero getId is supplied"); + } amount = getUint(getId, amount); borrowerOperations.addColl{value: amount}(upperHint, lowerHint); - _eventName = "LogDeposit(address,uint,uint)"; + _eventName = "LogDeposit(address,uint256,uint256)"; _eventParam = abi.encode(msg.sender, amount, getId); } @@ -124,7 +141,7 @@ abstract contract LiquityResolver is Events, Helpers { borrowerOperations.withdrawColl(amount, upperHint, lowerHint); setUint(setId, amount); - _eventName = "LogWithdraw(address,uint,uint)"; + _eventName = "LogWithdraw(address,uint256,uint256)"; _eventParam = abi.encode(msg.sender, amount, setId); } @@ -147,7 +164,7 @@ abstract contract LiquityResolver is Events, Helpers { borrowerOperations.withdrawLUSD(maxFeePercentage, amount, upperHint, lowerHint); setUint(setId, amount); // TODO: apply fee / get exact amount borrowed (with the fee applied) - _eventName = "LogBorrow(address,uint,uint)"; + _eventName = "LogBorrow(address,uint256,uint256)"; _eventParam = abi.encode(msg.sender, amount, setId); } @@ -165,9 +182,12 @@ abstract contract LiquityResolver is Events, Helpers { address lowerHint, uint getId ) external payable returns (string memory _eventName, bytes memory _eventParam) { + if (getId != 0 && amount != 0) { + revert("repay(): Cannot supply an amount if a non-zero getId is supplied"); + } amount = getUint(getId, amount); borrowerOperations.repayLUSD(amount, upperHint, lowerHint); - _eventName = "LogRepay(address,uint,uint)"; + _eventName = "LogRepay(address,uint256,uint256)"; _eventParam = abi.encode(msg.sender, amount, getId); } @@ -199,6 +219,13 @@ abstract contract LiquityResolver is Events, Helpers { uint getRepayId, uint setBorrowId ) external payable returns (string memory _eventName, bytes memory _eventParam) { + if (getDepositId != 0 && depositAmount != 0) { + revert("adjust(): Cannot supply a depositAmount if a non-zero getDepositId is supplied"); + } + if (getRepayId != 0 && repayAmount != 0) { + revert("adjust(): Cannot supply a repayAmount if a non-zero getRepayId is supplied"); + } + AdjustTrove memory adjustTrove; adjustTrove.maxFeePercentage = maxFeePercentage; @@ -223,18 +250,22 @@ abstract contract LiquityResolver is Events, Helpers { // Allow other spells to use the borrowed amount setUint(setBorrowId, borrowAmount); - _eventName = "LogAdjust(address,uint,uint,uint,uint,uint,uint,uint,uint,uint)"; - _eventParam = abi.encode(msg.sender, maxFeePercentage, depositAmount, borrowAmount, getDepositId, setWithdrawId, getRepayId, setBorrowId); + _eventName = "LogAdjust(address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode(msg.sender, maxFeePercentage, depositAmount, withdrawAmount, borrowAmount, repayAmount, getDepositId, setWithdrawId, getRepayId, setBorrowId); } /** * @dev Withdraw remaining ETH balance from user's redeemed Trove to their DSA + * @param setId Optional storage slot to store the ETH claimed * @notice Claim remaining collateral from Trove */ - function claimCollateralFromRedemption() external returns(string memory _eventName, bytes memory _eventParam) { + function claimCollateralFromRedemption(uint setId) external returns(string memory _eventName, bytes memory _eventParam) { + uint amount = collateralSurplus.getCollateral(address(this)); borrowerOperations.claimCollateral(); - _eventName = "LogClaimCollateralFromRedemption(address)"; - _eventParam = abi.encode(msg.sender); + setUint(setId, amount); + + _eventName = "LogClaimCollateralFromRedemption(address,uint256,uint256)"; + _eventParam = abi.encode(msg.sender, amount, setId); } /* End: Trove */ @@ -256,7 +287,7 @@ abstract contract LiquityResolver is Events, Helpers { stabilityPool.provideToSP(amount, frontendTag); - _eventName = "LogStabilityDeposit(address,uint,address,uint)"; + _eventName = "LogStabilityDeposit(address,uint256,address,uint256)"; _eventParam = abi.encode(msg.sender, amount, frontendTag, getId); } @@ -273,7 +304,7 @@ abstract contract LiquityResolver is Events, Helpers { stabilityPool.withdrawFromSP(amount); setUint(setId, amount); - _eventName = "LogStabilityWithdraw(address,uint,uint)"; + _eventName = "LogStabilityWithdraw(address,uint256,uint256)"; _eventParam = abi.encode(msg.sender, amount, setId); } @@ -287,10 +318,10 @@ abstract contract LiquityResolver is Events, Helpers { address upperHint, address lowerHint ) external returns (string memory _eventName, bytes memory _eventParam) { + uint amount = stabilityPool.getDepositorETHGain(address(this)); stabilityPool.withdrawETHGainToTrove(upperHint, lowerHint); - - _eventName = "LogStabilityMoveEthGainToTrove(address)"; - _eventParam = abi.encode(msg.sender); + _eventName = "LogStabilityMoveEthGainToTrove(address,uint256)"; + _eventParam = abi.encode(msg.sender, amount); } /* End: Stability Pool */ @@ -308,7 +339,7 @@ abstract contract LiquityResolver is Events, Helpers { ) external returns (string memory _eventName, bytes memory _eventParam) { amount = getUint(getId, amount); staking.stake(amount); - _eventName = "LogStake(address,uint,uint)"; + _eventName = "LogStake(address,uint256,uint256)"; _eventParam = abi.encode(msg.sender, amount, getId); } @@ -324,19 +355,27 @@ abstract contract LiquityResolver is Events, Helpers { ) external returns (string memory _eventName, bytes memory _eventParam) { staking.unstake(amount); setUint(setId, amount); - _eventName = "LogUnstake(address,uint,uint)"; + _eventName = "LogUnstake(address,uint256,uint256)"; _eventParam = abi.encode(msg.sender, amount, setId); } /** * @dev Sends ETH and LUSD gains from Staking to user * @notice Claim ETH and LUSD gains from Staking + * @param setEthGainId Optional storage slot to store the claimed ETH + * @param setLusdGainId Optional storage slot to store the claimed LUSD */ - function claimGains() external returns (string memory _eventName, bytes memory _eventParam) { - // claims are gained when a user's stake is adjusted, so we unstake 0 to trigger the claim - staking.unstake(0); - _eventName = "LogClaimGains(address)"; - _eventParam = abi.encode(msg.sender); + function claimStakingGains(uint setEthGainId, uint setLusdGainId) external returns (string memory _eventName, bytes memory _eventParam) { + uint ethAmount = staking.getPendingETHGain(address(this)); + uint lusdAmount = staking.getPendingLUSDGain(address(this)); + + // Gains are claimed when a user's stake is adjusted, so we unstake 0 to trigger the claim + staking.unstake(0); + setUint(setEthGainId, ethAmount); + setUint(setLusdGainId, lusdAmount); + + _eventName = "LogClaimStakingGains(address,uint256,uint256)"; + _eventParam = abi.encode(msg.sender, ethAmount, lusdAmount); } /* End: Staking */ diff --git a/test/liquity/liquity.abi.js b/test/liquity/liquity.contracts.js similarity index 94% rename from test/liquity/liquity.abi.js rename to test/liquity/liquity.contracts.js index d1171d1d..d73407c5 100644 --- a/test/liquity/liquity.abi.js +++ b/test/liquity/liquity.contracts.js @@ -60,6 +60,11 @@ const LQTY_TOKEN_ABI = [ "function transfer(address _to, uint256 _value) public returns (bool success)", ]; +const COLL_SURPLUS_ADDRESS = "0x3D32e8b97Ed5881324241Cf03b2DA5E2EBcE5521"; +const COLL_SURPLUS_ABI = [ + "function getCollateral(address _account) external view returns (uint)", +]; + module.exports = { TROVE_MANAGER_ADDRESS, TROVE_MANAGER_ABI, @@ -81,4 +86,6 @@ module.exports = { STAKING_ABI, LQTY_TOKEN_ADDRESS, LQTY_TOKEN_ABI, + COLL_SURPLUS_ADDRESS, + COLL_SURPLUS_ABI, }; diff --git a/test/liquity/liquity.helpers.js b/test/liquity/liquity.helpers.js index c400ded5..07a94ee3 100644 --- a/test/liquity/liquity.helpers.js +++ b/test/liquity/liquity.helpers.js @@ -5,6 +5,7 @@ const hardhatConfig = require("../../hardhat.config"); const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js"); const encodeSpells = require("../../scripts/encodeSpells.js"); const getMasterSigner = require("../../scripts/getMasterSigner"); +const buildDSAv2 = require("../../scripts/buildDSAv2"); // Instadapp instadappAddresses/ABIs const instadappAddresses = require("../../scripts/constant/addresses"); @@ -13,12 +14,13 @@ const instadappAbi = require("../../scripts/constant/abis"); // Instadapp Liquity Connector artifacts const connectV2LiquityArtifacts = require("../../artifacts/contracts/mainnet/connectors/liquity/main.sol/ConnectV2Liquity.json"); const connectV2BasicV1Artifacts = require("../../artifacts/contracts/mainnet/connectors/basic/main.sol/ConnectV2Basic.json"); +const { ethers } = require("hardhat"); const CONNECTOR_NAME = "LIQUITY-v1-TEST"; const LUSD_GAS_COMPENSATION = hre.ethers.utils.parseUnits("200", 18); // 200 LUSD gas compensation repaid after loan repayment -const BLOCK_NUMBER = 12478159; // Deterministic block number for tests to run against, if you change this, tests will break. +const LIQUIDATABLE_TROVES_BLOCK_NUMBER = 12478159; // Deterministic block number for tests to run against, if you change this, tests will break. const JUSTIN_SUN_ADDRESS = "0x903d12bf2c57a29f32365917c706ce0e1a84cce3"; // LQTY whale address -const LIQUIDATABLE_TROVE_ADDRESS = "0xafbeb4cb97f3b08ec2fe07ef0dac15d37013a347"; // Trove which is liquidatable at blockNumber: BLOCK_NUMBER +const LIQUIDATABLE_TROVE_ADDRESS = "0xafbeb4cb97f3b08ec2fe07ef0dac15d37013a347"; // Trove which is liquidatable at blockNumber: LIQUIDATABLE_TROVES_BLOCK_NUMBER const MAX_GAS = hardhatConfig.networks.hardhat.blockGasLimit; // Maximum gas limit (12000000) const openTroveSpell = async ( @@ -48,12 +50,11 @@ const openTroveSpell = async ( 0, ], }; - const openTx = await dsa + return await dsa .connect(signer) .cast(...encodeSpells([openTroveSpell]), address, { value: depositAmount, }); - return await openTx.wait(); }; const createDsaTrove = async ( @@ -92,6 +93,13 @@ const sendToken = async (token, amount, from, to) => { return await token.connect(signer).transfer(to, amount); }; +const resetInitialState = async (walletAddress, contracts, isDebug = false) => { + const liquity = await deployAndConnect(contracts, isDebug); + const dsa = await buildDSAv2(walletAddress); + + return [liquity, dsa]; +}; + const resetHardhatBlockNumber = async (blockNumber) => { return await hre.network.provider.request({ method: "hardhat_reset", @@ -108,7 +116,7 @@ const resetHardhatBlockNumber = async (blockNumber) => { const deployAndConnect = async (contracts, isDebug = false) => { // Pin Liquity tests to a particular block number to create deterministic state (Ether price etc.) - await resetHardhatBlockNumber(BLOCK_NUMBER); + await resetHardhatBlockNumber(LIQUIDATABLE_TROVES_BLOCK_NUMBER); const liquity = { troveManager: null, @@ -121,6 +129,7 @@ const deployAndConnect = async (contracts, isDebug = false) => { hintHelpers: null, sortedTroves: null, staking: null, + collSurplus: null, }; const masterSigner = await getMasterSigner(); @@ -230,6 +239,14 @@ const deployAndConnect = async (contracts, isDebug = false) => { ); isDebug && console.log("Staking contract address", liquity.staking.address); + liquity.collSurplus = new ethers.Contract( + contracts.COLL_SURPLUS_ADDRESS, + contracts.COLL_SURPLUS_ABI, + ethers.provider + ); + isDebug && + console.log("CollSurplus contract address", liquity.collSurplus.address); + return liquity; }; @@ -310,18 +327,48 @@ const getRedemptionHints = async ( }; }; +const redeem = async (amount, from, to, liquity) => { + await sendToken(liquity.lusdToken, amount, from, to); + const { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + } = await getRedemptionHints( + amount, + liquity.hintHelpers, + liquity.sortedTroves, + liquity.priceFeed + ); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + return await liquity.troveManager + .connect(wallet) + .redeemCollateral( + amount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: MAX_GAS, // permit max gas + } + ); +}; + module.exports = { deployAndConnect, + resetInitialState, createDsaTrove, - openTroveSpell, sendToken, + getTroveInsertionHints, + getRedemptionHints, + redeem, CONNECTOR_NAME, LUSD_GAS_COMPENSATION, - BLOCK_NUMBER, JUSTIN_SUN_ADDRESS, LIQUIDATABLE_TROVE_ADDRESS, MAX_GAS, - resetHardhatBlockNumber, - getTroveInsertionHints, - getRedemptionHints, }; diff --git a/test/liquity/liquity.test.js b/test/liquity/liquity.test.js index a7993876..274492d7 100644 --- a/test/liquity/liquity.test.js +++ b/test/liquity/liquity.test.js @@ -6,7 +6,7 @@ const buildDSAv2 = require("../../scripts/buildDSAv2"); const encodeSpells = require("../../scripts/encodeSpells.js"); // Liquity smart contracts -const contracts = require("./liquity.abi"); +const contracts = require("./liquity.contracts"); // Liquity helpers const helpers = require("./liquity.helpers"); @@ -45,1089 +45,1847 @@ describe.only("Liquity", () => { describe("Main (Connector)", () => { describe("Trove", () => { - 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( - wallet.address - ); - const originalDsaBalance = await ethers.provider.getBalance( - dsa.address - ); + 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( + wallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsa.address + ); - const openTroveSpell = { - connector: helpers.CONNECTOR_NAME, - method: "open", - args: [ + const openTroveSpell = { + connector: helpers.CONNECTOR_NAME, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + 0, + 0, + ], + }; + + const tx = await dsa + .connect(wallet) + .cast(...encodeSpells([openTroveSpell]), wallet.address, { + value: depositAmount, + }); + + await tx.wait(); + + const userBalance = await ethers.provider.getBalance(wallet.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).lt( + originalUserBalance, + "User should have less Ether after opening Trove" + ); + + 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 tried to borrow" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( + borrowAmount, + "Trove debt should equal the borrowed amount plus fee" + ); + + expect(troveCollateral).to.eq( depositAmount, - maxFeePercentage, + "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( + wallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsa.address + ); + const depositId = 1; // Choose an ID to store and retrieve the deopsited ETH + + const depositEthSpell = { + connector: "Basic-v1", + method: "deposit", + args: [ETH_ADDRESS, depositAmount, 0, depositId], + }; + + const openTroveSpell = { + connector: helpers.CONNECTOR_NAME, + 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, + ], + }; + + const spells = [depositEthSpell, openTroveSpell]; + const tx = await dsa + .connect(wallet) + .cast(...encodeSpells(spells), wallet.address, { + value: depositAmount, + }); + + await tx.wait(); + const userBalance = await ethers.provider.getBalance(wallet.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).lt( + originalUserBalance, + "User should have less Ether" + ); + + expect(dsaEthBalance).to.eq( + originalDsaBalance, + "DSA balance should not change" + ); + + expect( + dsaLusdBalance, + "DSA account should now hold the amount the user tried to borrow" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( borrowAmount, - upperHint, - lowerHint, - 0, - 0, - ], - }; + "Trove debt should equal the borrowed amount plus fee" + ); - const spells = [openTroveSpell]; - const tx = await dsa - .connect(wallet) - .cast(...encodeSpells(spells), wallet.address, { - value: depositAmount, - }); - - await tx.wait(); - - const userBalance = await ethers.provider.getBalance(wallet.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).lt( - originalUserBalance, - "User should have less Ether after opening Trove" - ); - - 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 tried to borrow" - ).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( - wallet.address - ); - const originalDsaBalance = await ethers.provider.getBalance( - dsa.address - ); - const depositId = 1; // Choose an ID to store and retrieve the deopsited ETH - - const depositEthSpell = { - connector: "Basic-v1", - method: "deposit", - args: [ETH_ADDRESS, depositAmount, 0, depositId], - }; - - const openTroveSpell = { - connector: helpers.CONNECTOR_NAME, - 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, - ], - }; - - const spells = [depositEthSpell, openTroveSpell]; - const tx = await dsa - .connect(wallet) - .cast(...encodeSpells(spells), wallet.address, { - value: depositAmount, - }); - - await tx.wait(); - const userBalance = await ethers.provider.getBalance(wallet.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).lt( - originalUserBalance, - "User should have less Ether" - ); - - expect(dsaEthBalance).to.eq( - originalDsaBalance, - "DSA balance should not change" - ); - - expect( - dsaLusdBalance, - "DSA account should now hold the amount the user tried to borrow" - ).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( - wallet.address - ); - const originalDsaBalance = await ethers.provider.getBalance( - dsa.address - ); - const borrowId = 1; - - const openTroveSpell = { - connector: helpers.CONNECTOR_NAME, - method: "open", - args: [ + expect(troveCollateral).to.eq( depositAmount, - maxFeePercentage, + "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( + wallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsa.address + ); + const borrowId = 1; + + const openTroveSpell = { + connector: helpers.CONNECTOR_NAME, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + 0, + borrowId, + ], + }; + + const withdrawLusdSpell = { + connector: "Basic-v1", + method: "withdraw", + args: [ + contracts.LUSD_TOKEN_ADDRESS, + 0, // amount comes from the previous spell's setId + dsa.address, + borrowId, + 0, + ], + }; + + const spells = [openTroveSpell, withdrawLusdSpell]; + const tx = await dsa + .connect(wallet) + .cast(...encodeSpells(spells), wallet.address, { + value: depositAmount, + }); + + await tx.wait(); + + const userBalance = await ethers.provider.getBalance(wallet.address); + + expect(userBalance).lt( + originalUserBalance, + "User should have less Ether after opening Trove" + ); + + 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(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 tried to borrow" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( borrowAmount, - upperHint, - lowerHint, - 0, - borrowId, - ], - }; + "Trove debt should equal the borrowed amount plus fee" + ); - const withdrawLusdSpell = { - connector: "Basic-v1", - method: "withdraw", - args: [ - contracts.LUSD_TOKEN_ADDRESS, - 0, // amount comes from the previous spell's setId - dsa.address, - borrowId, - 0, - ], - }; + expect(troveCollateral).to.eq( + depositAmount, + "Trove collateral should equal the deposited amount" + ); + }); - const spells = [openTroveSpell, withdrawLusdSpell]; - const tx = await dsa - .connect(wallet) - .cast(...encodeSpells(spells), wallet.address, { - value: depositAmount, - }); + 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; - await tx.wait(); + const openTroveSpell = { + connector: helpers.CONNECTOR_NAME, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + 0, + 0, + ], + }; - const userBalance = await ethers.provider.getBalance(wallet.address); - - expect(userBalance).lt( - originalUserBalance, - "User should have less Ether after opening Trove" - ); - - 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(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 tried to borrow" - ).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" - ); + const openTx = await dsa.cast( + ...encodeSpells([openTroveSpell]), + wallet.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"], + [ + wallet.address, + maxFeePercentage, + depositAmount, + borrowAmount, + 0, + 0, + ] + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); - it("closes a Trove", async () => { - const depositAmount = ethers.utils.parseEther("5"); - const borrowAmount = ethers.utils.parseUnits("2000", 18); - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves, - depositAmount, - borrowAmount - ); + describe("close()", () => { + it("closes a Trove", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves, + depositAmount, + borrowAmount + ); - const originalTroveDebt = await liquity.troveManager.getTroveDebt( - dsa.address - ); + const originalTroveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); - const originalTroveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); + const originalTroveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); - // Send DSA account enough LUSD (from Stability Pool) to close their Trove - const extraLusdRequiredToCloseTrove = originalTroveDebt.sub( - borrowAmount - ); + // Send DSA account enough LUSD (from Stability Pool) to close their Trove + const extraLusdRequiredToCloseTrove = originalTroveDebt.sub( + borrowAmount + ); - await helpers.sendToken( - liquity.lusdToken, - extraLusdRequiredToCloseTrove, - contracts.STABILITY_POOL_ADDRESS, - dsa.address - ); + await helpers.sendToken( + liquity.lusdToken, + extraLusdRequiredToCloseTrove, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); - const originalDsaLusdBalance = await liquity.lusdToken.balanceOf( - 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(originalTroveDebt); + expect( + originalDsaLusdBalance, + "DSA account should now hold the LUSD amount required to pay off the Trove debt" + ).to.eq(originalTroveDebt); - const closeTroveSpell = { - connector: helpers.CONNECTOR_NAME, - method: "close", - args: [0], - }; + const closeTroveSpell = { + connector: helpers.CONNECTOR_NAME, + method: "close", + args: [0], + }; - const closeTx = await dsa - .connect(wallet) - .cast(...encodeSpells([closeTroveSpell]), wallet.address); + const closeTx = await dsa + .connect(wallet) + .cast(...encodeSpells([closeTroveSpell]), wallet.address); - await closeTx.wait(); - 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 - ); + await closeTx.wait(); + 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(troveDebt, "Trove debt should equal 0 after close").to.eq(0); - expect( - troveCollateral, - "Trove collateral 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(originalTroveCollateral); + expect( + dsaEthBalance, + "DSA account should now hold the Trove's ETH collateral" + ).to.eq(originalTroveCollateral); - 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); - }); + 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 () => { - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves - ); + it("closes a Trove using LUSD obtained from a previous spell", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); - const originalTroveDebt = await liquity.troveManager.getTroveDebt( - dsa.address - ); - const originalTroveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); + const originalTroveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const originalTroveCollateral = 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, - originalTroveDebt, - contracts.STABILITY_POOL_ADDRESS, - wallet.address - ); - - // Allow DSA to spend user's LUSD - await liquity.lusdToken - .connect(wallet) - .approve(dsa.address, originalTroveDebt); - - const lusdDepositId = 1; - - // Simulate a spell which would have pulled LUSD from somewhere (e.g. AAVE) into InstaMemory - // In this case we're simply running a deposit spell from the user's EOA - const depositLusdSpell = { - connector: "Basic-v1", - method: "deposit", - args: [ - contracts.LUSD_TOKEN_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, originalTroveDebt, - 0, - lusdDepositId, - ], - }; - // Withdraw the obtained LUSD into DSA account - const withdrawLusdSpell = { - connector: "Basic-v1", - method: "withdraw", - args: [ - contracts.LUSD_TOKEN_ADDRESS, - 0, // amount comes from the previous spell's setId - dsa.address, - lusdDepositId, - 0, - ], - }; - - const closeTroveSpell = { - connector: helpers.CONNECTOR_NAME, - method: "close", - args: [0], - }; - - const closeTx = await dsa - .connect(wallet) - .cast( - ...encodeSpells([ - depositLusdSpell, - withdrawLusdSpell, - closeTroveSpell, - ]), + contracts.STABILITY_POOL_ADDRESS, wallet.address ); - await closeTx.wait(); - const dsaEthBalance = await ethers.provider.getBalance(dsa.address); - const troveDebt = await liquity.troveManager.getTroveDebt(dsa.address); - const troveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(wallet) + .approve(dsa.address, originalTroveDebt); - expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); + const lusdDepositId = 1; - expect( - troveCollateral, - "Trove collateral should equal 0 after close" - ).to.eq(0); + // Simulate a spell which would have pulled LUSD from somewhere (e.g. AAVE) into InstaMemory + // In this case we're simply running a deposit spell from the user's EOA + const depositLusdSpell = { + connector: "Basic-v1", + method: "deposit", + args: [ + contracts.LUSD_TOKEN_ADDRESS, + originalTroveDebt, + 0, + lusdDepositId, + ], + }; + // Withdraw the obtained LUSD into DSA account + const withdrawLusdSpell = { + connector: "Basic-v1", + method: "withdraw", + args: [ + contracts.LUSD_TOKEN_ADDRESS, + 0, // amount comes from the previous spell's setId + dsa.address, + lusdDepositId, + 0, + ], + }; - expect( - dsaEthBalance, - "DSA account should now hold the Trove's ETH collateral" - ).to.eq(originalTroveCollateral); - }); + const closeTroveSpell = { + connector: helpers.CONNECTOR_NAME, + method: "close", + args: [0], + }; - 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); - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves, - depositAmount, - borrowAmount - ); + const closeTx = await dsa + .connect(wallet) + .cast( + ...encodeSpells([ + depositLusdSpell, + withdrawLusdSpell, + closeTroveSpell, + ]), + wallet.address + ); - const originalTroveDebt = await liquity.troveManager.getTroveDebt( - dsa.address - ); - const originalTroveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); - - // Send DSA account enough LUSD (from Stability Pool) to close their Trove - const extraLusdRequiredToCloseTrove = originalTroveDebt.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(originalTroveDebt); - - const collateralWithdrawId = 1; - - const closeTroveSpell = { - connector: helpers.CONNECTOR_NAME, - method: "close", - args: [collateralWithdrawId], - }; - - const withdrawEthSpell = { - connector: "Basic-v1", - method: "withdraw", - args: [ - ETH_ADDRESS, - 0, // amount comes from the previous spell's setId - dsa.address, - collateralWithdrawId, - 0, - ], - }; - - const closeTx = await dsa - .connect(wallet) - .cast( - ...encodeSpells([closeTroveSpell, withdrawEthSpell]), - wallet.address + await closeTx.wait(); + const dsaEthBalance = await ethers.provider.getBalance(dsa.address); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address ); - await closeTx.wait(); - 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(troveDebt, "Trove debt should equal 0 after close").to.eq(0); + expect( + troveCollateral, + "Trove collateral 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(originalTroveCollateral); + }); - expect( - dsaEthBalance, - "DSA account should now hold the Trove's ETH collateral" - ).to.eq(originalTroveCollateral); + 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); + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves, + depositAmount, + borrowAmount + ); - 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); + const originalTroveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const originalTroveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + + // Send DSA account enough LUSD (from Stability Pool) to close their Trove + const extraLusdRequiredToCloseTrove = originalTroveDebt.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(originalTroveDebt); + + const collateralWithdrawId = 1; + + const closeTroveSpell = { + connector: helpers.CONNECTOR_NAME, + method: "close", + args: [collateralWithdrawId], + }; + + const withdrawEthSpell = { + connector: "Basic-v1", + method: "withdraw", + args: [ + ETH_ADDRESS, + 0, // amount comes from the previous spell's setId + dsa.address, + collateralWithdrawId, + 0, + ], + }; + + const closeTx = await dsa + .connect(wallet) + .cast( + ...encodeSpells([closeTroveSpell, withdrawEthSpell]), + wallet.address + ); + + await closeTx.wait(); + 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(originalTroveCollateral); + + 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); + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves, + depositAmount, + borrowAmount + ); + await helpers.sendToken( + liquity.lusdToken, + ethers.utils.parseUnits("2500", 18), + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const closeTroveSpell = { + connector: helpers.CONNECTOR_NAME, + method: "close", + args: [0], + }; + + const closeTx = await dsa + .connect(wallet) + .cast(...encodeSpells([closeTroveSpell]), wallet.address); + + const receipt = await closeTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256"], + [wallet.address, 0] + ); + expect(castLogEvent.eventNames[0]).eq("LogClose(address,uint256)"); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); - it("deposits ETH into a Trove", async () => { - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves - ); + describe("deposit()", () => { + it("deposits ETH into a Trove", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); - const originalTroveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); + const originalTroveCollateral = 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.CONNECTOR_NAME, - method: "deposit", - args: [topupAmount, upperHint, lowerHint, 0], - }; + const topupAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const depositEthSpell = { + connector: helpers.CONNECTOR_NAME, + method: "deposit", + args: [topupAmount, upperHint, lowerHint, 0], + }; - const depositTx = await dsa - .connect(wallet) - .cast(...encodeSpells([depositEthSpell]), wallet.address, { - value: topupAmount, - }); + const depositTx = await dsa + .connect(wallet) + .cast(...encodeSpells([depositEthSpell]), wallet.address, { + value: topupAmount, + }); - await depositTx.wait(); + await depositTx.wait(); - const troveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); - const expectedTroveCollateral = originalTroveCollateral.add( - topupAmount - ); + const expectedTroveCollateral = originalTroveCollateral.add( + topupAmount + ); - expect( - troveCollateral, - `Trove collateral should have increased by ${topupAmount} ETH` - ).to.eq(expectedTroveCollateral); + expect( + troveCollateral, + `Trove collateral should have increased by ${topupAmount} ETH` + ).to.eq(expectedTroveCollateral); + }); + + it("returns Instadapp event name and data", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); + + const topupAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const depositEthSpell = { + connector: helpers.CONNECTOR_NAME, + method: "deposit", + args: [topupAmount, upperHint, lowerHint, 0], + }; + + const depositTx = await dsa + .connect(wallet) + .cast(...encodeSpells([depositEthSpell]), wallet.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"], + [wallet.address, topupAmount, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogDeposit(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); - it("withdraws ETH from a Trove", async () => { - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves - ); + describe("withdraw()", () => { + it("withdraws ETH from a Trove", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); - const originalTroveCollateral = 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.CONNECTOR_NAME, - method: "withdraw", - args: [withdrawAmount, upperHint, lowerHint, 0], - }; + const originalTroveCollateral = 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.CONNECTOR_NAME, + method: "withdraw", + args: [withdrawAmount, upperHint, lowerHint, 0], + }; - const withdrawTx = await dsa - .connect(wallet) - .cast(...encodeSpells([withdrawEthSpell]), wallet.address); + const withdrawTx = await dsa + .connect(wallet) + .cast(...encodeSpells([withdrawEthSpell]), wallet.address); - await withdrawTx.wait(); - const troveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); - const expectedTroveCollateral = originalTroveCollateral.sub( - withdrawAmount - ); + await withdrawTx.wait(); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const expectedTroveCollateral = originalTroveCollateral.sub( + withdrawAmount + ); - expect( - troveCollateral, - `Trove collateral should have decreased by ${withdrawAmount} ETH` - ).to.eq(expectedTroveCollateral); + expect( + troveCollateral, + `Trove collateral should have decreased by ${withdrawAmount} ETH` + ).to.eq(expectedTroveCollateral); + }); + + it("returns Instadapp event name and data", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); + + const withdrawAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const setId = 0; + const withdrawEthSpell = { + connector: helpers.CONNECTOR_NAME, + method: "withdraw", + args: [withdrawAmount, upperHint, lowerHint, setId], + }; + + const withdrawTx = await dsa + .connect(wallet) + .cast(...encodeSpells([withdrawEthSpell]), wallet.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"], + [wallet.address, withdrawAmount, setId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogWithdraw(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); - it("borrows LUSD from a Trove", async () => { - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves - ); + describe("borrow()", () => { + it("borrows LUSD from a Trove", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); - const originalTroveDebt = await liquity.troveManager.getTroveDebt( - dsa.address - ); - const borrowAmount = ethers.utils.parseUnits("1000"); // 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.CONNECTOR_NAME, - method: "borrow", - args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, 0], - }; + const originalTroveDebt = 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.CONNECTOR_NAME, + method: "borrow", + args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, 0], + }; - const borrowTx = await dsa - .connect(wallet) - .cast(...encodeSpells([borrowSpell]), wallet.address); + const borrowTx = await dsa + .connect(wallet) + .cast(...encodeSpells([borrowSpell]), wallet.address); - await borrowTx.wait(); - const troveDebt = await liquity.troveManager.getTroveDebt(dsa.address); - const expectedTroveDebt = originalTroveDebt.add(borrowAmount); + await borrowTx.wait(); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveDebt = originalTroveDebt.add(borrowAmount); - expect( - troveDebt, - `Trove debt should have increased by at least ${borrowAmount} ETH` - ).to.gte(expectedTroveDebt); + expect( + troveDebt, + `Trove debt should have increased by at least ${borrowAmount} ETH` + ).to.gte(expectedTroveDebt); + }); + + it("returns Instadapp event name and data", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); + + 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 setId = 0; + const borrowSpell = { + connector: helpers.CONNECTOR_NAME, + method: "borrow", + args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, setId], + }; + + const borrowTx = await dsa + .connect(wallet) + .cast(...encodeSpells([borrowSpell]), wallet.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"], + [wallet.address, borrowAmount, setId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogBorrow(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); - it("repays LUSD to a Trove", async () => { - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves - ); + describe("repay()", () => { + it("repays LUSD to a Trove", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); - const originalTroveDebt = await liquity.troveManager.getTroveDebt( - dsa.address - ); - const repayAmount = ethers.utils.parseUnits("100"); // 100 LUSD - const upperHint = ethers.constants.AddressZero; - const lowerHint = ethers.constants.AddressZero; - const borrowSpell = { - connector: helpers.CONNECTOR_NAME, - method: "repay", - args: [repayAmount, upperHint, lowerHint, 0], - }; + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves, + depositAmount, + borrowAmount + ); - const repayTx = await dsa - .connect(wallet) - .cast(...encodeSpells([borrowSpell]), wallet.address, { - value: repayAmount, - }); - - await repayTx.wait(); - const troveDebt = await liquity.troveManager.getTroveDebt(dsa.address); - const expectedTroveDebt = originalTroveDebt.sub(repayAmount); - - expect( - troveDebt, - `Trove debt should have decreased by ${repayAmount} ETH` - ).to.eq(expectedTroveDebt); - }); - - it("adjusts a Trove: deposit ETH and borrow LUSD", async () => { - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves - ); - - const originalTroveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); - const originalTroveDebt = await liquity.troveManager.getTroveDebt( - dsa.address - ); - const depositAmount = ethers.utils.parseEther("1"); // 1 ETH - const borrowAmount = ethers.utils.parseUnits("500"); // 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.CONNECTOR_NAME, - method: "adjust", - args: [ - maxFeePercentage, - withdrawAmount, + const originalTroveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( depositAmount, borrowAmount, - repayAmount, - upperHint, - lowerHint, - 0, - 0, - 0, - 0, - ], - }; + liquity.hintHelpers, + liquity.sortedTroves + ); + const borrowSpell = { + connector: helpers.CONNECTOR_NAME, + method: "repay", + args: [repayAmount, upperHint, lowerHint, 0], + }; - const adjustTx = await dsa - .connect(wallet) - .cast(...encodeSpells([adjustSpell]), wallet.address, { - value: depositAmount, - gasLimit: helpers.MAX_GAS, - }); + await dsa + .connect(wallet) + .cast(...encodeSpells([borrowSpell]), wallet.address, { + value: repayAmount, + }); - await adjustTx.wait(); - const troveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); - const troveDebt = await liquity.troveManager.getTroveDebt(dsa.address); - const expectedTroveColl = originalTroveCollateral.add(depositAmount); - const expectedTroveDebt = originalTroveDebt.add(borrowAmount); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveDebt = originalTroveDebt.sub(repayAmount); - expect( - troveCollateral, - `Trove collateral should have increased by ${depositAmount} ETH` - ).to.eq(expectedTroveColl); + expect( + troveDebt, + `Trove debt should have decreased by ${repayAmount} ETH` + ).to.eq(expectedTroveDebt); + }); - expect( - troveDebt, - `Trove debt should have increased by at least ${borrowAmount} ETH` - ).to.gte(expectedTroveDebt); - }); + it("returns Instadapp event name and data", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves, + depositAmount, + borrowAmount + ); - it("adjusts a Trove: withdraw ETH and repay LUSD", async () => { - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves - ); - - const originalTroveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); - const originalTroveDebt = 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("500"); // 500 LUSD; - const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( - originalTroveCollateral.sub(withdrawAmount), - originalTroveDebt.sub(repayAmount), - liquity.hintHelpers, - liquity.sortedTroves - ); - const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - - const adjustSpell = { - connector: helpers.CONNECTOR_NAME, - method: "adjust", - args: [ - maxFeePercentage, - withdrawAmount, + const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( depositAmount, borrowAmount, - repayAmount, - upperHint, - lowerHint, - 0, - 0, - 0, - 0, - ], - }; + liquity.hintHelpers, + liquity.sortedTroves + ); + const getId = 0; - const adjustTx = await dsa - .connect(wallet) - .cast(...encodeSpells([adjustSpell]), wallet.address, { - value: depositAmount, - gasLimit: helpers.MAX_GAS, - }); + const borrowSpell = { + connector: helpers.CONNECTOR_NAME, + method: "repay", + args: [repayAmount, upperHint, lowerHint, getId], + }; - await adjustTx.wait(); - const troveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); - const troveDebt = await liquity.troveManager.getTroveDebt(dsa.address); - const expectedTroveColl = originalTroveCollateral.sub(withdrawAmount); - const expectedTroveDebt = originalTroveDebt.sub(repayAmount); + const repayTx = await dsa + .connect(wallet) + .cast(...encodeSpells([borrowSpell]), wallet.address, { + value: repayAmount, + }); - 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); + const receipt = await repayTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256"], + [wallet.address, repayAmount, getId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogRepay(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); - 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); + describe("adjust()", () => { + it("adjusts a Trove: deposit ETH and borrow LUSD", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves, - depositAmount, - borrowAmount - ); + const originalTroveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const originalTroveDebt = 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 - // 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, - wallet.address - ); - const { - partialRedemptionHintNicr, - firstRedemptionHint, - upperHint, - lowerHint, - } = await helpers.getRedemptionHints( - redeemAmount, - liquity.hintHelpers, - liquity.sortedTroves, - liquity.priceFeed - ); - const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const adjustSpell = { + connector: helpers.CONNECTOR_NAME, + method: "adjust", + args: [ + maxFeePercentage, + withdrawAmount, + depositAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + 0, + 0, + 0, + 0, + ], + }; - await liquity.troveManager - .connect(wallet) - .redeemCollateral( + const adjustTx = await dsa + .connect(wallet) + .cast(...encodeSpells([adjustSpell]), wallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + await adjustTx.wait(); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveColl = originalTroveCollateral.add(depositAmount); + const expectedTroveDebt = originalTroveDebt.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 () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); + + const originalTroveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const originalTroveDebt = 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("500", 18); // 500 LUSD; + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + originalTroveCollateral.sub(withdrawAmount), + originalTroveDebt.sub(repayAmount), + liquity.hintHelpers, + liquity.sortedTroves + ); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const adjustSpell = { + connector: helpers.CONNECTOR_NAME, + method: "adjust", + args: [ + maxFeePercentage, + withdrawAmount, + depositAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + 0, + 0, + 0, + 0, + ], + }; + + const adjustTx = await dsa + .connect(wallet) + .cast(...encodeSpells([adjustSpell]), wallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + await adjustTx.wait(); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsa.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsa.address + ); + const expectedTroveColl = originalTroveCollateral.sub(withdrawAmount); + const expectedTroveDebt = originalTroveDebt.sub(repayAmount); + + 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("returns Instadapp event name and data", async () => { + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); + + 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 getDepositId = 0; + const setWithdrawId = 0; + const getRepayId = 0; + const setBorrowId = 0; + + const adjustSpell = { + connector: helpers.CONNECTOR_NAME, + method: "adjust", + args: [ + maxFeePercentage, + withdrawAmount, + depositAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + getDepositId, + setWithdrawId, + getRepayId, + setBorrowId, + ], + }; + + const adjustTx = await dsa + .connect(wallet) + .cast(...encodeSpells([adjustSpell]), wallet.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", + "uint256", + "uint256", + ], + [ + wallet.address, + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + getDepositId, + setWithdrawId, + getRepayId, + setBorrowId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogAdjust(address,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("claim()", () => { + 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, + wallet, + liquity.hintHelpers, + liquity.sortedTroves, + 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, + wallet.address + ); + const { + partialRedemptionHintNicr, firstRedemptionHint, upperHint, lowerHint, - partialRedemptionHintNicr, - 0, - maxFeePercentage, - { - gasLimit: helpers.MAX_GAS, // permit max gas - } + } = await helpers.getRedemptionHints( + redeemAmount, + liquity.hintHelpers, + liquity.sortedTroves, + liquity.priceFeed + ); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + await liquity.troveManager + .connect(wallet) + .redeemCollateral( + redeemAmount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: helpers.MAX_GAS, // permit max gas + } + ); + + const ethBalanceBefore = await ethers.provider.getBalance( + dsa.address ); - const ethBalanceBefore = await ethers.provider.getBalance(dsa.address); + // Claim the remaining collateral from the redeemed Trove + const claimCollateralFromRedemptionSpell = { + connector: helpers.CONNECTOR_NAME, + method: "claimCollateralFromRedemption", + args: [0], + }; - // Claim the remaining collateral from the redeemed Trove - const claimCollateralFromRedemptionSpell = { - connector: helpers.CONNECTOR_NAME, - method: "claimCollateralFromRedemption", - args: [], - }; + const claimTx = await dsa + .connect(wallet) + .cast( + ...encodeSpells([claimCollateralFromRedemptionSpell]), + wallet.address + ); - const claimTx = await dsa - .connect(wallet) - .cast( - ...encodeSpells([claimCollateralFromRedemptionSpell]), + await claimTx.wait(); + + const ethBalanceAfter = await ethers.provider.getBalance(dsa.address); + + const expectedRemainingCollateral = "527014573774047160"; // ~0.52 ETH based on this mainnet fork's blockNumber + expect(ethBalanceAfter).to.be.gt(ethBalanceBefore); + expect(ethBalanceAfter).to.eq(expectedRemainingCollateral); + }); + + 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, + wallet, + liquity.hintHelpers, + liquity.sortedTroves, + 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, wallet.address ); + const { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + } = await helpers.getRedemptionHints( + redeemAmount, + liquity.hintHelpers, + liquity.sortedTroves, + liquity.priceFeed + ); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee - await claimTx.wait(); + await liquity.troveManager + .connect(wallet) + .redeemCollateral( + redeemAmount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: helpers.MAX_GAS, // permit max gas + } + ); + const claimAmount = await liquity.collSurplus.getCollateral( + dsa.address + ); - const ethBalanceAfter = await ethers.provider.getBalance(dsa.address); + const claimCollateralFromRedemptionSpell = { + connector: helpers.CONNECTOR_NAME, + method: "claimCollateralFromRedemption", + args: [setId], + }; - const expectedRemainingCollateral = "527014573774047160"; // ~0.25 ETH based on this mainnet fork's blockNumber - expect(ethBalanceAfter).to.be.gt(ethBalanceBefore); - expect(ethBalanceAfter).to.eq(expectedRemainingCollateral); + const claimTx = await dsa + .connect(wallet) + .cast( + ...encodeSpells([claimCollateralFromRedemptionSpell]), + wallet.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"], + [wallet.address, claimAmount, setId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogClaimCollateralFromRedemption(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); }); describe("Stability Pool", () => { - it("deposits into Stability Pool", async () => { - const amount = ethers.utils.parseUnits("100", 18); - const frontendTag = ethers.constants.AddressZero; + 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 - ); + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); - const stabilityDepositSpell = { - connector: helpers.CONNECTOR_NAME, - method: "stabilityDeposit", - args: [amount, frontendTag, 0], - }; + const stabilityDepositSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stabilityDeposit", + args: [amount, frontendTag, 0], + }; - const depositTx = await dsa - .connect(wallet) - .cast(...encodeSpells([stabilityDepositSpell]), wallet.address); + const depositTx = await dsa + .connect(wallet) + .cast(...encodeSpells([stabilityDepositSpell]), wallet.address); - await depositTx.wait(); - const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( - dsa.address - ); - expect(depositedAmount).to.eq(amount); - }); - - it("withdraws from Stability Pool", async () => { - // The current block number has liquidatable Troves. - // Remove them otherwise Stability Pool withdrawals are disabled - await liquity.troveManager.connect(wallet).liquidateTroves(90, { - gasLimit: helpers.MAX_GAS, + await depositTx.wait(); + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsa.address + ); + expect(depositedAmount).to.eq(amount); }); - const amount = ethers.utils.parseUnits("100", 18); - const frontendTag = ethers.constants.AddressZero; - await helpers.sendToken( - liquity.lusdToken, - amount, - contracts.STABILITY_POOL_ADDRESS, - dsa.address - ); + it("returns Instadapp event name and data", async () => { + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + const getId = 0; + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); - const stabilityDepositSpell = { - connector: helpers.CONNECTOR_NAME, - method: "stabilityDeposit", - args: [amount, frontendTag, 0], - }; + const stabilityDepositSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stabilityDeposit", + args: [amount, frontendTag, getId], + }; - // Withdraw half of the deposit - const stabilitWithdrawSpell = { - connector: helpers.CONNECTOR_NAME, - method: "stabilityWithdraw", - args: [amount.div(2), 0], - }; - const spells = [stabilityDepositSpell, stabilitWithdrawSpell]; + const depositTx = await dsa + .connect(wallet) + .cast(...encodeSpells([stabilityDepositSpell]), wallet.address); - const spellsTx = await dsa - .connect(wallet) - .cast(...encodeSpells(spells), wallet.address); - - await spellsTx.wait(); - - 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)); + const receipt = await depositTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "address", "uint256"], + [wallet.address, amount, frontendTag, getId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStabilityDeposit(address,uint256,address,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); - it("moves ETH gain from Stability Pool to Trove", async () => { - // Start this test from fresh so that we definitely have a liquidatable Trove within this block - liqiuty = await helpers.deployAndConnect(contracts); + describe("stabilityWithdraw()", () => { + it("withdraws from Stability Pool", async () => { + // Start this test from scratch since we don't want to rely on test order for this to pass. + [liquity, dsa] = await helpers.resetInitialState( + wallet.address, + contracts + ); - // Create a DSA owned Trove to capture ETH liquidation gains - await helpers.createDsaTrove( - dsa, - wallet, - liquity.hintHelpers, - liquity.sortedTroves - ); - 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.CONNECTOR_NAME, - method: "stabilityDeposit", - args: [amount, frontendTag, 0], - }; - - const depositTx = await dsa - .connect(wallet) - .cast(...encodeSpells([stabilityDepositSpell]), wallet.address); - - await depositTx.wait(); - - // Liquidate a Trove to create an ETH gain for the new DSA Trove - await liquity.troveManager - .connect(wallet) - .liquidate(helpers.LIQUIDATABLE_TROVE_ADDRESS, { - gasLimit: helpers.MAX_GAS, // permit max gas + // The current block number has liquidatable Troves. + // Remove them otherwise Stability Pool withdrawals are disabled + await liquity.troveManager.connect(wallet).liquidateTroves(90, { + gasLimit: helpers.MAX_GAS, }); + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; - const ethGainFromLiquidation = await liquity.stabilityPool.getDepositorETHGain( - dsa.address - ); + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); - // Move ETH gain to Trove - const moveEthGainSpell = { - connector: helpers.CONNECTOR_NAME, - method: "stabilityMoveEthGainToTrove", - args: [ethers.constants.AddressZero, ethers.constants.AddressZero], - }; + const stabilityDepositSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stabilityDeposit", + args: [amount, frontendTag, 0], + }; - const moveEthGainTx = await dsa - .connect(wallet) - .cast(...encodeSpells([moveEthGainSpell]), wallet.address); + // Withdraw half of the deposit + const stabilitWithdrawSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stabilityWithdraw", + args: [amount.div(2), 0], + }; + const spells = [stabilityDepositSpell, stabilitWithdrawSpell]; - await moveEthGainTx.wait(); + const castTx = await dsa + .connect(wallet) + .cast(...encodeSpells(spells), wallet.address); - const ethGainAfterMove = await liquity.stabilityPool.getDepositorETHGain( - dsa.address - ); - const troveCollateral = await liquity.troveManager.getTroveColl( - dsa.address - ); - const expectedTroveCollateral = troveCollateralBefore.add( - ethGainFromLiquidation - ); + await castTx.wait(); - expect(ethGainAfterMove).to.eq(0); - expect(troveCollateral).to.eq(expectedTroveCollateral); + 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("returns Instadapp event name and data", async () => { + // Start this test from scratch since we don't want to rely on test order for this to pass. + [liquity, dsa] = await helpers.resetInitialState( + wallet.address, + contracts + ); + + // The current block number has liquidatable Troves. + // Remove them otherwise Stability Pool withdrawals are disabled + await liquity.troveManager.connect(wallet).liquidateTroves(90, { + gasLimit: helpers.MAX_GAS, + }); + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + const setId = 0; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsa.address + ); + + const stabilityDepositSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stabilityDeposit", + args: [amount, frontendTag, setId], + }; + + // Withdraw half of the deposit + const withdrawAmount = amount.div(2); + const stabilitWithdrawSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stabilityWithdraw", + args: [withdrawAmount, 0], + }; + const spells = [stabilityDepositSpell, stabilitWithdrawSpell]; + + const castTx = await dsa + .connect(wallet) + .cast(...encodeSpells(spells), wallet.address); + + const receipt = await castTx.wait(); + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256"], + [wallet.address, withdrawAmount, setId] + ); + expect(castLogEvent.eventNames[1]).eq( + "LogStabilityWithdraw(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[1]).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( + wallet.address, + contracts + ); + }); + + it("moves ETH gain from Stability Pool to Trove", async () => { + // Create a DSA owned Trove to capture ETH liquidation gains + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); + 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.CONNECTOR_NAME, + method: "stabilityDeposit", + args: [amount, frontendTag, 0], + }; + + const depositTx = await dsa + .connect(wallet) + .cast(...encodeSpells([stabilityDepositSpell]), wallet.address); + + await depositTx.wait(); + + // Liquidate a Trove to create an ETH gain for the new DSA Trove + await liquity.troveManager + .connect(wallet) + .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.CONNECTOR_NAME, + method: "stabilityMoveEthGainToTrove", + args: [ethers.constants.AddressZero, ethers.constants.AddressZero], + }; + + const moveEthGainTx = await dsa + .connect(wallet) + .cast(...encodeSpells([moveEthGainSpell]), wallet.address); + + await moveEthGainTx.wait(); + + 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 + await helpers.createDsaTrove( + dsa, + wallet, + liquity.hintHelpers, + liquity.sortedTroves + ); + + // 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.CONNECTOR_NAME, + method: "stabilityDeposit", + args: [amount, frontendTag, 0], + }; + + const depositTx = await dsa + .connect(wallet) + .cast(...encodeSpells([stabilityDepositSpell]), wallet.address); + + await depositTx.wait(); + + // Liquidate a Trove to create an ETH gain for the new DSA Trove + await liquity.troveManager + .connect(wallet) + .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.CONNECTOR_NAME, + method: "stabilityMoveEthGainToTrove", + args: [ethers.constants.AddressZero, ethers.constants.AddressZero], + }; + + const moveEthGainTx = await dsa + .connect(wallet) + .cast(...encodeSpells([moveEthGainSpell]), wallet.address); + + const receipt = await moveEthGainTx.wait(); + + const castLogEvent = receipt.events.find((e) => e.event === "LogCast") + .args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256"], + [wallet.address, ethGainFromLiquidation] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStabilityMoveEthGainToTrove(address,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); }); describe("Staking", () => { - it("stakes LQTY", async () => { - const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( - contracts.STAKING_ADDRESS - ); + 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 amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsa.address + ); - const stakeSpell = { - connector: helpers.CONNECTOR_NAME, - method: "stake", - args: [amount, 0], - }; + const stakeSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stake", + args: [amount, 0], + }; - const stakeTx = await dsa - .connect(wallet) - .cast(...encodeSpells([stakeSpell]), wallet.address); + const stakeTx = await dsa + .connect(wallet) + .cast(...encodeSpells([stakeSpell]), wallet.address); - await stakeTx.wait(); + await stakeTx.wait(); - const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); - expect(lqtyBalance).to.eq(0); + 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) - ); + 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 getId = 0; + + const stakeSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stake", + args: [amount, getId], + }; + + const stakeTx = await dsa + .connect(wallet) + .cast(...encodeSpells([stakeSpell]), wallet.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"], + [wallet.address, amount, getId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStake(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); - it("unstakes LQTY", async () => { - const amount = ethers.utils.parseUnits("1", 18); - await helpers.sendToken( - liquity.lqtyToken, - amount, - helpers.JUSTIN_SUN_ADDRESS, - dsa.address - ); + 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.CONNECTOR_NAME, - method: "stake", - args: [amount, 0], - }; + const stakeSpell = { + connector: helpers.CONNECTOR_NAME, + method: "stake", + args: [amount, 0], + }; - const stakeTx = await dsa - .connect(wallet) - .cast(...encodeSpells([stakeSpell]), wallet.address); + const stakeTx = await dsa + .connect(wallet) + .cast(...encodeSpells([stakeSpell]), wallet.address); - await stakeTx.wait(); + await stakeTx.wait(); - const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( - contracts.STAKING_ADDRESS - ); + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); - const unstakeSpell = { - connector: helpers.CONNECTOR_NAME, - method: "unstake", - args: [amount, 0], - }; + const unstakeSpell = { + connector: helpers.CONNECTOR_NAME, + method: "unstake", + args: [amount, 0], + }; - const unstakeTx = await dsa - .connect(wallet) - .cast(...encodeSpells([unstakeSpell]), wallet.address); + const unstakeTx = await dsa + .connect(wallet) + .cast(...encodeSpells([unstakeSpell]), wallet.address); - await unstakeTx.wait(); + await unstakeTx.wait(); - const lqtyBalance = await liquity.lqtyToken.balanceOf(dsa.address); - expect(lqtyBalance).to.eq(amount); + 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) - ); + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.sub(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.CONNECTOR_NAME, + method: "stake", + args: [amount, 0], + }; + + await dsa + .connect(wallet) + .cast(...encodeSpells([stakeSpell]), wallet.address); + + const setId = 0; + const unstakeSpell = { + connector: helpers.CONNECTOR_NAME, + method: "unstake", + args: [amount, setId], + }; + + const unstakeTx = await dsa + .connect(wallet) + .cast(...encodeSpells([unstakeSpell]), wallet.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"], + [wallet.address, amount, setId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogUnstake(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe.skip("claimStakingGains()", () => { + it("Claims gains from staking", async () => {}); + + it("returns Instadapp event name and data", async () => { + const stakerDsa = await buildDSAv2(wallet.address); + const whaleLqtyBalance = await liquity.lqtyToken.balanceOf( + helpers.JUSTIN_SUN_ADDRESS + ); + console.log("BALANCE", whaleLqtyBalance.toString()); + + // Stake lots of LQTY + await helpers.sendToken( + liquity.lqtyToken, + whaleLqtyBalance, + helpers.JUSTIN_SUN_ADDRESS, + stakerDsa.address + ); + const dsaBalance = await liquity.lqtyToken.balanceOf( + stakerDsa.address + ); + console.log("dsaBalance", dsaBalance.toString()); + await liquity.staking + .connect(stakerDsa.signer) + .stake(ethers.utils.parseUnits("1", 18)); + + // Open a Trove to cause an ETH issuance gain for stakers + await helpers.createDsaTrove( + dsa, + wallet, + liqiuty.hintHelpers, + liquity.sortedTroves + ); + + // Redeem some ETH to cause an LUSD redemption gain for stakers + await helpers.redeem( + ethers.utils.parseUnits("1000", 18), + contracts.STABILITY_POOL_ADDRESS, + wallet.address, + liquity + ); + + const setEthGainId = 0; + const setLusdGainId = 0; + const claimStakingGainsSpell = { + connector: helpers.CONNECTOR_NAME, + method: "claimStakingGains", + args: [setEthGainId, setLusdGainId], + }; + + const claimGainsTx = await stakerDsa + .connect(wallet) + .cast(...encodeSpells([claimStakingGainsSpell]), wallet.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"], + [helpers.JUSTIN_SUN_ADDRESS, setEthGainId, setLusdGainId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogClaimStakingGains(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); }); }); }); }); - -// TODO add set of tests to verify log return values are generated correctly