From 7db723fec308e89009fd4b9bdadf38504a004020 Mon Sep 17 00:00:00 2001 From: alpha-guy Date: Tue, 31 Aug 2021 21:44:08 +0530 Subject: [PATCH 1/7] Replace WETH with WBTC as collateral asset * Remove extra 'greater than equal to' comparisons. * WBTC only has 8 decimal places which is less compared to 18 decimal places in WETH. Lesser the number of decimal places, lesser is the chance of the aToken returning a balance with 1 wei error. * Also interest accrues with less granularity which saves us the pain of having to calculate interest rates and take those extra few wei into account. --- .../aaveUniswapLeverageDebtIssuance.spec.ts | 454 ++++++++---------- 1 file changed, 210 insertions(+), 244 deletions(-) diff --git a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts index a4450c2fa..6ef71df0e 100644 --- a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts +++ b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts @@ -37,7 +37,7 @@ import { ADDRESS_ZERO, ZERO, EMPTY_BYTES, MAX_UINT_256 } from "@utils/constants" const expect = getWaffleExpect(); -describe.skip("AaveUniswapLeverageDebtIssuance", () => { +describe("AaveUniswapLeverageDebtIssuance", () => { let owner: Account; let feeRecipient: Account; let deployer: DeployHelper; @@ -50,7 +50,7 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { let debtIssuanceModule: DebtIssuanceModuleV2; let uniswapExchangeAdapter: UniswapV2ExchangeAdapter; - let aWETH: AaveV2AToken; + let aWBTC: AaveV2AToken; let variableDebtDAI: AaveV2VariableDebtToken; let variableDebtUSDC: AaveV2VariableDebtToken; @@ -71,6 +71,18 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { aaveV2Setup = getAaveV2Fixture(owner.address); await aaveV2Setup.initialize(setup.weth.address, setup.dai.address); + + // Create a WBTC reserve + const wbtcReserveTokens = await aaveV2Setup.createAndEnableReserve( + setup.wbtc.address, "WBTC", BigNumber.from(8), + BigNumber.from(8000), // base LTV: 80% + BigNumber.from(8250), // liquidation threshold: 82.5% + BigNumber.from(10500), // liquidation bonus: 105.00% + BigNumber.from(1000), // reserve factor: 10% + true, // enable borrowing on reserve + true // enable stable debts + ); + // Create an USDC reserve const usdcReserveTokens = await aaveV2Setup.createAndEnableReserve( setup.usdc.address, "USDC", BigNumber.from(6), @@ -81,13 +93,15 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { true, // enable borrowing on reserve true // enable stable debts ); + + await aaveV2Setup.setAssetPriceInOracle(setup.wbtc.address, ether(1)); // Set to 1 ETH await aaveV2Setup.setAssetPriceInOracle(setup.usdc.address, ether(0.001)); // Set to $1000 ETH // Mint aTokens on Aave - await setup.weth.approve(aaveV2Setup.lendingPool.address, ether(1000)); + await setup.wbtc.approve(aaveV2Setup.lendingPool.address, bitcoin(1000)); await aaveV2Setup.lendingPool.deposit( - setup.weth.address, - ether(1000), + setup.wbtc.address, + bitcoin(1000), owner.address, ZERO ); @@ -106,36 +120,36 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { ZERO ); - aWETH = aaveV2Setup.wethReserveTokens.aToken; + aWBTC = wbtcReserveTokens.aToken; variableDebtUSDC = usdcReserveTokens.variableDebtToken; variableDebtDAI = aaveV2Setup.daiReserveTokens.variableDebtToken; - // Create ETH USDC pool and pool liquidity - await uniswapSetup.createNewPair(setup.weth.address, setup.usdc.address); + // Create WBTC USDC pool and pool liquidity + await uniswapSetup.createNewPair(setup.wbtc.address, setup.usdc.address); await setup.usdc.connect(owner.wallet).approve(uniswapSetup.router.address, MAX_UINT_256); - await setup.weth.connect(owner.wallet).approve(uniswapSetup.router.address, MAX_UINT_256); + await setup.wbtc.connect(owner.wallet).approve(uniswapSetup.router.address, MAX_UINT_256); await setup.dai.connect(owner.wallet).approve(uniswapSetup.router.address, MAX_UINT_256); - // 100 ETH = 100k USDC. 1 ETH = 1000 USDC + // 100 WBTC = 100k USDC. 1 WBTC = 1000 USDC await uniswapSetup.router.connect(owner.wallet).addLiquidity( - setup.weth.address, + setup.wbtc.address, setup.usdc.address, - ether(100), + bitcoin(100), usdc(100000), - ether(100), + bitcoin(100), usdc(100000), owner.address, MAX_UINT_256 ); - // 100 ETH = 100k DAI. 1 ETH = 1000 DAI + // 100 WBTC = 100k DAI. 1 WBTC = 1000 DAI await uniswapSetup.router.connect(owner.wallet).addLiquidity( - setup.weth.address, + setup.wbtc.address, setup.dai.address, - ether(100), + bitcoin(100), ether(100000), - ether(100), + bitcoin(100), ether(100000), owner.address, MAX_UINT_256 @@ -173,7 +187,7 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { describe("#issuance", async () => { let setToken: SetToken; let issueFee: BigNumber; - let aWETHUnits: BigNumber; + let aWBTCUnits: BigNumber; let actualSeizedTokens: BigNumber; let subjectSetToken: Address; @@ -182,15 +196,23 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { let subjectCaller: Account; let issueQuantity: BigNumber; + async function subject(): Promise { + return debtIssuanceModule.connect(subjectCaller.wallet).issue( + subjectSetToken, + subjectQuantity, + subjectTo, + ); + } + context("when a default aToken position with 0 supply", async () => { cacheBeforeEach(async () => { // Borrow some WETH to ensure the aWETH balance increases due to interest - await aaveV2Setup.lendingPool.borrow(setup.weth.address, ether(10), 2, ZERO, owner.address); + await aaveV2Setup.lendingPool.borrow(setup.wbtc.address, bitcoin(10), 2, ZERO, owner.address); - aWETHUnits = ether(1); + aWBTCUnits = bitcoin(1); setToken = await setup.createSetToken( - [aWETH.address], - [aWETHUnits], + [aWBTC.address], + [aWBTCUnits], [aaveLeverageModule.address, debtIssuanceModule.address] ); issueFee = ether(0.005); @@ -206,12 +228,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); await aaveLeverageModule.initialize( setToken.address, - [setup.weth.address], + [setup.wbtc.address], [setup.usdc.address] ); // Approve tokens to issuance module and call issue - await aWETH.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -224,14 +246,6 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { subjectCaller = owner; }); - async function subject(): Promise { - return debtIssuanceModule.connect(subjectCaller.wallet).issue( - subjectSetToken, - subjectQuantity, - subjectTo, - ); - } - it("should not update the collateral position on the SetToken", async () => { const initialPositions = await setToken.getPositions(); @@ -242,9 +256,9 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { expect(initialPositions.length).to.eq(1); expect(currentPositions.length).to.eq(1); - expect(newFirstPosition.component).to.eq(aWETH.address); + expect(newFirstPosition.component).to.eq(aWBTC.address); expect(newFirstPosition.positionState).to.eq(0); // Default - expect(newFirstPosition.unit).to.eq(aWETHUnits); + expect(newFirstPosition.unit).to.eq(aWBTCUnits); expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); }); @@ -260,8 +274,8 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { }); it("should have the correct token balances", async () => { - const preMinterAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const preSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const preMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const preMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const preSetUsdcBalance = await setup.usdc.balanceOf(subjectSetToken); @@ -269,18 +283,18 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { const mintQuantity = preciseMul(subjectQuantity, ether(1).add(issueFee)); const usdcFlows = preciseMulCeil(mintQuantity, ZERO); - const aWETHFlows = preciseMul(mintQuantity, aWETHUnits); + const aWBTCFlows = preciseMul(mintQuantity, aWBTCUnits); - const postMinterAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const postSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const postMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const postMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const postSetUsdcBalance = await setup.usdc.balanceOf(subjectSetToken); // Note: Due to 1 aToken = 1 underlying and interest accruing every block, it is difficult to track precise // balances without replicating interest rate logic. Therefore, we expect actual balances to be greater - // than expected due to interest accruals on aWETH - expect(postMinterAWETHBalance).to.gte(preMinterAWETHBalance.sub(aWETHFlows)); - expect(postSetAWETHBalance).to.gte(preSetAWETHBalance.add(aWETHFlows)); + // than expected due to interest accruals on aWBTC + expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.add(usdcFlows)); // No debt expect(postSetUsdcBalance).to.eq(preSetUsdcBalance); // No debt }); @@ -290,13 +304,13 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { let issueQuantity: BigNumber; cacheBeforeEach(async () => { - // Borrow some WETH to ensure the aWETH balance increases due to interest - await aaveV2Setup.lendingPool.borrow(setup.weth.address, ether(10), 2, ZERO, owner.address); + // Borrow some WBTC to ensure the aWBTC balance increases due to interest + await aaveV2Setup.lendingPool.borrow(setup.wbtc.address, bitcoin(10), 2, ZERO, owner.address); - aWETHUnits = ether(1); + aWBTCUnits = bitcoin(1); setToken = await setup.createSetToken( - [aWETH.address], - [aWETHUnits], + [aWBTC.address], + [aWBTCUnits], [aaveLeverageModule.address, debtIssuanceModule.address] ); issueFee = ether(0.005); @@ -312,12 +326,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); await aaveLeverageModule.initialize( setToken.address, - [setup.weth.address], + [setup.wbtc.address], [setup.usdc.address] ); // Approve tokens to issuance module and call issue - await aWETH.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -327,9 +341,9 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.lever( setToken.address, setup.usdc.address, - setup.weth.address, + setup.wbtc.address, usdc(500), - ether(0.4), + bitcoin(0.4), "UniswapV2ExchangeAdapter", EMPTY_BYTES ); @@ -342,28 +356,20 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { subjectCaller = owner; }); - async function subject(): Promise { - return debtIssuanceModule.connect(subjectCaller.wallet).issue( - subjectSetToken, - subjectQuantity, - subjectTo, - ); - } - it("should update the collateral position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); await subject(); - // aWETH position is increased + // aWBTC position is increased const currentPositions = await setToken.getPositions(); const newFirstPosition = (await setToken.getPositions())[0]; expect(initialPositions.length).to.eq(2); expect(currentPositions.length).to.eq(2); - expect(newFirstPosition.component).to.eq(aWETH.address); + expect(newFirstPosition.component).to.eq(aWBTC.address); expect(newFirstPosition.positionState).to.eq(0); // Default - expect(newFirstPosition.unit).to.gte(initialPositions[0].unit); // Should be greater due to interest accrual + expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); }); @@ -387,27 +393,27 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { }); it("should have the correct token balances", async () => { - const preMinteraWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const preSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const preMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const preMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); const mintQuantity = preciseMul(subjectQuantity, ether(1).add(issueFee)); - const newAWETHPositionUnits = (await setToken.getPositions())[0].unit; - const aWETHFlows = preciseMulCeil(mintQuantity, newAWETHPositionUnits); + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const aWBTCFlows = preciseMulCeil(mintQuantity, newAWBTCPositionUnits); await subject(); const debtPositionUnits = (await setToken.getPositions())[1].unit; const usdcFlows = preciseMul(mintQuantity, debtPositionUnits).mul(-1); - const postMinteraWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const postSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const postMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const postMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); - expect(postMinteraWETHBalance).to.gte(preMinteraWETHBalance.sub(aWETHFlows)); - expect(postSetAWETHBalance).to.gte(preSetAWETHBalance.add(aWETHFlows)); + expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.add(usdcFlows)); expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcFlows)); }); @@ -417,13 +423,13 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { let issueQuantity: BigNumber; cacheBeforeEach(async () => { - // TODO: Borrow some WETH to ensure the aWETH balance increases due to interest - // await aaveV2Setup.lendingPool.borrow(setup.weth.address, ether(10), 2, ZERO, owner.address); + // Borrow some WBTC to ensure the aWBTC balance increases due to interest + await aaveV2Setup.lendingPool.borrow(setup.wbtc.address, bitcoin(10), 2, ZERO, owner.address); - aWETHUnits = ether(1); + aWBTCUnits = bitcoin(1); setToken = await setup.createSetToken( - [aWETH.address], - [aWETHUnits], + [aWBTC.address], + [aWBTCUnits], [aaveLeverageModule.address, debtIssuanceModule.address] ); issueFee = ether(0.005); @@ -439,12 +445,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); await aaveLeverageModule.initialize( setToken.address, - [setup.weth.address], + [setup.wbtc.address], [setup.usdc.address] ); // Approve tokens to issuance module and call issue - await aWETH.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -454,14 +460,14 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.lever( setToken.address, setup.usdc.address, - setup.weth.address, + setup.wbtc.address, usdc(750), - ether(0.5), + bitcoin(0.5), "UniswapV2ExchangeAdapter", EMPTY_BYTES ); - // ETH decreases to $400 + // ETH decreases to $400. 1 WBTC = 1 WETH. So, WBTC = 400$. const liquidationUsdcPriceInEth = ether(0.0025); // 1/400 = 0.0025 await aaveV2Setup.setAssetPriceInOracle(setup.usdc.address, liquidationUsdcPriceInEth); @@ -469,20 +475,20 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { const debtToCoverHumanReadable = 200; actualSeizedTokens = preciseMul( preciseMul(liquidationUsdcPriceInEth, ether(debtToCoverHumanReadable)), - ether(1.05) // 5% liquidation bonus as configured in fixture + bitcoin(1.05) // 5% liquidation bonus as configured in fixture ); const debtToCover = usdc(debtToCoverHumanReadable); await setup.usdc.approve(aaveV2Setup.lendingPool.address, ether(250)); await aaveV2Setup.lendingPool.connect(owner.wallet).liquidationCall( - setup.weth.address, + setup.wbtc.address, setup.usdc.address, setToken.address, debtToCover, true ); - // ETH increases to $1000 to allow more borrow + // WBTC increases to $1000 to allow more borrow await aaveV2Setup.setAssetPriceInOracle(setup.usdc.address, ether(0.001)); // 1/1000 = .001 }); @@ -493,27 +499,21 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { subjectCaller = owner; }); - async function subject(): Promise { - return debtIssuanceModule.connect(subjectCaller.wallet).issue( - subjectSetToken, - subjectQuantity, - subjectTo, - ); - } - it("should update the collateral position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); const setTotalSupply = await setToken.totalSupply(); await subject(); - // aWETH position is increased + const currentPositions = await setToken.getPositions(); const newFirstPosition = (await setToken.getPositions())[0]; + // aWBTC position has decreased const expectedPostLiquidationUnit = initialPositions[0].unit.sub(preciseDivCeil(actualSeizedTokens, setTotalSupply)); + expect(initialPositions.length).to.eq(2); expect(currentPositions.length).to.eq(2); - expect(newFirstPosition.component).to.eq(aWETH.address); + expect(newFirstPosition.component).to.eq(aWBTC.address); expect(newFirstPosition.positionState).to.eq(0); // Default expect(newFirstPosition.unit).to.eq(expectedPostLiquidationUnit); expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); @@ -522,12 +522,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { it("should update the borrow position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); + // Get debt balance after some debt is paid during liquidation const previousSecondPositionBalance = await variableDebtUSDC.balanceOf(setToken.address); const setTotalSupply = await setToken.totalSupply(); await subject(); - // aWETH position is increased const currentPositions = await setToken.getPositions(); const newSecondPosition = (await setToken.getPositions())[1]; @@ -541,29 +541,29 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { }); it("should have the correct token balances", async () => { - const preMinterAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const preSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const preMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const preMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); const mintQuantity = preciseMul(subjectQuantity, ether(1).add(issueFee)); - const newAWETHPositionUnits = (await setToken.getPositions())[0].unit; - const aWETHFlows = preciseMulCeil(mintQuantity, newAWETHPositionUnits).sub(actualSeizedTokens); + const aWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const aWBTCFlows = preciseMulCeil(mintQuantity, aWBTCPositionUnits).sub(actualSeizedTokens); await subject(); const debtPositionUnits = (await setToken.getPositions())[1].unit; const usdcFlows = preciseMul(mintQuantity, debtPositionUnits).mul(-1); - const postMinterAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const postSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const postMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const postMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); - expect(postMinterAWETHBalance).to.eq(preMinterAWETHBalance.sub(aWETHFlows)); - expect(postSetAWETHBalance).to.eq(preSetAWETHBalance.add(aWETHFlows)); + expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.add(usdcFlows)); - expect(postSetUsdcDebtBalance).to.gte(preSetUsdcDebtBalance.add(usdcFlows)); + expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcFlows)); }); }); @@ -572,10 +572,10 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { cacheBeforeEach(async () => { setToken = await setup.createSetToken( - [aWETH.address, setup.wbtc.address], + [aWBTC.address, setup.weth.address], [ - ether(1), bitcoin(1), + ether(1), ], [aaveLeverageModule.address, debtIssuanceModule.address] ); @@ -592,13 +592,13 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); await aaveLeverageModule.initialize( setToken.address, - [setup.weth.address], + [setup.wbtc.address], [setup.dai.address, setup.usdc.address] ); // Approve tokens to issuance module and call issue - await aWETH.approve(debtIssuanceModule.address, MAX_UINT_256); - await setup.wbtc.approve(debtIssuanceModule.address, MAX_UINT_256); + await aWBTC.approve(debtIssuanceModule.address, MAX_UINT_256); + await setup.weth.approve(debtIssuanceModule.address, MAX_UINT_256); // Issue 1 SetToken issueQuantity = ether(1); @@ -608,9 +608,9 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.lever( setToken.address, setup.dai.address, - setup.weth.address, + setup.wbtc.address, ether(100), - ether(0.01), + bitcoin(0.01), "UniswapV2ExchangeAdapter", EMPTY_BYTES ); @@ -618,9 +618,9 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.lever( setToken.address, setup.usdc.address, - setup.weth.address, + setup.wbtc.address, usdc(100), - ether(0.01), + bitcoin(0.01), "UniswapV2ExchangeAdapter", EMPTY_BYTES ); @@ -633,20 +633,11 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { subjectCaller = owner; }); - async function subject(): Promise { - return debtIssuanceModule.connect(subjectCaller.wallet).issue( - subjectSetToken, - subjectQuantity, - subjectTo, - ); - } - it("should update the collateral position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); await subject(); - // aWETH position is increased const currentPositions = await setToken.getPositions(); const newFirstPosition = (await setToken.getPositions())[0]; const newSecondPosition = (await setToken.getPositions())[1]; @@ -654,12 +645,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { expect(initialPositions.length).to.eq(4); expect(currentPositions.length).to.eq(4); - expect(newFirstPosition.component).to.eq(aWETH.address); + expect(newFirstPosition.component).to.eq(aWBTC.address); expect(newFirstPosition.positionState).to.eq(0); // Default expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); - expect(newSecondPosition.component).to.eq(setup.wbtc.address); + expect(newSecondPosition.component).to.eq(setup.weth.address); expect(newSecondPosition.positionState).to.eq(0); // Default expect(newSecondPosition.unit).to.eq(initialPositions[1].unit); // Should be unchanged expect(newSecondPosition.module).to.eq(ADDRESS_ZERO); @@ -691,19 +682,19 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { }); it("should have the correct token balances", async () => { - const preMinterAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const preSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const preMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const preMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); const preMinterDaiBalance = await setup.dai.balanceOf(subjectCaller.address); const preSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); - const preMinterWbtcBalance = await setup.wbtc.balanceOf(subjectCaller.address); - const preSetWbtcBalance = await setup.wbtc.balanceOf(subjectSetToken); + const preMinterWethBalance = await setup.weth.balanceOf(subjectCaller.address); + const preSetWethBalance = await setup.weth.balanceOf(subjectSetToken); const mintQuantity = preciseMul(subjectQuantity, ether(1).add(issueFee)); const setTotalSupply = await setToken.totalSupply(); - const newAWETHPositionUnits = (await setToken.getPositions())[0].unit; - const aWETHFlows = preciseMulCeil(mintQuantity, newAWETHPositionUnits); + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const aWBTCFlows = preciseMulCeil(mintQuantity, newAWBTCPositionUnits); await subject(); @@ -711,25 +702,25 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { const usdcDebtPositionUnits = (await setToken.getPositions())[3].unit; const daiFlows = preciseMul(mintQuantity, daiDebtPositionUnits).mul(-1); const usdcFlows = preciseMul(mintQuantity, usdcDebtPositionUnits).mul(-1); - const wbtcFlows = preciseMul(bitcoin(1), setTotalSupply); // 1 BTC unit + const wethFlows = preciseMul(ether(1), setTotalSupply); // 1 WETH unit - const postMinterAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const postSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const postMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const postMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); const postMinterDaiBalance = await setup.dai.balanceOf(subjectCaller.address); const postSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); - const postMinterWbtcBalance = await setup.wbtc.balanceOf(subjectCaller.address); - const postSetWbtcBalance = await setup.wbtc.balanceOf(subjectSetToken); + const postMinterWethBalance = await setup.weth.balanceOf(subjectCaller.address); + const postSetWethBalance = await setup.weth.balanceOf(subjectSetToken); - expect(postMinterAWETHBalance).to.eq(preMinterAWETHBalance.sub(aWETHFlows)); - expect(postSetAWETHBalance).to.eq(preSetAWETHBalance.add(aWETHFlows)); + expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.add(usdcFlows)); expect(postSetUsdcDebtBalance).to.gte(preSetUsdcDebtBalance.add(usdcFlows)); // Round up due to interest accrual expect(postMinterDaiBalance).to.eq(preMinterDaiBalance.add(daiFlows)); expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); // Round up due to interest accrual - expect(postMinterWbtcBalance).to.eq(preMinterWbtcBalance.sub(wbtcFlows)); - expect(postSetWbtcBalance).to.eq(preSetWbtcBalance.add(wbtcFlows)); + expect(postMinterWethBalance).to.eq(preMinterWethBalance.sub(wethFlows)); + expect(postSetWethBalance).to.eq(preSetWethBalance.add(wethFlows)); }); }); }); @@ -737,7 +728,7 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { describe("#redemption", async () => { let setToken: SetToken; let redeemFee: BigNumber; - let aWETHUnits: BigNumber; + let aWBTCUnits: BigNumber; let actualSeizedTokens: BigNumber; let subjectSetToken: Address; @@ -746,12 +737,21 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { let subjectCaller: Account; let issueQuantity: BigNumber; + async function subject(): Promise { + return debtIssuanceModule.connect(subjectCaller.wallet).redeem( + subjectSetToken, + subjectQuantity, + subjectTo, + ); + } + + context("when a default aToken position and redeem will take supply to 0", async () => { cacheBeforeEach(async () => { - aWETHUnits = ether(1); + aWBTCUnits = bitcoin(1); setToken = await setup.createSetToken( - [aWETH.address], - [aWETHUnits], + [aWBTC.address], + [aWBTCUnits], [aaveLeverageModule.address, debtIssuanceModule.address] ); await debtIssuanceModule.initialize( @@ -766,12 +766,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); await aaveLeverageModule.initialize( setToken.address, - [setup.weth.address], + [setup.wbtc.address], [setup.usdc.address] ); // Approve tokens to issuance module and call issue - await aWETH.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, ether(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -785,14 +785,6 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { subjectCaller = owner; }); - async function subject(): Promise { - return debtIssuanceModule.connect(subjectCaller.wallet).redeem( - subjectSetToken, - subjectQuantity, - subjectTo, - ); - } - it("should update the collateral position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); await subject(); @@ -801,9 +793,9 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { const newFirstPosition = (await setToken.getPositions())[0]; expect(initialPositions.length).to.eq(1); expect(currentPositions.length).to.eq(1); - expect(newFirstPosition.component).to.eq(aWETH.address); + expect(newFirstPosition.component).to.eq(aWBTC.address); expect(newFirstPosition.positionState).to.eq(0); // Default - expect(newFirstPosition.unit).to.eq(aWETHUnits); + expect(newFirstPosition.unit).to.eq(aWBTCUnits); expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); }); @@ -819,8 +811,8 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { }); it("should have the correct token balances", async () => { - const preMinterAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const preSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const preMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const preMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const preSetUsdcBalance = await setup.usdc.balanceOf(subjectSetToken); @@ -828,15 +820,15 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { const mintQuantity = preciseMul(subjectQuantity, ether(1)); const usdcFlows = preciseMulCeil(mintQuantity, ZERO); - const aWETHFlows = preciseMul(mintQuantity, aWETHUnits); + const aWBTCFlows = preciseMul(mintQuantity, aWBTCUnits); - const postMinterAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const postSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const postMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const postMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const postSetUsdcBalance = await setup.usdc.balanceOf(subjectSetToken); - expect(postMinterAWETHBalance).to.eq(preMinterAWETHBalance.add(aWETHFlows)); - expect(postSetAWETHBalance).to.eq(preSetAWETHBalance.sub(aWETHFlows)); + expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.add(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.sub(aWBTCFlows)); expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.sub(usdcFlows)); // No debt expect(postSetUsdcBalance).to.eq(preSetUsdcBalance); // No debt }); @@ -847,10 +839,10 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { cacheBeforeEach(async () => { - aWETHUnits = ether(1); + aWBTCUnits = bitcoin(1); setToken = await setup.createSetToken( - [aWETH.address], - [aWETHUnits], + [aWBTC.address], + [aWBTCUnits], [aaveLeverageModule.address, debtIssuanceModule.address] ); redeemFee = ether(0.005); @@ -866,12 +858,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); await aaveLeverageModule.initialize( setToken.address, - [setup.weth.address], + [setup.wbtc.address], [setup.usdc.address] ); // Approve tokens to issuance module and call issue - await aWETH.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, ether(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -881,9 +873,9 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.lever( setToken.address, setup.usdc.address, - setup.weth.address, + setup.wbtc.address, usdc(500), - ether(0.4), + bitcoin(0.4), "UniswapV2ExchangeAdapter", EMPTY_BYTES ); @@ -899,28 +891,19 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { subjectCaller = owner; }); - async function subject(): Promise { - return debtIssuanceModule.connect(subjectCaller.wallet).redeem( - subjectSetToken, - subjectQuantity, - subjectTo, - ); - } - it("should update the collateral position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); await subject(); - // aWETH position is increased const currentPositions = await setToken.getPositions(); const newFirstPosition = (await setToken.getPositions())[0]; expect(initialPositions.length).to.eq(2); expect(currentPositions.length).to.eq(2); - expect(newFirstPosition.component).to.eq(aWETH.address); + expect(newFirstPosition.component).to.eq(aWBTC.address); expect(newFirstPosition.positionState).to.eq(0); // Default - expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); // Should be greater due to interest accrual + expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); }); @@ -939,13 +922,13 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { expect(currentPositions.length).to.eq(2); expect(newSecondPosition.component).to.eq(setup.usdc.address); expect(newSecondPosition.positionState).to.eq(1); // External - expect(newSecondPosition.unit).to.eq(expectedSecondPositionUnit); // Should be greater due to interest accrual + expect(newSecondPosition.unit).to.eq(expectedSecondPositionUnit); expect(newSecondPosition.module).to.eq(aaveLeverageModule.address); }); it("should have the correct token balances", async () => { - const preRedeemerAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const preSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const preRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const preRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); @@ -953,18 +936,18 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await subject(); - const newAWETHPositionUnits = (await setToken.getPositions())[0].unit; - const aWETHFlows = preciseMul(redeemQuantity, newAWETHPositionUnits); + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const aWBTCFlows = preciseMul(redeemQuantity, newAWBTCPositionUnits); const debtPositionUnits = (await setToken.getPositions())[1].unit; const usdcFlows = preciseMulCeil(redeemQuantity, debtPositionUnits.mul(-1)); - const postRedeemerAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const postSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const postRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const postRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); - expect(postRedeemerAWETHBalance).to.eq(preRedeemerAWETHBalance.add(aWETHFlows)); - expect(postSetAWETHBalance).to.eq(preSetAWETHBalance.sub(aWETHFlows)); + expect(postRedeemerAWBTCBalance).to.eq(preRedeemerAWBTCBalance.add(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.sub(aWBTCFlows)); expect(postRedeemerUsdcBalance).to.eq(preRedeemerUsdcBalance.sub(usdcFlows)); expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); }); @@ -975,10 +958,10 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { cacheBeforeEach(async () => { - aWETHUnits = ether(1); + aWBTCUnits = bitcoin(1); setToken = await setup.createSetToken( - [aWETH.address], - [aWETHUnits], + [aWBTC.address], + [aWBTCUnits], [aaveLeverageModule.address, debtIssuanceModule.address] ); await debtIssuanceModule.initialize( @@ -993,12 +976,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); await aaveLeverageModule.initialize( setToken.address, - [setup.weth.address], + [setup.wbtc.address], [setup.usdc.address] ); // Approve tokens to issuance module and call issue - await aWETH.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -1008,9 +991,9 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.lever( setToken.address, setup.usdc.address, - setup.weth.address, + setup.wbtc.address, usdc(500), - ether(0.4), + bitcoin(0.4), "UniswapV2ExchangeAdapter", EMPTY_BYTES ); @@ -1026,26 +1009,17 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { subjectCaller = owner; }); - async function subject(): Promise { - return debtIssuanceModule.connect(subjectCaller.wallet).redeem( - subjectSetToken, - subjectQuantity, - subjectTo, - ); - } - it("should update the collateral position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); await subject(); - // aWETH position is increased const currentPositions = await setToken.getPositions(); const newFirstPosition = (await setToken.getPositions())[0]; expect(initialPositions.length).to.eq(2); expect(currentPositions.length).to.eq(2); - expect(newFirstPosition.component).to.eq(aWETH.address); + expect(newFirstPosition.component).to.eq(aWBTC.address); expect(newFirstPosition.positionState).to.eq(0); // Default expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); @@ -1073,8 +1047,8 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { }); it("should have the correct token balances", async () => { - const preRedeemerAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const preSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const preRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const preRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); @@ -1082,18 +1056,18 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await subject(); - const newAWETHPositionUnits = (await setToken.getPositions())[0].unit; - const aWETHFlows = preciseMul(redeemQuantity, newAWETHPositionUnits); + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const aWBTCFlows = preciseMul(redeemQuantity, newAWBTCPositionUnits); const debtPositionUnits = (await setToken.getPositions())[1].unit; const usdcFlows = preciseMulCeil(redeemQuantity, debtPositionUnits.mul(-1)); - const postRedeemerAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const postSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const postRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const postRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); - expect(postRedeemerAWETHBalance).to.eq(preRedeemerAWETHBalance.add(aWETHFlows)); - expect(postSetAWETHBalance).to.eq(preSetAWETHBalance.sub(aWETHFlows)); + expect(postRedeemerAWBTCBalance).to.eq(preRedeemerAWBTCBalance.add(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.sub(aWBTCFlows)); expect(postRedeemerUsdcBalance).to.eq(preRedeemerUsdcBalance.sub(usdcFlows)); expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); }); @@ -1104,10 +1078,10 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { cacheBeforeEach(async () => { - aWETHUnits = ether(1); + aWBTCUnits = bitcoin(1); setToken = await setup.createSetToken( - [aWETH.address], - [aWETHUnits], + [aWBTC.address], + [aWBTCUnits], [aaveLeverageModule.address, debtIssuanceModule.address] ); redeemFee = ether(0.005); @@ -1123,12 +1097,12 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); await aaveLeverageModule.initialize( setToken.address, - [setup.weth.address], + [setup.wbtc.address], [setup.usdc.address] ); // Approve tokens to issuance module and call issue - await aWETH.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -1138,14 +1112,14 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { await aaveLeverageModule.lever( setToken.address, setup.usdc.address, - setup.weth.address, + setup.wbtc.address, usdc(750), - ether(0.5), + bitcoin(0.5), "UniswapV2ExchangeAdapter", EMPTY_BYTES ); - // ETH decreases to $400 + // ETH decreases to $400. 1 WBTC = 1 ETH = 400$. const liquidationUsdcPriceInEth = ether(0.0025); // 1/400 = 0.0025 await aaveV2Setup.setAssetPriceInOracle(setup.usdc.address, liquidationUsdcPriceInEth); @@ -1153,20 +1127,20 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { const debtToCoverHumanReadable = 200; actualSeizedTokens = preciseMul( preciseMul(liquidationUsdcPriceInEth, ether(debtToCoverHumanReadable)), - ether(1.05) // 5% liquidation bonus as configured in fixture + bitcoin(1.05) // 5% liquidation bonus as configured in fixture ); const debtToCover = usdc(debtToCoverHumanReadable); await setup.usdc.approve(aaveV2Setup.lendingPool.address, ether(250)); await aaveV2Setup.lendingPool.connect(owner.wallet).liquidationCall( - setup.weth.address, + setup.wbtc.address, setup.usdc.address, setToken.address, debtToCover, true ); - // ETH increases to $1000 to allow more borrow + // WBTC increases to $1000 to allow more borrow await aaveV2Setup.setAssetPriceInOracle(setup.usdc.address, ether(0.001)); // 1/1000 = .001 // Approve debt token to issuance module for redeem @@ -1180,14 +1154,6 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { subjectCaller = owner; }); - async function subject(): Promise { - return debtIssuanceModule.connect(subjectCaller.wallet).redeem( - subjectSetToken, - subjectQuantity, - subjectTo, - ); - } - it("should update the collateral position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); const setTotalSupply = await setToken.totalSupply(); @@ -1198,7 +1164,7 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { const expectedPostLiquidationUnit = initialPositions[0].unit.sub(preciseDivCeil(actualSeizedTokens, setTotalSupply)); expect(initialPositions.length).to.eq(2); expect(currentPositions.length).to.eq(2); - expect(newFirstPosition.component).to.eq(aWETH.address); + expect(newFirstPosition.component).to.eq(aWBTC.address); expect(newFirstPosition.positionState).to.eq(0); // Default expect(newFirstPosition.unit).to.eq(expectedPostLiquidationUnit); expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); @@ -1208,7 +1174,7 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { const initialPositions = await setToken.getPositions(); await subject(); - // aWETH position is increased + // aWBTC position is increased const setTotalSupply = await setToken.totalSupply(); const currentPositions = await setToken.getPositions(); const newSecondPosition = (await setToken.getPositions())[1]; @@ -1219,35 +1185,35 @@ describe.skip("AaveUniswapLeverageDebtIssuance", () => { expect(currentPositions.length).to.eq(2); expect(newSecondPosition.component).to.eq(setup.usdc.address); expect(newSecondPosition.positionState).to.eq(1); // External - expect(newSecondPositionNotional).to.gte(previousSecondPositionBalance); + expect(newSecondPositionNotional).to.gte(previousSecondPositionBalance); // Debt accrues expect(newSecondPosition.module).to.eq(aaveLeverageModule.address); }); it("should have the correct token balances", async () => { - const preRedeemerAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const preSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const preRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const preRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); await subject(); const redeemQuantity = preciseMul(subjectQuantity, ether(1).sub(redeemFee)); - const newAWETHPositionUnits = (await setToken.getPositions())[0].unit; - const aWETHFlows = preciseMul(redeemQuantity, newAWETHPositionUnits); + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const aWBTCFlows = preciseMul(redeemQuantity, newAWBTCPositionUnits); const debtPositionUnits = (await setToken.getPositions())[1].unit; const usdcFlows = preciseMulCeil(redeemQuantity, debtPositionUnits.mul(-1)); - const postRedeemerAWETHBalance = await aWETH.balanceOf(subjectCaller.address); - const postSetAWETHBalance = await aWETH.balanceOf(subjectSetToken); + const postRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); const postRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); - expect(postRedeemerAWETHBalance).to.eq(preRedeemerAWETHBalance.add(aWETHFlows)); - expect(postSetAWETHBalance).to.gte(preSetAWETHBalance.sub(aWETHFlows)); + expect(postRedeemerAWBTCBalance).to.eq(preRedeemerAWBTCBalance.add(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.sub(aWBTCFlows)); expect(postRedeemerUsdcBalance).to.eq(preRedeemerUsdcBalance.sub(usdcFlows)); expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); }); }); }); -}); +}); \ No newline at end of file From 0acab9452c5dc640d6e08c4e73adc9bd403c81dd Mon Sep 17 00:00:00 2001 From: alpha-guy Date: Wed, 1 Sep 2021 17:00:54 +0530 Subject: [PATCH 2/7] Add test when deafult and external positions overlap --- .../aaveUniswapLeverageDebtIssuance.spec.ts | 462 ++++++++++++------ 1 file changed, 305 insertions(+), 157 deletions(-) diff --git a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts index 6ef71df0e..365fd8eeb 100644 --- a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts +++ b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts @@ -301,8 +301,6 @@ describe("AaveUniswapLeverageDebtIssuance", () => { }); context("when a default aToken position and external borrow position", async () => { - let issueQuantity: BigNumber; - cacheBeforeEach(async () => { // Borrow some WBTC to ensure the aWBTC balance increases due to interest await aaveV2Setup.lendingPool.borrow(setup.wbtc.address, bitcoin(10), 2, ZERO, owner.address); @@ -420,8 +418,6 @@ describe("AaveUniswapLeverageDebtIssuance", () => { }); context("when a default aToken position and liquidated borrow position", async () => { - let issueQuantity: BigNumber; - cacheBeforeEach(async () => { // Borrow some WBTC to ensure the aWBTC balance increases due to interest await aaveV2Setup.lendingPool.borrow(setup.wbtc.address, bitcoin(10), 2, ZERO, owner.address); @@ -568,159 +564,312 @@ describe("AaveUniswapLeverageDebtIssuance", () => { }); context("when 2 default positions and 2 external borrow positions", async () => { - let issueQuantity: BigNumber; - - cacheBeforeEach(async () => { - setToken = await setup.createSetToken( - [aWBTC.address, setup.weth.address], - [ - bitcoin(1), - ether(1), - ], - [aaveLeverageModule.address, debtIssuanceModule.address] - ); - issueFee = ether(0.005); - await debtIssuanceModule.initialize( - setToken.address, - ether(0.02), - issueFee, - ether(0.005), - feeRecipient.address, - ADDRESS_ZERO - ); - // Add SetToken to allow list - await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); - await aaveLeverageModule.initialize( - setToken.address, - [setup.wbtc.address], - [setup.dai.address, setup.usdc.address] - ); - - // Approve tokens to issuance module and call issue - await aWBTC.approve(debtIssuanceModule.address, MAX_UINT_256); - await setup.weth.approve(debtIssuanceModule.address, MAX_UINT_256); - - // Issue 1 SetToken - issueQuantity = ether(1); - await debtIssuanceModule.issue(setToken.address, issueQuantity, owner.address); - - // Lever up - await aaveLeverageModule.lever( - setToken.address, - setup.dai.address, - setup.wbtc.address, - ether(100), - bitcoin(0.01), - "UniswapV2ExchangeAdapter", - EMPTY_BYTES - ); - - await aaveLeverageModule.lever( - setToken.address, - setup.usdc.address, - setup.wbtc.address, - usdc(100), - bitcoin(0.01), - "UniswapV2ExchangeAdapter", - EMPTY_BYTES - ); - }); - - beforeEach(() => { - subjectSetToken = setToken.address; - subjectQuantity = issueQuantity; - subjectTo = owner.address; - subjectCaller = owner; + context("when default and external positions do not overlap", async () => { + cacheBeforeEach(async () => { + setToken = await setup.createSetToken( + [aWBTC.address, setup.weth.address], + [ + bitcoin(1), + ether(1), + ], + [aaveLeverageModule.address, debtIssuanceModule.address] + ); + issueFee = ether(0.005); + await debtIssuanceModule.initialize( + setToken.address, + ether(0.02), + issueFee, + ether(0.005), + feeRecipient.address, + ADDRESS_ZERO + ); + // Add SetToken to allow list + await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); + await aaveLeverageModule.initialize( + setToken.address, + [setup.wbtc.address], + [setup.dai.address, setup.usdc.address] + ); + + // Approve tokens to issuance module and call issue + await aWBTC.approve(debtIssuanceModule.address, MAX_UINT_256); + await setup.weth.approve(debtIssuanceModule.address, MAX_UINT_256); + + // Issue 1 SetToken + issueQuantity = ether(1); + await debtIssuanceModule.issue(setToken.address, issueQuantity, owner.address); + + // Lever up + await aaveLeverageModule.lever( + setToken.address, + setup.dai.address, + setup.wbtc.address, + ether(100), + bitcoin(0.01), + "UniswapV2ExchangeAdapter", + EMPTY_BYTES + ); + + await aaveLeverageModule.lever( + setToken.address, + setup.usdc.address, + setup.wbtc.address, + usdc(100), + bitcoin(0.01), + "UniswapV2ExchangeAdapter", + EMPTY_BYTES + ); + }); + + beforeEach(() => { + subjectSetToken = setToken.address; + subjectQuantity = issueQuantity; + subjectTo = owner.address; + subjectCaller = owner; + }); + + it("should update the collateral position on the SetToken correctly", async () => { + const initialPositions = await setToken.getPositions(); + + await subject(); + + const currentPositions = await setToken.getPositions(); + const newFirstPosition = (await setToken.getPositions())[0]; + const newSecondPosition = (await setToken.getPositions())[1]; + + expect(initialPositions.length).to.eq(4); + expect(currentPositions.length).to.eq(4); + + expect(newFirstPosition.component).to.eq(aWBTC.address); + expect(newFirstPosition.positionState).to.eq(0); // Default + expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); + expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); + + expect(newSecondPosition.component).to.eq(setup.weth.address); + expect(newSecondPosition.positionState).to.eq(0); // Default + expect(newSecondPosition.unit).to.eq(initialPositions[1].unit); // Should be unchanged + expect(newSecondPosition.module).to.eq(ADDRESS_ZERO); + }); + + it("should update the borrow position on the SetToken correctly", async () => { + + await subject(); + + const setTotalSupply = await setToken.totalSupply(); + const previousThirdPositionBalance = await variableDebtDAI.balanceOf(setToken.address); + const expectedThirdPositionUnit = preciseDiv(previousThirdPositionBalance, setTotalSupply).mul(-1); + + const previousFourthPositionBalance = await variableDebtUSDC.balanceOf(setToken.address); + const expectedFourthPositionUnit = preciseDiv(previousFourthPositionBalance, setTotalSupply).mul(-1); + + const newThirdPosition = (await setToken.getPositions())[2]; + const newFourthPosition = (await setToken.getPositions())[3]; + + expect(newThirdPosition.component).to.eq(setup.dai.address); + expect(newThirdPosition.positionState).to.eq(1); // External + expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); // Debt accrues + expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); + + expect(newFourthPosition.component).to.eq(setup.usdc.address); + expect(newFourthPosition.positionState).to.eq(1); // External + expect(newFourthPosition.unit).to.eq(expectedFourthPositionUnit); + expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); + }); + + it("should have the correct token balances", async () => { + const preMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); + const preMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); + const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); + const preMinterDaiBalance = await setup.dai.balanceOf(subjectCaller.address); + const preSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); + const preMinterWethBalance = await setup.weth.balanceOf(subjectCaller.address); + const preSetWethBalance = await setup.weth.balanceOf(subjectSetToken); + + const mintQuantity = preciseMul(subjectQuantity, ether(1).add(issueFee)); + // const setTotalSupply = await setToken.totalSupply(); + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const wethPositionUnits = (await setToken.getPositions())[1].unit; + const aWBTCFlows = preciseMulCeil(mintQuantity, newAWBTCPositionUnits); + const wethFlows = preciseMul(mintQuantity, wethPositionUnits); + + await subject(); + + const daiDebtPositionUnits = (await setToken.getPositions())[2].unit; + const usdcDebtPositionUnits = (await setToken.getPositions())[3].unit; + const daiFlows = preciseMul(mintQuantity, daiDebtPositionUnits).mul(-1); + const usdcFlows = preciseMul(mintQuantity, usdcDebtPositionUnits).mul(-1); + + const postMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); + const postMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); + const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); + const postMinterDaiBalance = await setup.dai.balanceOf(subjectCaller.address); + const postSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); + const postMinterWethBalance = await setup.weth.balanceOf(subjectCaller.address); + const postSetWethBalance = await setup.weth.balanceOf(subjectSetToken); + + expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); + expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.add(usdcFlows)); + expect(postSetUsdcDebtBalance).to.gte(preSetUsdcDebtBalance.add(usdcFlows)); // Round up due to interest accrual + expect(postMinterDaiBalance).to.eq(preMinterDaiBalance.add(daiFlows)); + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); // Round up due to interest accrual + expect(postMinterWethBalance).to.eq(preMinterWethBalance.sub(wethFlows)); + expect(postSetWethBalance).to.eq(preSetWethBalance.add(wethFlows)); + }); }); - - it("should update the collateral position on the SetToken correctly", async () => { - const initialPositions = await setToken.getPositions(); - - await subject(); - - const currentPositions = await setToken.getPositions(); - const newFirstPosition = (await setToken.getPositions())[0]; - const newSecondPosition = (await setToken.getPositions())[1]; - - expect(initialPositions.length).to.eq(4); - expect(currentPositions.length).to.eq(4); - - expect(newFirstPosition.component).to.eq(aWBTC.address); - expect(newFirstPosition.positionState).to.eq(0); // Default - expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); - expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); - - expect(newSecondPosition.component).to.eq(setup.weth.address); - expect(newSecondPosition.positionState).to.eq(0); // Default - expect(newSecondPosition.unit).to.eq(initialPositions[1].unit); // Should be unchanged - expect(newSecondPosition.module).to.eq(ADDRESS_ZERO); - }); - - it("should update the borrow position on the SetToken correctly", async () => { - - await subject(); - - const setTotalSupply = await setToken.totalSupply(); - const previousThirdPositionBalance = await variableDebtDAI.balanceOf(setToken.address); - const expectedThirdPositionUnit = preciseDiv(previousThirdPositionBalance, setTotalSupply).mul(-1); - - const previousFourthPositionBalance = await variableDebtUSDC.balanceOf(setToken.address); - const expectedFourthPositionUnit = preciseDiv(previousFourthPositionBalance, setTotalSupply).mul(-1); - - const newThirdPosition = (await setToken.getPositions())[2]; - const newFourthPosition = (await setToken.getPositions())[3]; - - expect(newThirdPosition.component).to.eq(setup.dai.address); - expect(newThirdPosition.positionState).to.eq(1); // External - expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); // Debt accrues - expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); - - expect(newFourthPosition.component).to.eq(setup.usdc.address); - expect(newFourthPosition.positionState).to.eq(1); // External - expect(newFourthPosition.unit).to.eq(expectedFourthPositionUnit); - expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); - }); - - it("should have the correct token balances", async () => { - const preMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); - const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); - const preMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); - const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); - const preMinterDaiBalance = await setup.dai.balanceOf(subjectCaller.address); - const preSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); - const preMinterWethBalance = await setup.weth.balanceOf(subjectCaller.address); - const preSetWethBalance = await setup.weth.balanceOf(subjectSetToken); - - const mintQuantity = preciseMul(subjectQuantity, ether(1).add(issueFee)); - const setTotalSupply = await setToken.totalSupply(); - const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; - const aWBTCFlows = preciseMulCeil(mintQuantity, newAWBTCPositionUnits); - - await subject(); - - const daiDebtPositionUnits = (await setToken.getPositions())[2].unit; - const usdcDebtPositionUnits = (await setToken.getPositions())[3].unit; - const daiFlows = preciseMul(mintQuantity, daiDebtPositionUnits).mul(-1); - const usdcFlows = preciseMul(mintQuantity, usdcDebtPositionUnits).mul(-1); - const wethFlows = preciseMul(ether(1), setTotalSupply); // 1 WETH unit - - const postMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); - const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); - const postMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); - const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); - const postMinterDaiBalance = await setup.dai.balanceOf(subjectCaller.address); - const postSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); - const postMinterWethBalance = await setup.weth.balanceOf(subjectCaller.address); - const postSetWethBalance = await setup.weth.balanceOf(subjectSetToken); - - expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); - expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); - expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.add(usdcFlows)); - expect(postSetUsdcDebtBalance).to.gte(preSetUsdcDebtBalance.add(usdcFlows)); // Round up due to interest accrual - expect(postMinterDaiBalance).to.eq(preMinterDaiBalance.add(daiFlows)); - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); // Round up due to interest accrual - expect(postMinterWethBalance).to.eq(preMinterWethBalance.sub(wethFlows)); - expect(postSetWethBalance).to.eq(preSetWethBalance.add(wethFlows)); + context("when default and external positions do overlap", async () => { + cacheBeforeEach(async () => { + setToken = await setup.createSetToken( + [aWBTC.address, setup.usdc.address], // USDC is both a default and external position + [ + bitcoin(1), + usdc(100), + ], + [aaveLeverageModule.address, debtIssuanceModule.address] + ); + issueFee = ether(0.005); + await debtIssuanceModule.initialize( + setToken.address, + ether(0.02), + issueFee, + ether(0.005), + feeRecipient.address, + ADDRESS_ZERO + ); + + // Add SetToken to allow list + await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); + await aaveLeverageModule.initialize( + setToken.address, + [setup.wbtc.address], + [setup.usdc.address, setup.dai.address] // USDC is both a default and external position + ); + + // Approve tokens to issuance module and call issue + await aWBTC.approve(debtIssuanceModule.address, MAX_UINT_256); + await setup.usdc.approve(debtIssuanceModule.address, MAX_UINT_256); + + // Issue 1 SetToken + issueQuantity = ether(1); + await debtIssuanceModule.issue(setToken.address, issueQuantity, owner.address); + + // Lever up + await aaveLeverageModule.lever( + setToken.address, + setup.usdc.address, + setup.wbtc.address, + usdc(100), + bitcoin(0.01), + "UniswapV2ExchangeAdapter", + EMPTY_BYTES + ); + + await aaveLeverageModule.lever( + setToken.address, + setup.dai.address, + setup.wbtc.address, + ether(100), + bitcoin(0.01), + "UniswapV2ExchangeAdapter", + EMPTY_BYTES + ); + }); + + beforeEach(() => { + subjectSetToken = setToken.address; + subjectQuantity = issueQuantity; + subjectTo = owner.address; + subjectCaller = owner; + }); + + it("should update the collateral position on the SetToken correctly", async () => { + const initialPositions = await setToken.getPositions(); + + await subject(); + + const currentPositions = await setToken.getPositions(); + const newFirstPosition = (await setToken.getPositions())[0]; + const newSecondPosition = (await setToken.getPositions())[1]; + + expect(initialPositions.length).to.eq(4); + expect(currentPositions.length).to.eq(4); + + expect(newFirstPosition.component).to.eq(aWBTC.address); + expect(newFirstPosition.positionState).to.eq(0); // Default + expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); + expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); + + expect(newSecondPosition.component).to.eq(setup.usdc.address); + expect(newSecondPosition.positionState).to.eq(0); // Default + expect(newSecondPosition.unit).to.eq(initialPositions[1].unit); // Should be unchanged + expect(newSecondPosition.module).to.eq(ADDRESS_ZERO); + }); + + it("should update the borrow position on the SetToken correctly", async () => { + + await subject(); + + const setTotalSupply = await setToken.totalSupply(); + const previousThirdPositionBalance = await variableDebtUSDC.balanceOf(setToken.address); + const expectedThirdPositionUnit = preciseDiv(previousThirdPositionBalance, setTotalSupply).mul(-1); + + const previousFourthPositionBalance = await variableDebtDAI.balanceOf(setToken.address); + const expectedFourthPositionUnit = preciseDiv(previousFourthPositionBalance, setTotalSupply).mul(-1); + + const newThirdPosition = (await setToken.getPositions())[2]; + const newFourthPosition = (await setToken.getPositions())[3]; + + expect(newThirdPosition.component).to.eq(setup.usdc.address); + expect(newThirdPosition.positionState).to.eq(1); // External + expect(newThirdPosition.unit).to.eq(expectedThirdPositionUnit); + expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); + + expect(newFourthPosition.component).to.eq(setup.dai.address); + expect(newFourthPosition.positionState).to.eq(1); // External + expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); // Debt accrues + expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); + }); + + it("should have the correct token balances", async () => { + const preMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); + const preMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); + const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); + const preMinterDaiBalance = await setup.dai.balanceOf(subjectCaller.address); + const preSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); + const preSetUsdcEquityBalance = await setup.usdc.balanceOf(subjectSetToken); + + const mintQuantity = preciseMul(subjectQuantity, ether(1).add(issueFee)); + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const usdcPositionUnits = (await setToken.getPositions())[1].unit; + const aWBTCFlows = preciseMulCeil(mintQuantity, newAWBTCPositionUnits); + const usdcEquityFlows = preciseMul(mintQuantity, usdcPositionUnits); + + await subject(); + + const usdcDebtPositionUnits = (await setToken.getPositions())[2].unit; + const daiDebtPositionUnits = (await setToken.getPositions())[3].unit; + const usdcDebtFlows = preciseMul(mintQuantity, usdcDebtPositionUnits).mul(-1); + const daiFlows = preciseMul(mintQuantity, daiDebtPositionUnits).mul(-1); + + const postMinterAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); + const postMinterUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); + const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); + const postMinterDaiBalance = await setup.dai.balanceOf(subjectCaller.address); + const postSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); + const postSetUsdcEquityBalance = await setup.usdc.balanceOf(subjectSetToken); + + expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); + expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.sub(usdcEquityFlows).add(usdcDebtFlows)); + expect(postSetUsdcEquityBalance).to.eq(preSetUsdcEquityBalance.add(usdcEquityFlows)); + expect(postSetUsdcDebtBalance).to.gte(preSetUsdcDebtBalance.add(usdcDebtFlows)); // Round up due to interest accrual + expect(postMinterDaiBalance).to.eq(preMinterDaiBalance.add(daiFlows)); + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); // Round up due to interest accrual + }); }); }); }); @@ -745,7 +894,6 @@ describe("AaveUniswapLeverageDebtIssuance", () => { ); } - context("when a default aToken position and redeem will take supply to 0", async () => { cacheBeforeEach(async () => { aWBTCUnits = bitcoin(1); From 0addf1047294979c7f1fa0723f9d02de617bfb00 Mon Sep 17 00:00:00 2001 From: alpha-guy Date: Thu, 2 Sep 2021 19:53:38 +0530 Subject: [PATCH 3/7] Add test cases for multiple default and external positions during redemption * when default and external positions do not overlap --- .../aaveUniswapLeverageDebtIssuance.spec.ts | 171 +++++++++++++++++- 1 file changed, 168 insertions(+), 3 deletions(-) diff --git a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts index 365fd8eeb..9d704c4f5 100644 --- a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts +++ b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts @@ -687,7 +687,6 @@ describe("AaveUniswapLeverageDebtIssuance", () => { const preSetWethBalance = await setup.weth.balanceOf(subjectSetToken); const mintQuantity = preciseMul(subjectQuantity, ether(1).add(issueFee)); - // const setTotalSupply = await setToken.totalSupply(); const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; const wethPositionUnits = (await setToken.getPositions())[1].unit; const aWBTCFlows = preciseMulCeil(mintQuantity, newAWBTCPositionUnits); @@ -919,7 +918,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { ); // Approve tokens to issuance module and call issue - await aWBTC.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -1011,7 +1010,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { ); // Approve tokens to issuance module and call issue - await aWBTC.approve(debtIssuanceModule.address, ether(1000)); + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); // Issue 1 SetToken issueQuantity = ether(1); @@ -1363,5 +1362,171 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); }); }); + + context("when 2 default positions and 2 external borrow positions", async () => { + context("when default and external positions do not overlap", async () => { + let issueQuantity: BigNumber; + + cacheBeforeEach(async () => { + + aWBTCUnits = bitcoin(1); + setToken = await setup.createSetToken( + [aWBTC.address, setup.weth.address], + [aWBTCUnits, ether(1)], + [aaveLeverageModule.address, debtIssuanceModule.address] + ); + redeemFee = ether(0.005); + await debtIssuanceModule.initialize( + setToken.address, + ether(0.02), + ether(0.005), + redeemFee, + feeRecipient.address, + ADDRESS_ZERO + ); + // Add SetToken to allow list + await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); + await aaveLeverageModule.initialize( + setToken.address, + [setup.wbtc.address], + [setup.usdc.address, setup.dai.address] + ); + + // Approve tokens to issuance module and call issue + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); + await setup.weth.approve(debtIssuanceModule.address, ether(1000)); + + // Issue 1 SetToken + issueQuantity = ether(1); + await debtIssuanceModule.issue(setToken.address, issueQuantity, owner.address); + + // Lever up + await aaveLeverageModule.lever( + setToken.address, + setup.usdc.address, + setup.wbtc.address, + usdc(500), + bitcoin(0.4), + "UniswapV2ExchangeAdapter", + EMPTY_BYTES + ); + + await aaveLeverageModule.lever( + setToken.address, + setup.dai.address, + setup.wbtc.address, + ether(100), + bitcoin(0.01), + "UniswapV2ExchangeAdapter", + EMPTY_BYTES + ); + + // Approve debt token to issuance module for redeem + await setup.usdc.approve(debtIssuanceModule.address, MAX_UINT_256); + await setup.dai.approve(debtIssuanceModule.address, MAX_UINT_256); + }); + + beforeEach(() => { + subjectSetToken = setToken.address; + subjectQuantity = issueQuantity; // Redeem 1 SetToken so fee supply is left + subjectTo = owner.address; + subjectCaller = owner; + }); + + it("should update the collateral position on the SetToken correctly", async () => { + const initialPositions = await setToken.getPositions(); + + await subject(); + + const currentPositions = await setToken.getPositions(); + const newFirstPosition = (await setToken.getPositions())[0]; + const newSecondPosition = (await setToken.getPositions())[1]; + + expect(initialPositions.length).to.eq(4); + expect(currentPositions.length).to.eq(4); + + expect(newFirstPosition.component).to.eq(aWBTC.address); + expect(newFirstPosition.positionState).to.eq(0); // Default + expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); + expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); + + expect(newSecondPosition.component).to.eq(setup.weth.address); + expect(newSecondPosition.positionState).to.eq(0); // Default + expect(newSecondPosition.unit).to.eq(initialPositions[1].unit); // Should be unchanged + expect(newSecondPosition.module).to.eq(ADDRESS_ZERO); + }); + + it("should update the borrow position on the SetToken correctly", async () => { + const initialPositions = await setToken.getPositions(); + const setTotalSupply = await setToken.totalSupply(); + const previousThirdPositionBalance = await variableDebtUSDC.balanceOf(setToken.address); + const previousFourthPositionBalance = await variableDebtDAI.balanceOf(setToken.address); + + await subject(); + + const expectedThirdPositionUnit = preciseDivCeil(previousThirdPositionBalance, setTotalSupply).mul(-1); + const expectedFourthPositionUnit = preciseDivCeil(previousFourthPositionBalance, setTotalSupply).mul(-1); + + const currentPositions = await setToken.getPositions(); + const newThirdPosition = (await setToken.getPositions())[2]; + const newFourthPosition = (await setToken.getPositions())[3]; + + expect(initialPositions.length).to.eq(4); + expect(currentPositions.length).to.eq(4); + + expect(newThirdPosition.component).to.eq(setup.usdc.address); + expect(newThirdPosition.positionState).to.eq(1); // External + expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); // Debt accrues + expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); + + expect(newFourthPosition.component).to.eq(setup.dai.address); + expect(newFourthPosition.positionState).to.eq(1); // External + expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); // Debt accrues + expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); + }); + + it("should have the correct token balances", async () => { + const preRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); + const preRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); + const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); + const preRedeemerDaiBalance = await setup.dai.balanceOf(subjectCaller.address); + const preSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); + const preRedeemerWethBalance = await setup.weth.balanceOf(subjectCaller.address); + const preSetWethBalance = await setup.weth.balanceOf(subjectSetToken); + + const redeemQuantity = preciseMul(subjectQuantity, ether(1).sub(redeemFee)); + + await subject(); + + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const wethPositionUnits = (await setToken.getPositions())[1].unit; + const usdcDebtPositionUnits = (await setToken.getPositions())[2].unit; + const daiDebtPositionUnits = (await setToken.getPositions())[3].unit; + const aWBTCFlows = preciseMul(redeemQuantity, newAWBTCPositionUnits); + const wethFlows = preciseMul(redeemQuantity, wethPositionUnits); + const usdcFlows = preciseMulCeil(redeemQuantity, usdcDebtPositionUnits.mul(-1)); + const daiFlows = preciseMulCeil(redeemQuantity, daiDebtPositionUnits.mul(-1)); + + const postRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); + const postRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); + const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); + const postRedeemerWethBalance = await setup.weth.balanceOf(subjectCaller.address); + const postSetWethBalance = await setup.weth.balanceOf(subjectSetToken); + const postRedeemerDaiBalance = await setup.dai.balanceOf(subjectCaller.address); + const postSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); + + expect(postRedeemerAWBTCBalance).to.eq(preRedeemerAWBTCBalance.add(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.sub(aWBTCFlows)); + expect(postRedeemerUsdcBalance).to.eq(preRedeemerUsdcBalance.sub(usdcFlows)); + expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); + expect(postRedeemerWethBalance).to.eq(preRedeemerWethBalance.add(wethFlows)); + expect(postSetWethBalance).to.eq(preSetWethBalance.sub(wethFlows)); + expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiFlows)); + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiFlows)); // Debt accrues (18 decimal places so debt accrues more precisely) + }); + }); + }); }); }); \ No newline at end of file From 16a83dccbe7f74d4db6a3819820edbc9c8a8c3e0 Mon Sep 17 00:00:00 2001 From: alpha-guy Date: Thu, 2 Sep 2021 20:01:12 +0530 Subject: [PATCH 4/7] Add tests for redeem() when default and external positions do overlap --- .../aaveUniswapLeverageDebtIssuance.spec.ts | 162 +++++++++++++++++- 1 file changed, 161 insertions(+), 1 deletion(-) diff --git a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts index 9d704c4f5..204bbdfda 100644 --- a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts +++ b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts @@ -1524,7 +1524,167 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postRedeemerWethBalance).to.eq(preRedeemerWethBalance.add(wethFlows)); expect(postSetWethBalance).to.eq(preSetWethBalance.sub(wethFlows)); expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiFlows)); - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiFlows)); // Debt accrues (18 decimal places so debt accrues more precisely) + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiFlows)); // Debt accrues + }); + }); + context("when default and external positions do overlap", async () => { + let issueQuantity: BigNumber; + + cacheBeforeEach(async () => { + + aWBTCUnits = bitcoin(1); + setToken = await setup.createSetToken( + [aWBTC.address, setup.usdc.address], // USDC is both default and external position + [aWBTCUnits, usdc(100)], + [aaveLeverageModule.address, debtIssuanceModule.address] + ); + redeemFee = ether(0.005); + await debtIssuanceModule.initialize( + setToken.address, + ether(0.02), + ether(0.005), + redeemFee, + feeRecipient.address, + ADDRESS_ZERO + ); + // Add SetToken to allow list + await aaveLeverageModule.updateAllowedSetToken(setToken.address, true); + await aaveLeverageModule.initialize( + setToken.address, + [setup.wbtc.address], + [setup.usdc.address, setup.dai.address] + ); + + // Approve tokens to issuance module and call issue + await aWBTC.approve(debtIssuanceModule.address, bitcoin(1000)); + await setup.usdc.approve(debtIssuanceModule.address, usdc(1000)); + + // Issue 1 SetToken + issueQuantity = ether(1); + await debtIssuanceModule.issue(setToken.address, issueQuantity, owner.address); + + // Lever up + await aaveLeverageModule.lever( + setToken.address, + setup.usdc.address, + setup.wbtc.address, + usdc(500), + bitcoin(0.4), + "UniswapV2ExchangeAdapter", + EMPTY_BYTES + ); + + await aaveLeverageModule.lever( + setToken.address, + setup.dai.address, + setup.wbtc.address, + ether(100), + bitcoin(0.01), + "UniswapV2ExchangeAdapter", + EMPTY_BYTES + ); + + // Approve debt token to issuance module for redeem + await setup.usdc.approve(debtIssuanceModule.address, MAX_UINT_256); + await setup.dai.approve(debtIssuanceModule.address, MAX_UINT_256); + }); + + beforeEach(() => { + subjectSetToken = setToken.address; + subjectQuantity = issueQuantity; // Redeem 1 SetToken so fee supply is left + subjectTo = owner.address; + subjectCaller = owner; + }); + + it("should update the collateral position on the SetToken correctly", async () => { + const initialPositions = await setToken.getPositions(); + + await subject(); + + const currentPositions = await setToken.getPositions(); + const newFirstPosition = (await setToken.getPositions())[0]; + const newSecondPosition = (await setToken.getPositions())[1]; + + expect(initialPositions.length).to.eq(4); + expect(currentPositions.length).to.eq(4); + + expect(newFirstPosition.component).to.eq(aWBTC.address); + expect(newFirstPosition.positionState).to.eq(0); // Default + expect(newFirstPosition.unit).to.eq(initialPositions[0].unit); + expect(newFirstPosition.module).to.eq(ADDRESS_ZERO); + + expect(newSecondPosition.component).to.eq(setup.usdc.address); + expect(newSecondPosition.positionState).to.eq(0); // Default + expect(newSecondPosition.unit).to.eq(initialPositions[1].unit); // Should be unchanged + expect(newSecondPosition.module).to.eq(ADDRESS_ZERO); + }); + + it("should update the borrow position on the SetToken correctly", async () => { + const initialPositions = await setToken.getPositions(); + const setTotalSupply = await setToken.totalSupply(); + const previousThirdPositionBalance = await variableDebtUSDC.balanceOf(setToken.address); + const previousFourthPositionBalance = await variableDebtDAI.balanceOf(setToken.address); + + await subject(); + + const expectedThirdPositionUnit = preciseDivCeil(previousThirdPositionBalance, setTotalSupply).mul(-1); + const expectedFourthPositionUnit = preciseDivCeil(previousFourthPositionBalance, setTotalSupply).mul(-1); + + const currentPositions = await setToken.getPositions(); + const newThirdPosition = (await setToken.getPositions())[2]; + const newFourthPosition = (await setToken.getPositions())[3]; + + expect(initialPositions.length).to.eq(4); + expect(currentPositions.length).to.eq(4); + + expect(newThirdPosition.component).to.eq(setup.usdc.address); + expect(newThirdPosition.positionState).to.eq(1); // External + expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); // Debt accrues + expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); + + expect(newFourthPosition.component).to.eq(setup.dai.address); + expect(newFourthPosition.positionState).to.eq(1); // External + expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); // Debt accrues + expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); + }); + + it("should have the correct token balances", async () => { + const preRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const preSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); + const preRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); + const preSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); + const preSetUsdcEquityBalance = await setup.usdc.balanceOf(subjectSetToken); + const preRedeemerDaiBalance = await setup.dai.balanceOf(subjectCaller.address); + const preSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); + + const redeemQuantity = preciseMul(subjectQuantity, ether(1).sub(redeemFee)); + + await subject(); + + const newAWBTCPositionUnits = (await setToken.getPositions())[0].unit; + const usdcEquityPositionUnits = (await setToken.getPositions())[1].unit; + const usdcDebtPositionUnits = (await setToken.getPositions())[2].unit; + const daiDebtPositionUnits = (await setToken.getPositions())[3].unit; + const aWBTCFlows = preciseMul(redeemQuantity, newAWBTCPositionUnits); + const usdcEquityFlows = preciseMul(redeemQuantity, usdcEquityPositionUnits); + const usdcDebtFlows = preciseMulCeil(redeemQuantity, usdcDebtPositionUnits.mul(-1)); + const daiDebtFlows = preciseMulCeil(redeemQuantity, daiDebtPositionUnits.mul(-1)); + + const postRedeemerAWBTCBalance = await aWBTC.balanceOf(subjectCaller.address); + const postSetAWBTCBalance = await aWBTC.balanceOf(subjectSetToken); + const postRedeemerUsdcBalance = await setup.usdc.balanceOf(subjectCaller.address); + const postSetUsdcDebtBalance = await variableDebtUSDC.balanceOf(subjectSetToken); + const postSetUsdcEquityBalance = await setup.usdc.balanceOf(subjectSetToken); + const postRedeemerDaiBalance = await setup.dai.balanceOf(subjectCaller.address); + const postSetDaiDebtBalance = await variableDebtDAI.balanceOf(subjectSetToken); + + expect(postRedeemerAWBTCBalance).to.eq(preRedeemerAWBTCBalance.add(aWBTCFlows)); + expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.sub(aWBTCFlows)); + expect(postRedeemerUsdcBalance).to.eq(preRedeemerUsdcBalance.sub(usdcDebtFlows).add(usdcEquityFlows)); + expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcDebtFlows)); + expect(postSetUsdcEquityBalance).to.eq(preSetUsdcEquityBalance.sub(usdcEquityFlows)); + expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiDebtFlows)); + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiDebtFlows)); // Debt accrues }); }); }); From f8bf9ca82ca98074dcb8e058b6a43df4c731b098 Mon Sep 17 00:00:00 2001 From: alpha-guy Date: Fri, 3 Sep 2021 01:58:20 +0530 Subject: [PATCH 5/7] Refactor to stricter tests * Add an explanation for why we used WBTC and USDC * Explain why use greater than equal to, whenever it is used --- .../aaveUniswapLeverageDebtIssuance.spec.ts | 70 +++++++++++++------ 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts index 204bbdfda..0b4a37dba 100644 --- a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts +++ b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts @@ -37,6 +37,15 @@ import { ADDRESS_ZERO, ZERO, EMPTY_BYTES, MAX_UINT_256 } from "@utils/constants" const expect = getWaffleExpect(); +// Due to low utilization rate of the reserves and small principal amounts, the interest rate accrued each block is very small. +// Hence, accrued interest doesn't reflect immediately in tokens which have less precision, i.e. less number of decimal places. +// Eg. if interest accrued each block for an asset is 100 wei (1 wei = 10^(-18)), then USDC (6 decimal places) and +// WBTC (8 decimal places) would not register this increase until a couple of blocks, whereas DAI (18 decimal places) and +// WETH (18 decimal places) balances would register this increase in the immediate next block. Thus, we use WBTC and USDC as much +// as possible to avoid having to write the complex interest accrual logic of Aave to determine the interest rate accrued each block. +// For WETH and DAI, we use "greater than or equal to" to account for the small amount of interest accrued. + + describe("AaveUniswapLeverageDebtIssuance", () => { let owner: Account; let feeRecipient: Account; @@ -518,21 +527,25 @@ describe("AaveUniswapLeverageDebtIssuance", () => { it("should update the borrow position on the SetToken correctly", async () => { const initialPositions = await setToken.getPositions(); - // Get debt balance after some debt is paid during liquidation + // Get debt balance after some amount of debt is paid during liquidation const previousSecondPositionBalance = await variableDebtUSDC.balanceOf(setToken.address); const setTotalSupply = await setToken.totalSupply(); + const expectedSecondPositionUnit = preciseDiv(previousSecondPositionBalance, setTotalSupply).mul(-1); await subject(); const currentPositions = await setToken.getPositions(); const newSecondPosition = (await setToken.getPositions())[1]; - const expectedSecondPositionUnit = preciseDiv(previousSecondPositionBalance, setTotalSupply).mul(-1); expect(initialPositions.length).to.eq(2); expect(currentPositions.length).to.eq(2); expect(newSecondPosition.component).to.eq(setup.usdc.address); expect(newSecondPosition.positionState).to.eq(1); // External - expect(newSecondPosition.unit.abs()).to.gte(expectedSecondPositionUnit.abs()); // Debt accrues + // We have used previousSecondPositionBalance to calculate the expectedSecondPositionUnit + // but some debt accrued in the block in which we issued the Set. + // The accrued interest was synced to the SetToken position units during issuance which leads to the absolute + // value of newSecondPosition.unit to be greater than expectedSecondPositionUnit by a very small amount + expect(newSecondPosition.unit.abs()).to.gte(expectedSecondPositionUnit.abs()); expect(newSecondPosition.module).to.eq(aaveLeverageModule.address); }); @@ -653,8 +666,6 @@ describe("AaveUniswapLeverageDebtIssuance", () => { it("should update the borrow position on the SetToken correctly", async () => { - await subject(); - const setTotalSupply = await setToken.totalSupply(); const previousThirdPositionBalance = await variableDebtDAI.balanceOf(setToken.address); const expectedThirdPositionUnit = preciseDiv(previousThirdPositionBalance, setTotalSupply).mul(-1); @@ -662,18 +673,21 @@ describe("AaveUniswapLeverageDebtIssuance", () => { const previousFourthPositionBalance = await variableDebtUSDC.balanceOf(setToken.address); const expectedFourthPositionUnit = preciseDiv(previousFourthPositionBalance, setTotalSupply).mul(-1); + await subject(); + const newThirdPosition = (await setToken.getPositions())[2]; const newFourthPosition = (await setToken.getPositions())[3]; expect(newThirdPosition.component).to.eq(setup.dai.address); expect(newThirdPosition.positionState).to.eq(1); // External - expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); // Debt accrues expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); expect(newFourthPosition.component).to.eq(setup.usdc.address); expect(newFourthPosition.positionState).to.eq(1); // External - expect(newFourthPosition.unit).to.eq(expectedFourthPositionUnit); expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); + expect(newFourthPosition.unit).to.eq(expectedFourthPositionUnit); // Debt accrues but by an insignificant amount }); it("should have the correct token balances", async () => { @@ -711,11 +725,13 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.add(usdcFlows)); - expect(postSetUsdcDebtBalance).to.gte(preSetUsdcDebtBalance.add(usdcFlows)); // Round up due to interest accrual expect(postMinterDaiBalance).to.eq(preMinterDaiBalance.add(daiFlows)); - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); // Round up due to interest accrual expect(postMinterWethBalance).to.eq(preMinterWethBalance.sub(wethFlows)); expect(postSetWethBalance).to.eq(preSetWethBalance.add(wethFlows)); + // USDC has 6 decimal places. Interest accrued doesn't reflect in balance immediately. + expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcFlows)); + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); }); }); context("when default and external positions do overlap", async () => { @@ -822,12 +838,13 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newThirdPosition.component).to.eq(setup.usdc.address); expect(newThirdPosition.positionState).to.eq(1); // External - expect(newThirdPosition.unit).to.eq(expectedThirdPositionUnit); + expect(newThirdPosition.unit).to.eq(expectedThirdPositionUnit); // Debt accrues but doesn't reflect immediately expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External - expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); // Debt accrues + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); }); @@ -863,11 +880,13 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); - expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.sub(usdcEquityFlows).add(usdcDebtFlows)); expect(postSetUsdcEquityBalance).to.eq(preSetUsdcEquityBalance.add(usdcEquityFlows)); - expect(postSetUsdcDebtBalance).to.gte(preSetUsdcDebtBalance.add(usdcDebtFlows)); // Round up due to interest accrual + expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcDebtFlows)); expect(postMinterDaiBalance).to.eq(preMinterDaiBalance.add(daiFlows)); - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); // Round up due to interest accrual + // Debt accrues but doesn't reflect immediately + expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.sub(usdcEquityFlows).add(usdcDebtFlows)); + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); }); }); }); @@ -1332,7 +1351,8 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(currentPositions.length).to.eq(2); expect(newSecondPosition.component).to.eq(setup.usdc.address); expect(newSecondPosition.positionState).to.eq(1); // External - expect(newSecondPositionNotional).to.gte(previousSecondPositionBalance); // Debt accrues + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(newSecondPositionNotional).to.gte(previousSecondPositionBalance); expect(newSecondPosition.module).to.eq(aaveLeverageModule.address); }); @@ -1476,13 +1496,14 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newThirdPosition.component).to.eq(setup.usdc.address); expect(newThirdPosition.positionState).to.eq(1); // External - expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); // Debt accrues expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); + expect(newThirdPosition.unit.abs()).to.eq(expectedThirdPositionUnit.abs()); // Debt accrues but doesn't reflect immediately expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External - expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); // Debt accrues expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); }); it("should have the correct token balances", async () => { @@ -1520,11 +1541,12 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postRedeemerAWBTCBalance).to.eq(preRedeemerAWBTCBalance.add(aWBTCFlows)); expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.sub(aWBTCFlows)); expect(postRedeemerUsdcBalance).to.eq(preRedeemerUsdcBalance.sub(usdcFlows)); - expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); expect(postRedeemerWethBalance).to.eq(preRedeemerWethBalance.add(wethFlows)); expect(postSetWethBalance).to.eq(preSetWethBalance.sub(wethFlows)); expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiFlows)); - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiFlows)); // Debt accrues + expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); // Debt accrues but doesn't reflect immediately + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiFlows)); }); }); context("when default and external positions do overlap", async () => { @@ -1639,13 +1661,14 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newThirdPosition.component).to.eq(setup.usdc.address); expect(newThirdPosition.positionState).to.eq(1); // External - expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); // Debt accrues + expect(newThirdPosition.unit.abs()).to.eq(expectedThirdPositionUnit.abs()); // Debt accrues but doesn't reflect immediately expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External - expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); // Debt accrues expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); }); it("should have the correct token balances", async () => { @@ -1681,10 +1704,11 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postRedeemerAWBTCBalance).to.eq(preRedeemerAWBTCBalance.add(aWBTCFlows)); expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.sub(aWBTCFlows)); expect(postRedeemerUsdcBalance).to.eq(preRedeemerUsdcBalance.sub(usdcDebtFlows).add(usdcEquityFlows)); - expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcDebtFlows)); + expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcDebtFlows)); // Debt accrues but doesn't reflect immediately expect(postSetUsdcEquityBalance).to.eq(preSetUsdcEquityBalance.sub(usdcEquityFlows)); expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiDebtFlows)); - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiDebtFlows)); // Debt accrues + // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiDebtFlows)); }); }); }); From 4bf2b872aa4fb8fa4045b464ea663ed7fb7ec3fe Mon Sep 17 00:00:00 2001 From: alpha-guy Date: Fri, 3 Sep 2021 02:14:59 +0530 Subject: [PATCH 6/7] Improve comments --- .../aaveUniswapLeverageDebtIssuance.spec.ts | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts index 0b4a37dba..10898f205 100644 --- a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts +++ b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts @@ -43,7 +43,7 @@ const expect = getWaffleExpect(); // WBTC (8 decimal places) would not register this increase until a couple of blocks, whereas DAI (18 decimal places) and // WETH (18 decimal places) balances would register this increase in the immediate next block. Thus, we use WBTC and USDC as much // as possible to avoid having to write the complex interest accrual logic of Aave to determine the interest rate accrued each block. -// For WETH and DAI, we use "greater than or equal to" to account for the small amount of interest accrued. +// For WETH and DAI, we use the looser comparison, that is "greater than or equal to" to account for the small amount of interest accrued. describe("AaveUniswapLeverageDebtIssuance", () => { @@ -681,7 +681,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newThirdPosition.component).to.eq(setup.dai.address); expect(newThirdPosition.positionState).to.eq(1); // External expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); expect(newFourthPosition.component).to.eq(setup.usdc.address); @@ -730,7 +730,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postSetWethBalance).to.eq(preSetWethBalance.add(wethFlows)); // USDC has 6 decimal places. Interest accrued doesn't reflect in balance immediately. expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcFlows)); - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); }); }); @@ -843,7 +843,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); }); @@ -881,11 +881,10 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postMinterAWBTCBalance).to.eq(preMinterAWBTCBalance.sub(aWBTCFlows)); expect(postSetAWBTCBalance).to.eq(preSetAWBTCBalance.add(aWBTCFlows)); expect(postSetUsdcEquityBalance).to.eq(preSetUsdcEquityBalance.add(usdcEquityFlows)); - expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcDebtFlows)); expect(postMinterDaiBalance).to.eq(preMinterDaiBalance.add(daiFlows)); - // Debt accrues but doesn't reflect immediately expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.sub(usdcEquityFlows).add(usdcDebtFlows)); - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcDebtFlows)); // Debt accrues but doesn't reflect immediately + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); }); }); @@ -1351,7 +1350,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(currentPositions.length).to.eq(2); expect(newSecondPosition.component).to.eq(setup.usdc.address); expect(newSecondPosition.positionState).to.eq(1); // External - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(newSecondPositionNotional).to.gte(previousSecondPositionBalance); expect(newSecondPosition.module).to.eq(aaveLeverageModule.address); }); @@ -1502,7 +1501,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); }); @@ -1545,7 +1544,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postSetWethBalance).to.eq(preSetWethBalance.sub(wethFlows)); expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiFlows)); expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); // Debt accrues but doesn't reflect immediately - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiFlows)); }); }); @@ -1667,7 +1666,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); }); @@ -1707,7 +1706,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcDebtFlows)); // Debt accrues but doesn't reflect immediately expect(postSetUsdcEquityBalance).to.eq(preSetUsdcEquityBalance.sub(usdcEquityFlows)); expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiDebtFlows)); - // DAI has 18 decimal places. Thus we need to use greater than or equal to here to account for the very small amount of interest accrued. + // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiDebtFlows)); }); }); From 38c4079404fee427bfd554c5954daffae903fa6e Mon Sep 17 00:00:00 2001 From: alpha-guy Date: Fri, 3 Sep 2021 14:44:31 +0530 Subject: [PATCH 7/7] Add upper bound on greater than or equal to comparisons * Being more explicit with the checks --- .../aaveUniswapLeverageDebtIssuance.spec.ts | 55 ++++++++++++------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts index 10898f205..6aad85a74 100644 --- a/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts +++ b/test/integration/aaveUniswapLeverageDebtIssuance.spec.ts @@ -43,8 +43,19 @@ const expect = getWaffleExpect(); // WBTC (8 decimal places) would not register this increase until a couple of blocks, whereas DAI (18 decimal places) and // WETH (18 decimal places) balances would register this increase in the immediate next block. Thus, we use WBTC and USDC as much // as possible to avoid having to write the complex interest accrual logic of Aave to determine the interest rate accrued each block. -// For WETH and DAI, we use the looser comparison, that is "greater than or equal to" to account for the small amount of interest accrued. - +// For WETH and DAI, we use looser comparison checks implemented in the `expectGreaterThanOrEqualToWithUpperBound` helper function +// to account for the small amount of interest accrued. + +/** + * Checks for |val2| <= |val1| < |val2| * 1.000001 + * @param val1 Value 1 with accrued interest + * @param val2 Value 2 + */ +async function expectGreaterThanOrEqualToWithUpperBound(val1: BigNumber, val2: BigNumber): Promise { + const oneMil = BigNumber.from(10).pow(6); + await expect(val1.abs()).to.be.gte(val2.abs()); // |val1| >= |val2| + await expect(val1.abs()).to.be.lt(val2.abs().mul(oneMil.add(1)).div(oneMil)); // |val1| < |val2| * 1.000001 +} describe("AaveUniswapLeverageDebtIssuance", () => { let owner: Account; @@ -545,7 +556,7 @@ describe("AaveUniswapLeverageDebtIssuance", () => { // but some debt accrued in the block in which we issued the Set. // The accrued interest was synced to the SetToken position units during issuance which leads to the absolute // value of newSecondPosition.unit to be greater than expectedSecondPositionUnit by a very small amount - expect(newSecondPosition.unit.abs()).to.gte(expectedSecondPositionUnit.abs()); + await expectGreaterThanOrEqualToWithUpperBound(newSecondPosition.unit, expectedSecondPositionUnit); expect(newSecondPosition.module).to.eq(aaveLeverageModule.address); }); @@ -681,8 +692,8 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newThirdPosition.component).to.eq(setup.dai.address); expect(newThirdPosition.positionState).to.eq(1); // External expect(newThirdPosition.module).to.eq(aaveLeverageModule.address); - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(newThirdPosition.unit.abs()).to.gte(expectedThirdPositionUnit.abs()); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(newThirdPosition.unit, expectedThirdPositionUnit); expect(newFourthPosition.component).to.eq(setup.usdc.address); expect(newFourthPosition.positionState).to.eq(1); // External @@ -730,10 +741,11 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postSetWethBalance).to.eq(preSetWethBalance.add(wethFlows)); // USDC has 6 decimal places. Interest accrued doesn't reflect in balance immediately. expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcFlows)); - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(postSetDaiDebtBalance, preSetDaiDebtBalance.add(daiFlows)); }); }); + context("when default and external positions do overlap", async () => { cacheBeforeEach(async () => { setToken = await setup.createSetToken( @@ -843,9 +855,9 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(newFourthPosition.unit, expectedFourthPositionUnit); }); it("should have the correct token balances", async () => { @@ -884,8 +896,8 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postMinterDaiBalance).to.eq(preMinterDaiBalance.add(daiFlows)); expect(postMinterUsdcBalance).to.eq(preMinterUsdcBalance.sub(usdcEquityFlows).add(usdcDebtFlows)); expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.add(usdcDebtFlows)); // Debt accrues but doesn't reflect immediately - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.add(daiFlows)); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(postSetDaiDebtBalance, preSetDaiDebtBalance.add(daiFlows)); }); }); }); @@ -1350,9 +1362,9 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(currentPositions.length).to.eq(2); expect(newSecondPosition.component).to.eq(setup.usdc.address); expect(newSecondPosition.positionState).to.eq(1); // External - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(newSecondPositionNotional).to.gte(previousSecondPositionBalance); expect(newSecondPosition.module).to.eq(aaveLeverageModule.address); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(newSecondPositionNotional, previousSecondPositionBalance); }); it("should have the correct token balances", async () => { @@ -1501,8 +1513,8 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(newFourthPosition.unit, expectedFourthPositionUnit); }); it("should have the correct token balances", async () => { @@ -1544,10 +1556,11 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postSetWethBalance).to.eq(preSetWethBalance.sub(wethFlows)); expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiFlows)); expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcFlows)); // Debt accrues but doesn't reflect immediately - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiFlows)); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(postSetDaiDebtBalance, preSetDaiDebtBalance.sub(daiFlows)); }); }); + context("when default and external positions do overlap", async () => { let issueQuantity: BigNumber; @@ -1666,8 +1679,8 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(newFourthPosition.component).to.eq(setup.dai.address); expect(newFourthPosition.positionState).to.eq(1); // External expect(newFourthPosition.module).to.eq(aaveLeverageModule.address); - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(newFourthPosition.unit.abs()).to.gte(expectedFourthPositionUnit.abs()); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(newFourthPosition.unit, expectedFourthPositionUnit); }); it("should have the correct token balances", async () => { @@ -1706,8 +1719,8 @@ describe("AaveUniswapLeverageDebtIssuance", () => { expect(postSetUsdcDebtBalance).to.eq(preSetUsdcDebtBalance.sub(usdcDebtFlows)); // Debt accrues but doesn't reflect immediately expect(postSetUsdcEquityBalance).to.eq(preSetUsdcEquityBalance.sub(usdcEquityFlows)); expect(postRedeemerDaiBalance).to.eq(preRedeemerDaiBalance.sub(daiDebtFlows)); - // DAI has 18 decimal places. Thus we need to use "greater than or equal to" to account for the very small amount of interest accrued. - expect(postSetDaiDebtBalance).to.gte(preSetDaiDebtBalance.sub(daiDebtFlows)); + // DAI has 18 decimal places. Use `expectGreaterThanOrEqualToWithUpperBound()` to account for the very small amount of interest accrued. + await expectGreaterThanOrEqualToWithUpperBound(postSetDaiDebtBalance, preSetDaiDebtBalance.sub(daiDebtFlows)); }); }); });