Skip to content

Commit

Permalink
feat: thorchain LP unsafe high-slippage deposits flow (#6600)
Browse files Browse the repository at this point in the history
  • Loading branch information
gomesalexandre authored Mar 29, 2024
1 parent c40d5a8 commit ebc1c99
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 47 deletions.
3 changes: 2 additions & 1 deletion src/assets/translations/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -2223,7 +2223,7 @@
"emptyTitle": "The Land of Zero Loans!",
"emptyBody": "It appears you don't have any loans at the moment. Is this financial zen or just a break before your next big lending adventure? Either way, enjoy the calm!"
},
"unsafeBorrow": "This borrow has high slippage. Proceed with caution."
"unsafeBorrow": "This borrow has high slippage (%{slippagePercentage}%). Proceed with caution."
},
"foxDiscounts": {
"currentFoxPower": "Your FOX Power",
Expand Down Expand Up @@ -2258,6 +2258,7 @@
"availablePools": "Available Pools",
"addLiquidity": "Add Liquidity",
"customSlippageDisabled": "We are unable to set a custom slippage",
"unsafeQuote": "This deposit has high slippage (%{slippagePercentage}%). Proceed with caution.",
"removeLiquidity": "Remove Liquidity",
"initialPricesAndPoolShare": "Initial Prices and Pool Share",
"pricePerAsset": "%{from} per %{to}",
Expand Down
12 changes: 7 additions & 5 deletions src/lib/utils/thorchain/lp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,25 @@ export const getSlippage = ({
const numerator = bnOrZero(R).times(a).minus(bnOrZero(A).times(r))
const denominator = bnOrZero(A).times(r).plus(bnOrZero(R).times(A))

const slippageBps = numerator.div(denominator).abs()
const slippageDecimalPercent = numerator.div(denominator).abs()
const assetPriceInRune = bnOrZero(pool.balance_rune).div(pool.balance_asset)

if (a.gt(0) && r.eq(0)) {
const aInRune = a.times(assetPriceInRune)
return {
decimalPercent: slippageBps.times(100).toFixed(),
runeAmountCryptoPrecision: fromThorBaseUnit(aInRune.times(slippageBps)).toFixed(
decimalPercent: slippageDecimalPercent.toFixed(),
runeAmountCryptoPrecision: fromThorBaseUnit(aInRune.times(slippageDecimalPercent)).toFixed(
THOR_PRECISION,
),
}
}

if (r.gt(0) && a.eq(0)) {
return {
decimalPercent: slippageBps.times(100).toFixed(),
runeAmountCryptoPrecision: fromThorBaseUnit(r.times(slippageBps)).toFixed(THOR_PRECISION),
decimalPercent: slippageDecimalPercent.toFixed(),
runeAmountCryptoPrecision: fromThorBaseUnit(r.times(slippageDecimalPercent)).toFixed(
THOR_PRECISION,
),
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/pages/Lending/Pool/components/Borrow/BorrowInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,11 @@ export const BorrowInput = ({
<AlertIcon color='red' />
<Stack spacing={0}>
<AlertDescription lineHeight='short'>
{translate('lending.unsafeBorrow')}
{translate('lending.unsafeBorrow', {
slippagePercentage: bnOrZero(quoteSlippageDecimalPercentage)
.times(100)
.toFixed(2),
})}
</AlertDescription>
</Stack>
</Alert>
Expand Down
128 changes: 88 additions & 40 deletions src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ import { ReadOnlyAsset } from '../ReadOnlyAsset'
import { PoolSummary } from './components/PoolSummary'
import { AddLiquidityRoutePaths } from './types'

const UNSAFE_SLIPPAGE_DECIMAL_PERCENT = 0.05 // 5%

const buttonProps = { flex: 1, justifyContent: 'space-between' }

const formControlProps = {
Expand Down Expand Up @@ -194,6 +196,30 @@ export const AddLiquidityInput: React.FC<AddLiquidityInputProps> = ({
const [virtualRuneDepositAmountFiatUserCurrency, setVirtualRuneDepositAmountFiatUserCurrency] =
useState<string | undefined>()

const [slippageDecimalPercentage, setSlippageDecimalPercentage] = useState<string | undefined>()
const [isUnsafeQuoteNoticeDismissed, setIsUnsafeQuoteNoticeDismissed] = useState<boolean | null>(
null,
)

const handleAcknowledgeUnsafeQuote = useCallback(() => {
// We don't want to *immediately* set this or there will be a "click-through"
// i.e the regular continue button will render immediately, and click will bubble to it
setTimeout(() => {
setIsUnsafeQuoteNoticeDismissed(true)
}, 100)
}, [])

const isUnsafeQuote = useMemo(
() =>
slippageDecimalPercentage &&
bn(slippageDecimalPercentage).gt(UNSAFE_SLIPPAGE_DECIMAL_PERCENT),
[slippageDecimalPercentage],
)

useEffect(() => {
if (isUnsafeQuote) setIsUnsafeQuoteNoticeDismissed(false)
}, [isUnsafeQuote])

const { data: pools } = usePools()
const assets = useAppSelector(selectAssets)

Expand Down Expand Up @@ -989,6 +1015,8 @@ export const AddLiquidityInput: React.FC<AddLiquidityInputProps> = ({
assetId: poolAsset.assetId,
})

setSlippageDecimalPercentage(estimate.slippageDecimalPercent)

const _slippageFiatUserCurrency = bnOrZero(estimate.slippageRuneCryptoPrecision)
.times(runeMarketData.price)
.toFixed()
Expand Down Expand Up @@ -1549,46 +1577,66 @@ export const AddLiquidityInput: React.FC<AddLiquidityInputProps> = ({
{incompleteAlert}
{maybeOpportunityNotSupportedExplainer}
{symAlert}
<Button
mx={-2}
size='lg'
colorScheme={errorCopy ? 'red' : 'blue'}
isDisabled={
isTradingActive === false ||
!isThorchainLpDepositEnabled ||
!confirmedQuote ||
!votingPower ||
isVotingPowerLoading ||
!hasEnoughAssetBalance ||
!hasEnoughRuneBalance ||
isApprovalTxPending ||
(isSweepNeededEnabled && isSweepNeeded === undefined) ||
isSweepNeededError ||
isEstimatedPoolAssetFeesDataError ||
isEstimatedRuneFeesDataError ||
bnOrZero(actualAssetDepositAmountCryptoPrecision)
.plus(bnOrZero(actualRuneDepositAmountCryptoPrecision))
.isZero() ||
notEnoughFeeAssetError ||
notEnoughRuneFeeError ||
!walletSupportsOpportunity
}
isLoading={
(poolAssetTxFeeCryptoBaseUnit === undefined && isEstimatedPoolAssetFeesDataLoading) ||
isVotingPowerLoading ||
isInboundAddressesDataLoading ||
isTradingActiveLoading ||
isSmartContractAccountAddressLoading ||
isAllowanceDataLoading ||
isApprovalTxPending ||
(isSweepNeeded === undefined && isSweepNeededLoading) ||
isInboundAddressesDataLoading ||
(runeTxFeeCryptoBaseUnit === undefined && isEstimatedPoolAssetFeesDataLoading)
}
onClick={handleSubmit}
>
{confirmCopy}
</Button>
{isUnsafeQuote && !isUnsafeQuoteNoticeDismissed ? (
<>
<Flex direction='column' gap={2}>
<Alert status='error' width='auto' fontSize='sm' variant='solid'>
<AlertIcon color='red' />
<Stack spacing={0}>
<AlertDescription lineHeight='short'>
{translate('pools.unsafeQuote', {
slippagePercentage: bnOrZero(slippageDecimalPercentage).times(100).toFixed(2),
})}
</AlertDescription>
</Stack>
</Alert>
</Flex>
<Button size='lg' colorScheme='red' onClick={handleAcknowledgeUnsafeQuote}>
<Text translation={'defi.modals.saversVaults.understand'} />
</Button>
</>
) : (
<Button
mx={-2}
size='lg'
colorScheme={errorCopy ? 'red' : 'blue'}
isDisabled={
isTradingActive === false ||
!isThorchainLpDepositEnabled ||
!confirmedQuote ||
!votingPower ||
isVotingPowerLoading ||
!hasEnoughAssetBalance ||
!hasEnoughRuneBalance ||
isApprovalTxPending ||
(isSweepNeededEnabled && isSweepNeeded === undefined) ||
isSweepNeededError ||
isEstimatedPoolAssetFeesDataError ||
isEstimatedRuneFeesDataError ||
bnOrZero(actualAssetDepositAmountCryptoPrecision)
.plus(bnOrZero(actualRuneDepositAmountCryptoPrecision))
.isZero() ||
notEnoughFeeAssetError ||
notEnoughRuneFeeError ||
!walletSupportsOpportunity
}
isLoading={
(poolAssetTxFeeCryptoBaseUnit === undefined && isEstimatedPoolAssetFeesDataLoading) ||
isVotingPowerLoading ||
isInboundAddressesDataLoading ||
isTradingActiveLoading ||
isSmartContractAccountAddressLoading ||
isAllowanceDataLoading ||
isApprovalTxPending ||
(isSweepNeeded === undefined && isSweepNeededLoading) ||
isInboundAddressesDataLoading ||
(runeTxFeeCryptoBaseUnit === undefined && isEstimatedPoolAssetFeesDataLoading)
}
onClick={handleSubmit}
>
{confirmCopy}
</Button>
)}
</CardFooter>
<FeeModal
isOpen={showFeeModal}
Expand Down

0 comments on commit ebc1c99

Please sign in to comment.