import "module-alias/register" ; import { ContractTransaction } from "ethers" ; import { Address } from "@utils/types" ; import { Account } from "@utils/test/types" ; import { AaveV2 , AaveLeverageModule , DebtIssuanceModuleV2 , SetToken , UniswapV2ExchangeAdapter , } from "@utils/contracts" ; import { AaveV2AToken , AaveV2VariableDebtToken } from "@utils/contracts/aaveV2" ; import DeployHelper from "@utils/deploys" ; import { bitcoin , ether , usdc , preciseDiv , preciseMul , preciseMulCeil , preciseDivCeil } from "@utils/index" ; import { cacheBeforeEach , getAccounts , getWaffleExpect , getSystemFixture , getAaveV2Fixture , getUniswapFixture } from "@utils/test/index" ; import { AaveV2Fixture , SystemFixture , UniswapFixture } from "@utils/fixtures" ; import { BigNumber } from "ethers" ; import { ADDRESS_ZERO , ZERO , EMPTY_BYTES , MAX_UINT_256 } from "@utils/constants" ; const expect = getWaffleExpect ( ) ; param val1 param val2 async function expectGreaterThanOrEqualToWithUpperBound ( val1 , val2 ) { const oneMil = BigNumber . from ( 10 ) . pow ( 6 ) ; await expect ( val1 . abs ( ) ) . to . be . gte ( val2 . abs ( ) ) ; await expect ( val1 . abs ( ) ) . to . be . lt ( val2 . abs ( ) . mul ( oneMil . add ( 1 ) ) . div ( oneMil ) ) ; } describe ( "AaveUniswapLeverageDebtIssuance" , ( ) => { let owner ; let feeRecipient ; let deployer ; let setup ; let aaveV2Setup ; let uniswapSetup ; let aaveV2Library ; let aaveLeverageModule ; let debtIssuanceModule ; let uniswapExchangeAdapter ; let aWBTC ; let variableDebtDAI ; let variableDebtUSDC ; cacheBeforeEach ( async ( ) => { [ owner , feeRecipient , ] = await getAccounts ( ) ; deployer = new DeployHelper ( owner . wallet ) ; setup = getSystemFixture ( owner . address ) ; await setup . initialize ( ) ; uniswapSetup = getUniswapFixture ( owner . address ) ; await uniswapSetup . initialize ( owner , setup . weth . address , setup . wbtc . address , setup . dai . address ) ; aaveV2Setup = getAaveV2Fixture ( owner . address ) ; await aaveV2Setup . initialize ( setup . weth . address , setup . dai . address ) ; const wbtcReserveTokens = await aaveV2Setup . createAndEnableReserve ( setup . wbtc . address , "WBTC" , BigNumber . from ( 8 ) , BigNumber . from ( 8000 ) , BigNumber . from ( 8250 ) , BigNumber . from ( 10500 ) , BigNumber . from ( 1000 ) , true , true ) ; const usdcReserveTokens = await aaveV2Setup . createAndEnableReserve ( setup . usdc . address , "USDC" , BigNumber . from ( 6 ) , BigNumber . from ( 8000 ) , BigNumber . from ( 8250 ) , BigNumber . from ( 10500 ) , BigNumber . from ( 1000 ) , true , true ) ; await aaveV2Setup . setAssetPriceInOracle ( setup . wbtc . address , ether ( 1 ) ) ; await aaveV2Setup . setAssetPriceInOracle ( setup . usdc . address , ether ( 0.001 ) ) ; await setup . wbtc . approve ( aaveV2Setup . lendingPool . address , bitcoin ( 1000 ) ) ; await aaveV2Setup . lendingPool . deposit ( setup . wbtc . address , bitcoin ( 1000 ) , owner . address , ZERO ) ; await setup . dai . approve ( aaveV2Setup . lendingPool . address , ether ( 50000 ) ) ; await aaveV2Setup . lendingPool . deposit ( setup . dai . address , ether ( 50000 ) , owner . address , ZERO ) ; await setup . usdc . approve ( aaveV2Setup . lendingPool . address , usdc ( 50000 ) ) ; await aaveV2Setup . lendingPool . deposit ( setup . usdc . address , usdc ( 50000 ) , owner . address , ZERO ) ; aWBTC = wbtcReserveTokens . aToken ; variableDebtUSDC = usdcReserveTokens . variableDebtToken ; variableDebtDAI = aaveV2Setup . daiReserveTokens . variableDebtToken ; await uniswapSetup . createNewPair ( setup . wbtc . address , setup . usdc . address ) ; await setup . usdc . 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 ) ; await uniswapSetup . router . connect ( owner . wallet ) . addLiquidity ( setup . wbtc . address , setup . usdc . address , bitcoin ( 100 ) , usdc ( 100000 ) , bitcoin ( 100 ) , usdc ( 100000 ) , owner . address , MAX_UINT_256 ) ; await uniswapSetup . router . connect ( owner . wallet ) . addLiquidity ( setup . wbtc . address , setup . dai . address , bitcoin ( 100 ) , ether ( 100000 ) , bitcoin ( 100 ) , ether ( 100000 ) , owner . address , MAX_UINT_256 ) ; debtIssuanceModule = await deployer . modules . deployDebtIssuanceModuleV2 ( setup . controller . address ) ; await setup . controller . addModule ( debtIssuanceModule . address ) ; aaveV2Library = await deployer . libraries . deployAaveV2 ( ) ; aaveLeverageModule = await deployer . modules . deployAaveLeverageModule ( setup . controller . address , aaveV2Setup . lendingPoolAddressesProvider . address , "contracts/protocol/integration/lib/AaveV2.sol:AaveV2" , aaveV2Library . address ) ; await setup . controller . addModule ( aaveLeverageModule . address ) ; uniswapExchangeAdapter = await deployer . adapters . deployUniswapV2ExchangeAdapter ( uniswapSetup . router . address ) ; await setup . integrationRegistry . addIntegration ( aaveLeverageModule . address , "UniswapV2ExchangeAdapter" , uniswapExchangeAdapter . address ) ; await setup . integrationRegistry . addIntegration ( aaveLeverageModule . address , "DefaultIssuanceModule" , debtIssuanceModule . address ) ; } ) ; describe ( "#issuance" , async ( ) => { let setToken ; let issueFee ; let aWBTCUnits ; let actualSeizedTokens ; let subjectSetToken ; let subjectQuantity ; let subjectTo ; let subjectCaller ; let issueQuantity ; async function subject ( ) { return debtIssuanceModule . connect ( subjectCaller . wallet ) . issue ( subjectSetToken , subjectQuantity , subjectTo ) ; } context ( "when a default aToken position with 0 supply" , async ( ) => { cacheBeforeEach ( async ( ) => { await aaveV2Setup . lendingPool . borrow ( setup . wbtc . address , bitcoin ( 10 ) , 2 , ZERO , owner . address ) ; aWBTCUnits = bitcoin ( 1 ) ; setToken = await setup . createSetToken ( [ aWBTC . address ] , [ aWBTCUnits ] , [ aaveLeverageModule . address , debtIssuanceModule . address ] ) ; issueFee = ether ( 0.005 ) ; await debtIssuanceModule . initialize ( setToken . address , ether ( 0.02 ) , issueFee , ether ( 0.005 ) , feeRecipient . address , ADDRESS_ZERO ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; } ) ; beforeEach ( ( ) => { subjectSetToken = setToken . address ; subjectQuantity = issueQuantity ; subjectTo = owner . address ; subjectCaller = owner ; } ) ; it ( "should not update the collateral position on the SetToken" , async ( ) => { const initialPositions = await setToken . getPositions ( ) ; await subject ( ) ; const currentPositions = await setToken . getPositions ( ) ; const newFirstPosition = ( await setToken . getPositions ( ) ) [ 0 ] ; expect ( initialPositions . length ) . to . eq ( 1 ) ; expect ( currentPositions . length ) . to . eq ( 1 ) ; expect ( newFirstPosition . component ) . to . eq ( aWBTC . address ) ; expect ( newFirstPosition . positionState ) . to . eq ( 0 ) ; expect ( newFirstPosition . unit ) . to . eq ( aWBTCUnits ) ; expect ( newFirstPosition . module ) . to . eq ( ADDRESS_ZERO ) ; } ) ; it ( "should not have a borrow position" , async ( ) => { const initialPositions = await setToken . getPositions ( ) ; await subject ( ) ; const currentPositions = await setToken . getPositions ( ) ; expect ( initialPositions . length ) . to . eq ( 1 ) ; expect ( currentPositions . length ) . to . eq ( 1 ) ; } ) ; 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 preSetUsdcBalance = await setup . usdc . balanceOf ( subjectSetToken ) ; await subject ( ) ; const mintQuantity = preciseMul ( subjectQuantity , ether ( 1 ) . add ( issueFee ) ) ; const usdcFlows = preciseMulCeil ( mintQuantity , ZERO ) ; const aWBTCFlows = preciseMul ( mintQuantity , aWBTCUnits ) ; 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 ( postMinterAWBTCBalance ) . to . eq ( preMinterAWBTCBalance . sub ( aWBTCFlows ) ) ; expect ( postSetAWBTCBalance ) . to . eq ( preSetAWBTCBalance . add ( aWBTCFlows ) ) ; expect ( postMinterUsdcBalance ) . to . eq ( preMinterUsdcBalance . add ( usdcFlows ) ) ; expect ( postSetUsdcBalance ) . to . eq ( preSetUsdcBalance ) ; } ) ; } ) ; context ( "when a default aToken position and external borrow position" , async ( ) => { cacheBeforeEach ( async ( ) => { await aaveV2Setup . lendingPool . borrow ( setup . wbtc . address , bitcoin ( 10 ) , 2 , ZERO , owner . address ) ; aWBTCUnits = bitcoin ( 1 ) ; setToken = await setup . createSetToken ( [ aWBTC . address ] , [ aWBTCUnits ] , [ aaveLeverageModule . address , debtIssuanceModule . address ] ) ; issueFee = ether ( 0.005 ) ; await debtIssuanceModule . initialize ( setToken . address , ether ( 0.02 ) , issueFee , ether ( 0.005 ) , feeRecipient . address , ADDRESS_ZERO ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; await aaveLeverageModule . lever ( setToken . address , setup . usdc . address , setup . wbtc . address , usdc ( 500 ) , bitcoin ( 0.4 ) , "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 ] ; expect ( initialPositions . length ) . to . eq ( 2 ) ; expect ( currentPositions . length ) . to . eq ( 2 ) ; expect ( newFirstPosition . component ) . to . eq ( aWBTC . address ) ; expect ( newFirstPosition . positionState ) . to . eq ( 0 ) ; expect ( newFirstPosition . unit ) . to . eq ( initialPositions [ 0 ] . unit ) ; expect ( newFirstPosition . module ) . to . eq ( ADDRESS_ZERO ) ; } ) ; it ( "should update the borrow position on the SetToken correctly" , async ( ) => { const initialPositions = await setToken . getPositions ( ) ; const previousSecondPositionBalance = await variableDebtUSDC . balanceOf ( setToken . address ) ; const setTotalSupply = await setToken . totalSupply ( ) ; await subject ( ) ; const expectedSecondPositionUnit = preciseDivCeil ( previousSecondPositionBalance , setTotalSupply ) . mul ( - 1 ) ; const currentPositions = await setToken . getPositions ( ) ; const newSecondPosition = ( await setToken . getPositions ( ) ) [ 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 ) ; expect ( newSecondPosition . unit ) . to . eq ( expectedSecondPositionUnit ) ; expect ( newSecondPosition . 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 mintQuantity = preciseMul ( subjectQuantity , ether ( 1 ) . add ( issueFee ) ) ; 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 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 ( 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 ) ) ; } ) ; } ) ; context ( "when a default aToken position and liquidated borrow position" , async ( ) => { cacheBeforeEach ( async ( ) => { await aaveV2Setup . lendingPool . borrow ( setup . wbtc . address , bitcoin ( 10 ) , 2 , ZERO , owner . address ) ; aWBTCUnits = bitcoin ( 1 ) ; setToken = await setup . createSetToken ( [ aWBTC . address ] , [ aWBTCUnits ] , [ aaveLeverageModule . address , debtIssuanceModule . address ] ) ; issueFee = ether ( 0.005 ) ; await debtIssuanceModule . initialize ( setToken . address , ether ( 0.02 ) , issueFee , ether ( 0.005 ) , feeRecipient . address , ADDRESS_ZERO ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; await aaveLeverageModule . lever ( setToken . address , setup . usdc . address , setup . wbtc . address , usdc ( 750 ) , bitcoin ( 0.5 ) , "UniswapV2ExchangeAdapter" , EMPTY_BYTES ) ; const liquidationUsdcPriceInEth = ether ( 0.0025 ) ; await aaveV2Setup . setAssetPriceInOracle ( setup . usdc . address , liquidationUsdcPriceInEth ) ; const debtToCoverHumanReadable = 200 ; actualSeizedTokens = preciseMul ( preciseMul ( liquidationUsdcPriceInEth , ether ( debtToCoverHumanReadable ) ) , bitcoin ( 1.05 ) ) ; const debtToCover = usdc ( debtToCoverHumanReadable ) ; await setup . usdc . approve ( aaveV2Setup . lendingPool . address , ether ( 250 ) ) ; await aaveV2Setup . lendingPool . connect ( owner . wallet ) . liquidationCall ( setup . wbtc . address , setup . usdc . address , setToken . address , debtToCover , true ) ; await aaveV2Setup . setAssetPriceInOracle ( setup . usdc . address , ether ( 0.001 ) ) ; } ) ; 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 ( ) ; const setTotalSupply = await setToken . totalSupply ( ) ; await subject ( ) ; const currentPositions = await setToken . getPositions ( ) ; const newFirstPosition = ( await setToken . getPositions ( ) ) [ 0 ] ; 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 ( aWBTC . address ) ; expect ( newFirstPosition . positionState ) . to . eq ( 0 ) ; expect ( newFirstPosition . unit ) . to . eq ( expectedPostLiquidationUnit ) ; expect ( newFirstPosition . module ) . to . eq ( ADDRESS_ZERO ) ; } ) ; it ( "should update the borrow position on the SetToken correctly" , async ( ) => { const initialPositions = await setToken . getPositions ( ) ; 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 ] ; 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 ) ; await expectGreaterThanOrEqualToWithUpperBound ( newSecondPosition . unit , expectedSecondPositionUnit ) ; expect ( newSecondPosition . 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 mintQuantity = preciseMul ( subjectQuantity , ether ( 1 ) . add ( issueFee ) ) ; 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 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 ( 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 ) ) ; } ) ; } ) ; context ( "when 2 default positions and 2 external borrow positions" , async ( ) => { 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 ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . dai . address , setup . usdc . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; await setup . weth . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; 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 ) ; 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 ) ; expect ( newSecondPosition . unit ) . to . eq ( initialPositions [ 1 ] . unit ) ; expect ( newSecondPosition . module ) . to . eq ( ADDRESS_ZERO ) ; } ) ; it ( "should update the borrow position on the SetToken correctly" , async ( ) => { 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 ) ; 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 ) ; expect ( newThirdPosition . module ) . to . eq ( aaveLeverageModule . address ) ; await expectGreaterThanOrEqualToWithUpperBound ( newThirdPosition . unit , expectedThirdPositionUnit ) ; expect ( newFourthPosition . component ) . to . eq ( setup . usdc . address ) ; expect ( newFourthPosition . positionState ) . to . eq ( 1 ) ; expect ( newFourthPosition . module ) . to . eq ( aaveLeverageModule . address ) ; expect ( newFourthPosition . unit ) . to . eq ( expectedFourthPositionUnit ) ; } ) ; 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 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 ( postMinterDaiBalance ) . to . eq ( preMinterDaiBalance . add ( daiFlows ) ) ; expect ( postMinterWethBalance ) . to . eq ( preMinterWethBalance . sub ( wethFlows ) ) ; expect ( postSetWethBalance ) . to . eq ( preSetWethBalance . add ( wethFlows ) ) ; expect ( postSetUsdcDebtBalance ) . to . eq ( preSetUsdcDebtBalance . add ( usdcFlows ) ) ; await expectGreaterThanOrEqualToWithUpperBound ( postSetDaiDebtBalance , preSetDaiDebtBalance . add ( daiFlows ) ) ; } ) ; } ) ; context ( "when default and external positions do overlap" , async ( ) => { cacheBeforeEach ( async ( ) => { setToken = await setup . createSetToken ( [ aWBTC . address , setup . usdc . address ] , [ 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 ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address , setup . dai . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; await setup . usdc . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; 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 ) ; 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 ) ; expect ( newSecondPosition . unit ) . to . eq ( initialPositions [ 1 ] . unit ) ; 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 ) ; 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 ) ; expect ( newFourthPosition . module ) . to . eq ( aaveLeverageModule . address ) ; await expectGreaterThanOrEqualToWithUpperBound ( newFourthPosition . unit , expectedFourthPositionUnit ) ; } ) ; 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 ( postSetUsdcEquityBalance ) . to . eq ( preSetUsdcEquityBalance . add ( usdcEquityFlows ) ) ; expect ( postMinterDaiBalance ) . to . eq ( preMinterDaiBalance . add ( daiFlows ) ) ; expect ( postMinterUsdcBalance ) . to . eq ( preMinterUsdcBalance . sub ( usdcEquityFlows ) . add ( usdcDebtFlows ) ) ; expect ( postSetUsdcDebtBalance ) . to . eq ( preSetUsdcDebtBalance . add ( usdcDebtFlows ) ) ; await expectGreaterThanOrEqualToWithUpperBound ( postSetDaiDebtBalance , preSetDaiDebtBalance . add ( daiFlows ) ) ; } ) ; } ) ; } ) ; } ) ; describe ( "#redemption" , async ( ) => { let setToken ; let redeemFee ; let aWBTCUnits ; let actualSeizedTokens ; let subjectSetToken ; let subjectQuantity ; let subjectTo ; let subjectCaller ; let issueQuantity ; async function subject ( ) { 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 ( ) => { aWBTCUnits = bitcoin ( 1 ) ; setToken = await setup . createSetToken ( [ aWBTC . address ] , [ aWBTCUnits ] , [ aaveLeverageModule . address , debtIssuanceModule . address ] ) ; await debtIssuanceModule . initialize ( setToken . address , ether ( 0.02 ) , ZERO , ZERO , feeRecipient . address , ADDRESS_ZERO ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; } ) ; 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 ] ; expect ( initialPositions . length ) . to . eq ( 1 ) ; expect ( currentPositions . length ) . to . eq ( 1 ) ; expect ( newFirstPosition . component ) . to . eq ( aWBTC . address ) ; expect ( newFirstPosition . positionState ) . to . eq ( 0 ) ; expect ( newFirstPosition . unit ) . to . eq ( aWBTCUnits ) ; expect ( newFirstPosition . module ) . to . eq ( ADDRESS_ZERO ) ; } ) ; it ( "should update the borrow position on the SetToken correctly" , async ( ) => { const initialPositions = await setToken . getPositions ( ) ; await subject ( ) ; const currentPositions = await setToken . getPositions ( ) ; expect ( initialPositions . length ) . to . eq ( 1 ) ; expect ( currentPositions . length ) . to . eq ( 1 ) ; } ) ; 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 preSetUsdcBalance = await setup . usdc . balanceOf ( subjectSetToken ) ; await subject ( ) ; const mintQuantity = preciseMul ( subjectQuantity , ether ( 1 ) ) ; const usdcFlows = preciseMulCeil ( mintQuantity , ZERO ) ; const aWBTCFlows = preciseMul ( mintQuantity , aWBTCUnits ) ; 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 ( postMinterAWBTCBalance ) . to . eq ( preMinterAWBTCBalance . add ( aWBTCFlows ) ) ; expect ( postSetAWBTCBalance ) . to . eq ( preSetAWBTCBalance . sub ( aWBTCFlows ) ) ; expect ( postMinterUsdcBalance ) . to . eq ( preMinterUsdcBalance . sub ( usdcFlows ) ) ; expect ( postSetUsdcBalance ) . to . eq ( preSetUsdcBalance ) ; } ) ; } ) ; context ( "when a default aToken position and external borrow position" , async ( ) => { let issueQuantity ; cacheBeforeEach ( async ( ) => { aWBTCUnits = bitcoin ( 1 ) ; setToken = await setup . createSetToken ( [ aWBTC . address ] , [ aWBTCUnits ] , [ aaveLeverageModule . address , debtIssuanceModule . address ] ) ; redeemFee = ether ( 0.005 ) ; await debtIssuanceModule . initialize ( setToken . address , ether ( 0.02 ) , ether ( 0.005 ) , redeemFee , feeRecipient . address , ADDRESS_ZERO ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; await aaveLeverageModule . lever ( setToken . address , setup . usdc . address , setup . wbtc . address , usdc ( 500 ) , bitcoin ( 0.4 ) , "UniswapV2ExchangeAdapter" , EMPTY_BYTES ) ; await setup . usdc . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; } ) ; 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 ] ; expect ( initialPositions . length ) . to . eq ( 2 ) ; expect ( currentPositions . length ) . to . eq ( 2 ) ; expect ( newFirstPosition . component ) . to . eq ( aWBTC . address ) ; expect ( newFirstPosition . positionState ) . to . eq ( 0 ) ; expect ( newFirstPosition . unit ) . to . eq ( initialPositions [ 0 ] . unit ) ; expect ( newFirstPosition . module ) . to . eq ( ADDRESS_ZERO ) ; } ) ; it ( "should update the borrow position on the SetToken correctly" , async ( ) => { const initialPositions = await setToken . getPositions ( ) ; const previousSecondPositionBalance = await variableDebtUSDC . balanceOf ( setToken . address ) ; const setTotalSupply = await setToken . totalSupply ( ) ; await subject ( ) ; const expectedSecondPositionUnit = preciseDivCeil ( previousSecondPositionBalance , setTotalSupply ) . mul ( - 1 ) ; const currentPositions = await setToken . getPositions ( ) ; const newSecondPosition = ( await setToken . getPositions ( ) ) [ 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 ) ; expect ( newSecondPosition . unit ) . to . eq ( expectedSecondPositionUnit ) ; expect ( newSecondPosition . 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 redeemQuantity = preciseMul ( subjectQuantity , ether ( 1 ) . sub ( redeemFee ) ) ; await subject ( ) ; 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 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 ( 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 ) ) ; } ) ; } ) ; context ( "when a default aToken position and external borrow position with redeem to supply to 0" , async ( ) => { let issueQuantity ; cacheBeforeEach ( async ( ) => { aWBTCUnits = bitcoin ( 1 ) ; setToken = await setup . createSetToken ( [ aWBTC . address ] , [ aWBTCUnits ] , [ aaveLeverageModule . address , debtIssuanceModule . address ] ) ; await debtIssuanceModule . initialize ( setToken . address , ether ( 0.02 ) , ZERO , ZERO , feeRecipient . address , ADDRESS_ZERO ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; await aaveLeverageModule . lever ( setToken . address , setup . usdc . address , setup . wbtc . address , usdc ( 500 ) , bitcoin ( 0.4 ) , "UniswapV2ExchangeAdapter" , EMPTY_BYTES ) ; await setup . usdc . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; } ) ; 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 ] ; expect ( initialPositions . length ) . to . eq ( 2 ) ; expect ( currentPositions . length ) . to . eq ( 2 ) ; expect ( newFirstPosition . component ) . to . eq ( aWBTC . address ) ; expect ( newFirstPosition . positionState ) . to . eq ( 0 ) ; expect ( newFirstPosition . unit ) . to . eq ( initialPositions [ 0 ] . unit ) ; expect ( newFirstPosition . module ) . to . eq ( ADDRESS_ZERO ) ; } ) ; it ( "should update the borrow position on the SetToken correctly" , async ( ) => { const initialPositions = await setToken . getPositions ( ) ; await subject ( ) ; const currentPositions = await setToken . getPositions ( ) ; const newSecondPosition = ( await setToken . getPositions ( ) ) [ 1 ] ; const setTotalSupply = await setToken . totalSupply ( ) ; const currentSecondPositionBalance = await variableDebtUSDC . balanceOf ( setToken . address ) ; const usdcSetBalance = await setup . usdc . balanceOf ( setToken . address ) ; const adjustedSecondPositionBalance = currentSecondPositionBalance . mul ( - 1 ) . add ( usdcSetBalance ) ; const newSecondPositionNotional = preciseMul ( newSecondPosition . unit , setTotalSupply ) ; 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 ) ; expect ( newSecondPositionNotional ) . to . eq ( adjustedSecondPositionBalance ) ; expect ( newSecondPosition . 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 redeemQuantity = preciseMul ( subjectQuantity , ether ( 1 ) ) ; await subject ( ) ; 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 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 ( 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 ) ) ; } ) ; } ) ; context ( "when a default aToken position and liquidated borrow position" , async ( ) => { let issueQuantity ; cacheBeforeEach ( async ( ) => { aWBTCUnits = bitcoin ( 1 ) ; setToken = await setup . createSetToken ( [ aWBTC . address ] , [ aWBTCUnits ] , [ aaveLeverageModule . address , debtIssuanceModule . address ] ) ; redeemFee = ether ( 0.005 ) ; await debtIssuanceModule . initialize ( setToken . address , ether ( 0.02 ) , ether ( 0.005 ) , redeemFee , feeRecipient . address , ADDRESS_ZERO ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; await aaveLeverageModule . lever ( setToken . address , setup . usdc . address , setup . wbtc . address , usdc ( 750 ) , bitcoin ( 0.5 ) , "UniswapV2ExchangeAdapter" , EMPTY_BYTES ) ; const liquidationUsdcPriceInEth = ether ( 0.0025 ) ; await aaveV2Setup . setAssetPriceInOracle ( setup . usdc . address , liquidationUsdcPriceInEth ) ; const debtToCoverHumanReadable = 200 ; actualSeizedTokens = preciseMul ( preciseMul ( liquidationUsdcPriceInEth , ether ( debtToCoverHumanReadable ) ) , bitcoin ( 1.05 ) ) ; const debtToCover = usdc ( debtToCoverHumanReadable ) ; await setup . usdc . approve ( aaveV2Setup . lendingPool . address , ether ( 250 ) ) ; await aaveV2Setup . lendingPool . connect ( owner . wallet ) . liquidationCall ( setup . wbtc . address , setup . usdc . address , setToken . address , debtToCover , true ) ; await aaveV2Setup . setAssetPriceInOracle ( setup . usdc . address , ether ( 0.001 ) ) ; await setup . usdc . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; } ) ; 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 ( ) ; const setTotalSupply = await setToken . totalSupply ( ) ; await subject ( ) ; const currentPositions = await setToken . getPositions ( ) ; const newFirstPosition = ( await setToken . getPositions ( ) ) [ 0 ] ; 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 ( aWBTC . address ) ; expect ( newFirstPosition . positionState ) . to . eq ( 0 ) ; expect ( newFirstPosition . unit ) . to . eq ( expectedPostLiquidationUnit ) ; expect ( newFirstPosition . module ) . to . eq ( ADDRESS_ZERO ) ; } ) ; it ( "should update the borrow position on the SetToken correctly" , async ( ) => { const initialPositions = await setToken . getPositions ( ) ; await subject ( ) ; const setTotalSupply = await setToken . totalSupply ( ) ; const currentPositions = await setToken . getPositions ( ) ; const newSecondPosition = ( await setToken . getPositions ( ) ) [ 1 ] ; const previousSecondPositionBalance = await variableDebtUSDC . balanceOf ( setToken . address ) ; const newSecondPositionNotional = preciseMulCeil ( newSecondPosition . unit . mul ( - 1 ) , setTotalSupply ) ; 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 ) ; expect ( newSecondPosition . module ) . to . eq ( aaveLeverageModule . address ) ; await expectGreaterThanOrEqualToWithUpperBound ( newSecondPositionNotional , previousSecondPositionBalance ) ; } ) ; 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 ) ; await subject ( ) ; const redeemQuantity = preciseMul ( subjectQuantity , ether ( 1 ) . sub ( redeemFee ) ) ; 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 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 ( 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 ) ) ; } ) ; } ) ; context ( "when 2 default positions and 2 external borrow positions" , async ( ) => { context ( "when default and external positions do not overlap" , async ( ) => { let issueQuantity ; 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 ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address , setup . dai . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; await setup . weth . approve ( debtIssuanceModule . address , ether ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; 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 ) ; await setup . usdc . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; await setup . dai . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; } ) ; 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 ) ; 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 ) ; expect ( newSecondPosition . unit ) . to . eq ( initialPositions [ 1 ] . unit ) ; 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 ) ; expect ( newThirdPosition . module ) . to . eq ( aaveLeverageModule . address ) ; expect ( newThirdPosition . unit . abs ( ) ) . to . eq ( expectedThirdPositionUnit . abs ( ) ) ; expect ( newFourthPosition . component ) . to . eq ( setup . dai . address ) ; expect ( newFourthPosition . positionState ) . to . eq ( 1 ) ; expect ( newFourthPosition . module ) . to . eq ( aaveLeverageModule . address ) ; await expectGreaterThanOrEqualToWithUpperBound ( newFourthPosition . unit , expectedFourthPositionUnit ) ; } ) ; 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 ( postRedeemerWethBalance ) . to . eq ( preRedeemerWethBalance . add ( wethFlows ) ) ; expect ( postSetWethBalance ) . to . eq ( preSetWethBalance . sub ( wethFlows ) ) ; expect ( postRedeemerDaiBalance ) . to . eq ( preRedeemerDaiBalance . sub ( daiFlows ) ) ; expect ( postSetUsdcDebtBalance ) . to . eq ( preSetUsdcDebtBalance . sub ( usdcFlows ) ) ; await expectGreaterThanOrEqualToWithUpperBound ( postSetDaiDebtBalance , preSetDaiDebtBalance . sub ( daiFlows ) ) ; } ) ; } ) ; context ( "when default and external positions do overlap" , async ( ) => { let issueQuantity ; cacheBeforeEach ( async ( ) => { aWBTCUnits = bitcoin ( 1 ) ; setToken = await setup . createSetToken ( [ aWBTC . address , setup . usdc . address ] , [ 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 ) ; await aaveLeverageModule . updateAllowedSetToken ( setToken . address , true ) ; await aaveLeverageModule . initialize ( setToken . address , [ setup . wbtc . address ] , [ setup . usdc . address , setup . dai . address ] ) ; await aWBTC . approve ( debtIssuanceModule . address , bitcoin ( 1000 ) ) ; await setup . usdc . approve ( debtIssuanceModule . address , usdc ( 1000 ) ) ; issueQuantity = ether ( 1 ) ; await debtIssuanceModule . issue ( setToken . address , issueQuantity , owner . address ) ; 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 ) ; await setup . usdc . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; await setup . dai . approve ( debtIssuanceModule . address , MAX_UINT_256 ) ; } ) ; 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 ) ; 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 ) ; expect ( newSecondPosition . unit ) . to . eq ( initialPositions [ 1 ] . unit ) ; 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 ) ; expect ( newThirdPosition . unit . abs ( ) ) . to . eq ( expectedThirdPositionUnit . abs ( ) ) ; expect ( newThirdPosition . module ) . to . eq ( aaveLeverageModule . address ) ; expect ( newFourthPosition . component ) . to . eq ( setup . dai . address ) ; expect ( newFourthPosition . positionState ) . to . eq ( 1 ) ; expect ( newFourthPosition . module ) . to . eq ( aaveLeverageModule . address ) ; await expectGreaterThanOrEqualToWithUpperBound ( newFourthPosition . unit , expectedFourthPositionUnit ) ; } ) ; 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 ) ) ; await expectGreaterThanOrEqualToWithUpperBound ( postSetDaiDebtBalance , preSetDaiDebtBalance . sub ( daiDebtFlows ) ) ; } ) ; } ) ; } ) ; } ) ; } ) ;