Skip to content

Commit

Permalink
fix: aave close multiply should use common swap data helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
zerotucks committed Aug 22, 2023
1 parent 520d913 commit fa563c2
Showing 1 changed file with 25 additions and 234 deletions.
259 changes: 25 additions & 234 deletions packages/dma-library/src/strategies/aave/close/close.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { FEE_BASE, ONE, TEN, TYPICAL_PRECISION, ZERO } from '@dma-common/constants'
import { calculateFee } from '@dma-common/utils/swap'
import { TYPICAL_PRECISION } from '@dma-common/constants'
import { getFlashloanToken } from '@dma-library/strategies/aave/common'
import {
getAaveTokenAddress,
getAaveTokenAddresses,
} from '@dma-library/strategies/aave/get-aave-token-addresses'
import * as StrategiesCommon from '@dma-library/strategies/common'
import { PositionTransition } from '@dma-library/types'
import { acceptedFeeToken } from '@dma-library/utils/swap/accepted-fee-token'
import { feeResolver } from '@dma-library/utils/swap/fee-resolver'
import BigNumber from 'bignumber.js'

import { buildOperation } from './build-operation'
import { generateTransition } from './generate-transition'
Expand All @@ -21,8 +17,8 @@ export async function close(
dependencies: AaveCloseDependencies,
): Promise<PositionTransition> {
const getSwapData = args.shouldCloseToCollateral
? getSwapDataToCloseToCollateral
: getSwapDataToCloseToDebt
? getAaveSwapDataToCloseToCollateral
: getAaveSwapDataToCloseToDebt

const collateralTokenAddress = getAaveTokenAddress(args.collateralToken, dependencies.addresses)
const debtTokenAddress = getAaveTokenAddress(args.debtToken, dependencies.addresses)
Expand Down Expand Up @@ -63,207 +59,45 @@ export async function close(
)
}

// async function getAaveSwapDataToCloseToCollateral(
// { debtToken, collateralToken, slippage, protocolVersion }: AaveCloseArgsWithVersioning,
// dependencies: AaveCloseDependencies,
// ) {
// const { addresses } = dependencies
// const { collateralTokenAddress, debtTokenAddress } = getAaveTokenAddresses(
// { debtToken, collateralToken },
// addresses,
// )
//
// // Since we cannot get the exact amount that will be needed
// // to cover all debt, there will be left overs of the debt token
// // which will then have to be transferred back to the user
// const [, colPrice, debtPrice] = (
// await getValuesFromProtocol(
// protocolVersion,
// collateralTokenAddress,
// debtTokenAddress,
// dependencies,
// )
// ).map(price => {
// return new BigNumber(price.toString())
// })
//
// const collateralTokenWithAddress = {
// ...collateralToken,
// precision: collateralToken.precision || TYPICAL_PRECISION,
// address: collateralTokenAddress,
// }
// const debtTokenWithAddress = {
// ...debtToken,
// precision: debtToken.precision || TYPICAL_PRECISION,
// address: debtTokenAddress,
// }
//
// return await StrategiesCommon.getSwapDataForCloseToCollateral({
// collateralToken: collateralTokenWithAddress,
// debtToken: debtTokenWithAddress,
// colPrice,
// debtPrice,
// outstandingDebt: dependencies.currentPosition.debt.amount,
// slippage,
// ETHAddress: addresses.ETH,
// getSwapData: dependencies.getSwapData,
// })
// }
//
// async function getAaveSwapDataToCloseToDebt(
// { debtToken, collateralToken, slippage, collateralAmountLockedInProtocolInWei }: AaveCloseArgs,
// dependencies: AaveCloseDependencies,
// ) {
// const { addresses } = dependencies
// const { collateralTokenAddress, debtTokenAddress } = getAaveTokenAddresses(
// { debtToken, collateralToken },
// addresses,
// )
//
// const swapAmountBeforeFees = collateralAmountLockedInProtocolInWei
// const fromToken = {
// ...collateralToken,
// precision: collateralToken.precision || TYPICAL_PRECISION,
// address: collateralTokenAddress,
// }
// const toToken = {
// ...debtToken,
// precision: debtToken.precision || TYPICAL_PRECISION,
// address: debtTokenAddress,
// }
//
// return StrategiesCommon.getSwapDataForCloseToDebt({
// fromToken,
// toToken,
// slippage,
// swapAmountBeforeFees,
// getSwapData: dependencies.getSwapData,
// })
// }

