-
Notifications
You must be signed in to change notification settings - Fork 182
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
feat: savers remove dangerous withdraw warning #7107
Conversation
2d8cd1c
to
0b9ec69
Compare
@@ -29,7 +29,7 @@ import { fetchHasEnoughBalanceForTxPlusFeesPlusSweep } from 'lib/utils/thorchain | |||
import { BASE_BPS_POINTS } from 'lib/utils/thorchain/constants' | |||
import type { GetThorchainSaversWithdrawQuoteQueryKey } from 'lib/utils/thorchain/hooks/useGetThorchainSaversWithdrawQuoteQuery' | |||
import { | |||
queryFn as getThorchainSaversWithdrawQuoteQueryFn, | |||
fetchThorchainWithdrawQuote, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I.e just a good ol' async fn now, no need to tie it to react-query terminology
const { control, setValue } = methods | ||
|
||
const cryptoAmount = useWatch<WithdrawValues, Field.CryptoAmount>({ | ||
control, | ||
name: Field.CryptoAmount, | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getValues()
is non-reactive and we have to opt-chain on it, v. bad
const hasEnoughStakingBalance = useMemo( | ||
() => bnOrZero(cryptoAmount).lte(amountAvailableCryptoPrecision), | ||
[amountAvailableCryptoPrecision, cryptoAmount], | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exposed at component-level too so we can disable the query in case of no-ops.
This was the explanation for "Amount too low" when inputting more than your staked balance previously. The ratio we were doing to get the bps was resulting in > 10_000 bps, which makes sense since input/staked amount including fees is a positive ratio.
@@ -285,9 +295,9 @@ export const Withdraw: React.FC<WithdrawProps> = ({ accountId, fromAddress, onNe | |||
const fiatAmount = bnOrZero(cryptoAmount).times(assetMarketData.price) | |||
|
|||
setValue(Field.FiatAmount, fiatAmount.toString(), { shouldValidate: true }) | |||
setValue(Field.CryptoAmount, cryptoAmount.toFixed(), { shouldValidate: true }) | |||
setValue(Field.CryptoAmount, cryptoAmount.toFixed(asset.precision), { shouldValidate: true }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was off, and is in fact directly related to this PR even though it doesn't look like it.
Percentage option click wasn't taking into account asset precision, and we were ending up with a value that has possibly more dp than the asset's decimals (e.g: BTC).
This is the reason why trying to input the minimum that's stated still failed validation - one amount had the right amount of decimal places, while the other didn't.
@@ -306,7 +316,7 @@ export const Withdraw: React.FC<WithdrawProps> = ({ accountId, fromAddress, onNe | |||
const safeOutboundFeeInAssetCryptoBaseUnit = useMemo(() => { | |||
if (!outboundFeeInAssetCryptoBaseUnit) return | |||
// Add 5% as as a safety factor since the dust threshold fee is not necessarily going to cut it | |||
return bnOrZero(outboundFeeInAssetCryptoBaseUnit).times(1.05).toFixed() | |||
return bnOrZero(outboundFeeInAssetCryptoBaseUnit).times(1.05).toFixed(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other reason why inputing exactly the minimum amount (or just around) would still fail validation - a base unit amount should have no decimal places, and this one had some.
if (withdrawAmountCryptoPrecision.gt(amountAvailableCryptoPrecision)) | ||
return 'common.insufficientFunds' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't run the query and early bail if the withdraw amount is greater than the staked balance + rewards
@@ -332,7 +345,8 @@ export const Withdraw: React.FC<WithdrawProps> = ({ accountId, fromAddress, onNe | |||
await queryClient | |||
.fetchQuery({ | |||
queryKey: thorchainSaversWithdrawQuoteQueryKey, | |||
queryFn: getThorchainSaversWithdrawQuoteQueryFn, | |||
queryFn: () => | |||
fetchThorchainWithdrawQuote({ asset, accountId, amountCryptoBaseUnit }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, with the takeaways from react-query type-safety, we're not using a magic queryFn
which leverages the queryKey, but a good ol' async fn which is type-safe
const hasValidStakingBalance = | ||
amountAvailableFiat.gt(0) && valueCryptoPrecision.gt(0) && amountAvailableFiat.gte(value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Early bails if we don't have enough staking balance, regardless of whether this is an UTXO withdraw or not
...roviders/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Withdraw.tsx
Outdated
Show resolved
Hide resolved
src/lib/utils/thorchain/balance.ts
Outdated
getThorchainSaversWithdrawQuoteQueryFn({ | ||
asset, | ||
accountId, | ||
amountCryptoBaseUnit: withdrawAmountCryptoBaseUnit, | ||
}), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, with the takeaways from react-query type-safety, we're not using a magic queryFn
which leverages the queryKey, but a good ol' async fn which is type-safe
export const fetchThorchainWithdrawQuote = async ({ | ||
asset, | ||
accountId, | ||
amountCryptoBaseUnit, | ||
withdrawBps, | ||
}: FetchThorchainWithdrawQuoteInput) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note, with the takeaways from react-query type-safety, we're not using a magic queryFn
which leverages the queryKey, but a good ol' async fn which is type-safe
@@ -63,10 +65,12 @@ export const useGetThorchainSaversWithdrawQuoteQuery = ({ | |||
asset, | |||
accountId, | |||
amountCryptoBaseUnit, | |||
enabled = true, | |||
}: { | |||
asset: Asset |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not different than previously, but feels weird to have the hook take different input args than the query fn (missing withdrawBps
). Was thinking we could use the same type as well, but would have to handle undefined accountId
in the query function as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch! Initially we had support for both withdrawBps
and amountCryptoBaseUnit
(one or the other).
However I checked all the consumptions and we never pass in bps (not sure we ever did?), so might as well remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... and a few more things.
Description
See #6707 (comment)
Precisely what the comment says - we can't get into a dangerous withdraw state meaning this is effectively dead code.
Whilst in the house:
Pull Request Type
Issue (if applicable)
Risk
Low but if I got this wrong and we can get in this state, then problems.
Testing
Dangerous withdraws
Confirm I'm not derp and we indeed can't get into a dangerous withdraw state. How to do that, you ask?
close
, filter requests bywithdraw
and click withdraw on a savers positionexpected_amount_out: ''
(meaning fees eat the withdraw and nothing is effectively withdrawn)expected_amount_out: ''
(i.e guaranteed burned fundus), we can't continue past input and this is guarded against with "Minimum: "expected_amount_out
is non-empty in the quote responseMinimum amounts
Amout gt position
Precision shenanigans
Engineering
Operations
Screenshots (if applicable)
USDC on AVAX
BTC
AVAX
Cosmos
Cosmos send endpoint seemingly borked on sends so couldn't go to completion on this one but: