Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make THOR repayments great again #5898

Merged
merged 2 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions src/lib/utils/thorchain/lending.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { ThornodePoolResponse } from 'lib/swapper/swappers/ThorchainSwapper
import { assetIdToPoolAssetId } from 'lib/swapper/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers'
import { getAccountAddresses, toThorBaseUnit } from 'lib/utils/thorchain'
import { selectAssetById } from 'state/slices/selectors'
import { convertDecimalPercentageToBasisPoints } from 'state/slices/tradeQuoteSlice/utils'
import { store } from 'state/store'

import type {
Expand Down Expand Up @@ -72,22 +73,20 @@ export const getMaybeThorchainLendingOpenQuote = async ({
// see https://thornode.ninerealms.com/thorchain/doc
export const getMaybeThorchainLendingCloseQuote = async ({
collateralAssetId,
repaymentAmountCryptoBaseUnit,
repaymentPercent,
repaymentAssetId,
collateralAssetAddress,
}: {
collateralAssetId: AssetId
repaymentAmountCryptoBaseUnit: BigNumber.Value | null | undefined
repaymentPercent: number
repaymentAssetId: AssetId
collateralAssetAddress: string
}): Promise<Result<LendingWithdrawQuoteResponseSuccess, string>> => {
if (!repaymentAmountCryptoBaseUnit) return Err('Amount is required')
if (!repaymentPercent) return Err('A non-zero amount is required')

const repaymentAsset = selectAssetById(store.getState(), repaymentAssetId)
if (!repaymentAsset) return Err(`Asset not found for assetId ${repaymentAsset}`)
const amountCryptoThorBaseUnit = toThorBaseUnit({
valueCryptoBaseUnit: repaymentAmountCryptoBaseUnit,
asset: repaymentAsset,
})
const repayBps = convertDecimalPercentageToBasisPoints(bn(repaymentPercent).div(100).toNumber())

const from_asset = assetIdToPoolAssetId({ assetId: repaymentAssetId })
if (!from_asset) return Err(`Pool asset not found for assetId ${repaymentAssetId}`)
Expand All @@ -103,7 +102,7 @@ export const getMaybeThorchainLendingCloseQuote = async ({
const url =
`${REACT_APP_THORCHAIN_NODE_URL}/lcd/thorchain/quote/loan/close` +
`?from_asset=${from_asset}` +
`&amount=${amountCryptoThorBaseUnit.toString()}` +
`&repay_bps=${repayBps.toString()}` +
`&to_asset=${to_asset}` +
`&loan_owner=${parsedCollateralAssetAddress}`

Expand Down
2 changes: 1 addition & 1 deletion src/lib/utils/thorchain/lending/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export type LendingQuoteClose = {
quoteTotalTimeMs: number
quoteExpiry: number
repaymentAmountCryptoPrecision: string | null
repaymentPercentOrDefault: number
repaymentPercent: number
}

export const isLendingQuoteOpen = (
Expand Down
10 changes: 5 additions & 5 deletions src/pages/Lending/Pool/components/Repay/RepayConfirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export const RepayConfirm = ({
// which we *want* to wait for before considering the repay as complete
await waitForThorchainUpdate({
txId: _txId,
skipOutbound: bn(confirmedQuote.repaymentPercentOrDefault).lt(101),
skipOutbound: bn(confirmedQuote.repaymentPercent).lt(100),
expectedCompletionTime,
}).promise
queryClient.invalidateQueries({ queryKey: ['thorchainLendingPosition'], exact: false })
Expand Down Expand Up @@ -167,14 +167,14 @@ export const RepayConfirm = ({
repaymentAccountId,
// Use the previously locked quote's repayment perecent to refetch a quote after expiry
// This is locked in the confirmed quote and should never be programmatic, or we risk being off-by-one and missing a bit of dust for 100% repayments
repaymentPercent: confirmedQuote?.repaymentPercentOrDefault ?? 0,
repaymentPercent: confirmedQuote?.repaymentPercent ?? 0,
}),
[
collateralAccountId,
collateralAssetId,
repaymentAccountId,
repaymentAsset?.assetId,
confirmedQuote?.repaymentPercentOrDefault,
confirmedQuote?.repaymentPercent,
],
)

Expand Down Expand Up @@ -210,7 +210,7 @@ export const RepayConfirm = ({

// This should never happen, but if it does, we don't want to rug our testing accounts and have to wait 30.5 more days before testing again
if (
bn(confirmedQuote.repaymentPercentOrDefault).gte(100) &&
bn(confirmedQuote.repaymentPercent).gte(100) &&
bn(confirmedQuote.quoteLoanCollateralDecreaseCryptoPrecision).isZero()
) {
throw new Error('100% repayments should trigger a collateral refund transfer')
Expand Down Expand Up @@ -485,7 +485,7 @@ export const RepayConfirm = ({
confirmedQuote={confirmedQuote}
repaymentAsset={repaymentAsset}
collateralAssetId={collateralAssetId}
repaymentPercent={confirmedQuote?.repaymentPercentOrDefault ?? 0}
repaymentPercent={confirmedQuote?.repaymentPercent ?? 0}
repayAmountCryptoPrecision={confirmedQuote?.repaymentAmountCryptoPrecision ?? '0'}
collateralDecreaseAmountCryptoPrecision={
confirmedQuote?.quoteLoanCollateralDecreaseCryptoPrecision ?? '0'
Expand Down
29 changes: 9 additions & 20 deletions src/pages/Lending/hooks/useLendingCloseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import memoize from 'lodash/memoize'
import { useMemo } from 'react'
import { useDebounce } from 'hooks/useDebounce/useDebounce'
import { BigNumber, bn } from 'lib/bignumber/bignumber'
import { toBaseUnit } from 'lib/math'
import { fromThorBaseUnit } from 'lib/utils/thorchain'
import { BASE_BPS_POINTS } from 'lib/utils/thorchain/constants'
import { getMaybeThorchainLendingCloseQuote } from 'lib/utils/thorchain/lending'
Expand Down Expand Up @@ -39,12 +38,12 @@ const selectLendingCloseQueryData = memoize(
data,
collateralAssetMarketData,
repaymentAmountCryptoPrecision,
repaymentPercentOrDefault,
repaymentPercent,
}: {
data: LendingWithdrawQuoteResponseSuccess
collateralAssetMarketData: MarketData
repaymentAmountCryptoPrecision: string | null
repaymentPercentOrDefault: number
repaymentPercent: number
}): LendingQuoteClose => {
const quote = data

Expand Down Expand Up @@ -111,7 +110,7 @@ const selectLendingCloseQueryData = memoize(
quoteTotalTimeMs,
quoteExpiry,
repaymentAmountCryptoPrecision,
repaymentPercentOrDefault,
repaymentPercent,
}
},
)
Expand All @@ -124,13 +123,6 @@ export const useLendingQuoteCloseQuery = ({
collateralAccountId: _collateralAccountId,
enabled = true,
}: UseLendingQuoteCloseQueryProps & QueryObserverOptions) => {
const repaymentPercentOrDefault = useMemo(() => {
const repaymentPercentBn = bnOrZero(_repaymentPercent)
// 1% buffer in case our market data differs from THOR's, to ensure 100% loan repays are actually 100% repays
if (!repaymentPercentBn.eq(100)) return _repaymentPercent
return repaymentPercentBn.plus('1').toNumber()
}, [_repaymentPercent])

const { data: lendingPositionData } = useLendingPositionData({
assetId: _collateralAssetId,
accountId: _collateralAccountId,
Expand All @@ -145,7 +137,7 @@ export const useLendingQuoteCloseQuery = ({
repaymentAccountId: _repaymentAccountId,
collateralAssetId: _collateralAssetId,
collateralAccountId: _collateralAccountId,
repaymentPercent: repaymentPercentOrDefault,
repaymentPercent: _repaymentPercent,
},
],
500,
Expand Down Expand Up @@ -187,12 +179,12 @@ export const useLendingQuoteCloseQuery = ({
const repaymentAmountFiatUserCurrency = useMemo(() => {
if (!lendingPositionData) return null

const proratedCollateralFiatUserCurrency = bnOrZero(repaymentPercentOrDefault)
const proratedCollateralFiatUserCurrency = bnOrZero(repaymentPercent)
.times(lendingPositionData?.debtBalanceFiatUserCurrency ?? 0)
.div(100)

return proratedCollateralFiatUserCurrency.toFixed()
}, [lendingPositionData, repaymentPercentOrDefault])
}, [lendingPositionData, repaymentPercent])

const repaymentAmountCryptoPrecision = useMemo(() => {
if (!repaymentAmountFiatUserCurrency) return null
Expand All @@ -208,10 +200,7 @@ export const useLendingQuoteCloseQuery = ({
const quote = await getMaybeThorchainLendingCloseQuote({
repaymentAssetId,
collateralAssetId,
repaymentAmountCryptoBaseUnit: toBaseUnit(
repaymentAmountCryptoPrecision ?? 0, // actually always defined at runtime, see "enabled" option
repaymentAsset?.precision ?? 0, // actually always defined at runtime, see "enabled" option
),
repaymentPercent,
collateralAssetAddress, // actually always defined at runtime, see "enabled" option
})

Expand All @@ -227,7 +216,7 @@ export const useLendingQuoteCloseQuery = ({
data,
collateralAssetMarketData,
repaymentAmountCryptoPrecision,
repaymentPercentOrDefault,
repaymentPercent,
}),
// Do not refetch if consumers explicitly set enabled to false
// They do so because the query should never run in the reactive react realm, but only programmatically with the refetch function
Expand All @@ -236,7 +225,7 @@ export const useLendingQuoteCloseQuery = ({
enabled: Boolean(
enabled &&
lendingPositionData?.address &&
bnOrZero(repaymentPercentOrDefault).gt(0) &&
bnOrZero(repaymentPercent).gt(0) &&
repaymentAccountId &&
collateralAssetId &&
collateralAccountMetadata &&
Expand Down