async function getSwapDataToCloseToCollateral(
async function getAaveSwapDataToCloseToCollateral(
{
debtToken,
collateralToken,
slippage,
protocolValues: { collateralTokenPrice, debtTokenPrice },
collateralTokenAddress,
debtTokenAddress,
}: ExpandedAaveCloseArgs,
dependencies: AaveCloseDependencies,
) {
// Since we cannot get the exact amount that will be needed
// to cover all debt, there will be left overs of the debt token
// which will then have to be transferred back to the user
let colPrice = collateralTokenPrice
let debtPrice = debtTokenPrice

// 1.Use offset amount which will be used in the swap as well.
// The idea is that after the debt is paid, the remaining will be transferred to the beneficiary
// Debt is a complex number and interest rate is constantly applied.
// We don't want to end up having leftovers of debt transferred to the user
// so instead of charging the user a fee, we add an offset ( equal to the fee ) to the
// collateral amount. This means irrespective of whether the fee is collected before
// or after the swap, there will always be sufficient debt token remaining to cover the outstanding position debt.
const fee = feeResolver(collateralToken.symbol, debtToken.symbol) // as DECIMAL number
const debtTokenPrecision = debtToken.precision || TYPICAL_PRECISION
const collateralTokenPrecision = collateralToken.precision || TYPICAL_PRECISION

// 2. Calculated the needed amount of collateral to payback the debt
// This value is calculated based on the AAVE protocol oracles.
// At the time of writing, their main source are Chainlink oracles.
const collateralNeeded = calculateNeededCollateralToPaybackDebt(
debtPrice,
debtTokenPrecision,
colPrice,
collateralTokenPrecision,
dependencies.currentPosition.debt.amount,
fee,
slippage,
const { addresses } = dependencies
const { collateralTokenAddress, debtTokenAddress } = getAaveTokenAddresses(
{ debtToken, collateralToken },
addresses,
)

// 3 Get latest market price
// If you check i.e. https://data.chain.link/ethereum/mainnet/stablecoins/usdc-eth ,
// there is a deviation threshold value that shows how much the prices on/off chain might differ
// When there is a 1inch swap, we use real-time market price. To calculate that,
// A preflight request is sent to calculate the existing market price.
const debtIsEth = debtTokenAddress === dependencies.addresses.ETH
const collateralIsEth = collateralTokenAddress === dependencies.addresses.ETH

if (debtIsEth) {
debtPrice = ONE.times(TEN.pow(debtTokenPrecision))
} else {
const debtPricePreflightSwapData = await dependencies.getSwapData(
debtTokenAddress,
dependencies.addresses.ETH,
dependencies.currentPosition.debt.amount,
slippage,
)
debtPrice = new BigNumber(
debtPricePreflightSwapData.toTokenAmount
.div(debtPricePreflightSwapData.fromTokenAmount)
.times(TEN.pow(debtTokenPrecision))
.toFixed(0),
)
const collateralTokenWithAddress = {
...collateralToken,
precision: collateralToken.precision || TYPICAL_PRECISION,
address: collateralTokenAddress,
}

if (collateralIsEth) {
colPrice = ONE.times(TEN.pow(collateralTokenPrecision))
} else {
const colPricePreflightSwapData =
!collateralIsEth &&
(await dependencies.getSwapData(
collateralTokenAddress,
dependencies.addresses.ETH,
collateralNeeded,
slippage,
))

colPrice = new BigNumber(
colPricePreflightSwapData.toTokenAmount
.div(colPricePreflightSwapData.fromTokenAmount)
.times(TEN.pow(collateralTokenPrecision))
.toFixed(0),
)
const debtTokenWithAddress = {
...debtToken,
precision: debtToken.precision || TYPICAL_PRECISION,
address: debtTokenAddress,
}

// 4. Get Swap Data
// This is the actual swap data that will be used in the transaction.
const amountNeededToEnsureRemainingDebtIsRepaid = calculateNeededCollateralToPaybackDebt(
debtPrice,
debtTokenPrecision,
colPrice,
collateralTokenPrecision,
dependencies.currentPosition.debt.amount,
fee.div(FEE_BASE),
return await StrategiesCommon.getSwapDataForCloseToCollateral({
collateralToken: collateralTokenWithAddress,
debtToken: debtTokenWithAddress,
colPrice: collateralTokenPrice,
debtPrice: debtTokenPrice,
outstandingDebt: dependencies.currentPosition.debt.amount,
slippage,
)

const swapData = await dependencies.getSwapData(
collateralTokenAddress,
debtTokenAddress,
amountNeededToEnsureRemainingDebtIsRepaid,
slippage,
)

const collectFeeFrom = acceptedFeeToken({
fromToken: collateralTokenAddress,
toToken: debtTokenAddress,
ETHAddress: addresses.ETH,
getSwapData: dependencies.getSwapData,
})

const preSwapFee =
collectFeeFrom === 'sourceToken'
? calculateFee(amountNeededToEnsureRemainingDebtIsRepaid, fee.toNumber())
: ZERO

return {
swapData,
collectFeeFrom,
preSwapFee,
}
}

async function getSwapDataToCloseToDebt(
async function getAaveSwapDataToCloseToDebt(
{ debtToken, collateralToken, slippage }: ExpandedAaveCloseArgs,
dependencies: AaveCloseDependencies,
) {
Expand Down Expand Up @@ -292,47 +126,4 @@ async function getSwapDataToCloseToDebt(
swapAmountBeforeFees,
getSwapData: dependencies.getSwapData,
})
// const fee = feeResolver(collateralToken.symbol, debtToken.symbol)
//
// const preSwapFee =
// collectFeeFrom === 'sourceToken' ? calculateFee(swapAmountBeforeFees, fee.toNumber()) : ZERO
//
// const swapAmountAfterFees = swapAmountBeforeFees
// .minus(preSwapFee)
// .integerValue(BigNumber.ROUND_DOWN)
//
// const swapData = await dependencies.getSwapData(
// collateralTokenAddress,
// debtTokenAddress,
// swapAmountAfterFees,
// slippage,
// )
//
// return { swapData, collectFeeFrom, preSwapFee }
}

function calculateNeededCollateralToPaybackDebt(
debtPrice: BigNumber,
debtPrecision: number,
colPrice: BigNumber,
colPrecision: number,
debtAmount: BigNumber,
fee: BigNumber,
slippage: BigNumber,
) {
// Depending on the protocol the price could be anything.
// i.e AAVEv3 returns the prices in USD
// AAVEv2 returns the prices in ETH
// @paybackAmount - the amount denominated in the protocol base currency ( i.e. AAVEv2 - It will be in ETH, AAVEv3 - USDC)
const paybackAmount = debtPrice.times(debtAmount)
const paybackAmountInclFee = paybackAmount.times(ONE.plus(fee))
// Same rule applies for @collateralAmountNeeded. @colPrice is either in USDC ( AAVEv3 ) or ETH ( AAVEv2 )
// or could be anything eles in the following versions.
const collateralAmountNeeded = new BigNumber(
paybackAmount
.plus(paybackAmount.times(fee))
.plus(paybackAmountInclFee.times(slippage))
.div(colPrice),
).integerValue(BigNumber.ROUND_DOWN)
return collateralAmountNeeded.times(TEN.pow(colPrecision - debtPrecision))
}

0 comments on commit fa563c2

Please sign in to comment.