From ffbd40dda066dffc35824caff7bbf3d744b803f9 Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Wed, 13 Dec 2023 11:57:05 -0700 Subject: [PATCH] fix: fallback fees for doge (#5853) --- .../src/utxo/UtxoBaseAdapter.ts | 29 +++-------- .../src/utxo/dogecoin/DogecoinChainAdapter.ts | 52 +++++++++++++++++++ .../components/AssetChainDropdown.tsx | 3 +- .../components/AssetSelection.tsx | 11 +++- .../components/TradeInput/TradeInput.tsx | 6 ++- .../Pool/components/Borrow/BorrowInput.tsx | 14 +++-- .../Pool/components/Repay/RepayInput.tsx | 19 +++++-- 7 files changed, 102 insertions(+), 32 deletions(-) diff --git a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts index 72e31d9f2fd..c209eb9a65f 100644 --- a/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts +++ b/packages/chain-adapters/src/utxo/UtxoBaseAdapter.ts @@ -383,38 +383,25 @@ export abstract class UtxoBaseAdapter implements IChainAd throw new Error('UtxoBaseAdapter: failed to get fee data') } - // TODO: when does this happen and why? - if (!data.fast?.satsPerKiloByte || data.fast.satsPerKiloByte < 0) { - data.fast = data.average - } + // ensure higher confirmation speeds never have lower fees than lower confirmation speeds + if (data.slow.satsPerKiloByte > data.average.satsPerKiloByte) + data.average.satsPerKiloByte = data.slow.satsPerKiloByte + if (data.average.satsPerKiloByte > data.fast.satsPerKiloByte) + data.fast.satsPerKiloByte = data.average.satsPerKiloByte const utxos = await this.providers.http.getUtxos({ pubkey }) const utxoSelectInput = { from, to, value, opReturnData, utxos, sendMax } // We have to round because coinselect library uses sats per byte which cant be decimals - const fastPerByte = String(Math.round(data.fast.satsPerKiloByte / 1024)) - const averagePerByte = String(Math.round(data.average.satsPerKiloByte / 1024)) - const slowPerByte = String(Math.round(data.slow.satsPerKiloByte / 1024)) + const fastPerByte = String(Math.round(data.fast.satsPerKiloByte / 1000)) + const averagePerByte = String(Math.round(data.average.satsPerKiloByte / 1000)) + const slowPerByte = String(Math.round(data.slow.satsPerKiloByte / 1000)) const { fee: fastFee } = utxoSelect({ ...utxoSelectInput, satoshiPerByte: fastPerByte }) const { fee: averageFee } = utxoSelect({ ...utxoSelectInput, satoshiPerByte: averagePerByte }) const { fee: slowFee } = utxoSelect({ ...utxoSelectInput, satoshiPerByte: slowPerByte }) - // Special, temporary case for DOGE to provide a workable fee value when the node is struggling - const isDoge = pubkey.startsWith('dgub') - const allFeesDefined = - fastFee !== undefined && averageFee !== undefined && slowFee !== undefined - if (isDoge && !allFeesDefined) { - const satoshiPerByte = '20000' - const fee = utxoSelect({ ...utxoSelectInput, satoshiPerByte }).fee - return { - fast: { txFee: String(fee), chainSpecific: { satoshiPerByte } }, - average: { txFee: String(fee), chainSpecific: { satoshiPerByte } }, - slow: { txFee: String(fee), chainSpecific: { satoshiPerByte } }, - } as FeeDataEstimate - } - return { fast: { txFee: String(fastFee), chainSpecific: { satoshiPerByte: fastPerByte } }, average: { txFee: String(averageFee), chainSpecific: { satoshiPerByte: averagePerByte } }, diff --git a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.ts b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.ts index 3bae718ddf7..47b3c4bf149 100644 --- a/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.ts +++ b/packages/chain-adapters/src/utxo/dogecoin/DogecoinChainAdapter.ts @@ -4,9 +4,11 @@ import type { BIP44Params } from '@shapeshiftoss/types' import { KnownChainIds, UtxoAccountType } from '@shapeshiftoss/types' import * as unchained from '@shapeshiftoss/unchained-client' +import type { FeeDataEstimate, GetFeeDataInput } from '../../types' import { ChainAdapterDisplayName } from '../../types' import type { ChainAdapterArgs } from '../UtxoBaseAdapter' import { UtxoBaseAdapter } from '../UtxoBaseAdapter' +import { utxoSelect } from '../utxoSelect' const SUPPORTED_CHAIN_IDS = [KnownChainIds.DogecoinMainnet] const DEFAULT_CHAIN_ID = KnownChainIds.DogecoinMainnet @@ -54,4 +56,54 @@ export class ChainAdapter extends UtxoBaseAdapter getFeeAssetId(): AssetId { return this.assetId } + + async getFeeData({ + to, + value, + chainSpecific: { from, pubkey, opReturnData }, + sendMax = false, + }: GetFeeDataInput): Promise< + FeeDataEstimate + > { + if (!to) throw new Error('to is required') + if (!value) throw new Error('value is required') + if (!pubkey) throw new Error('pubkey is required') + + const { fast, average, slow } = await this.providers.http.getNetworkFees() + + if (!(fast?.satsPerKiloByte && average?.satsPerKiloByte && slow?.satsPerKiloByte)) { + throw new Error('UtxoBaseAdapter: failed to get fee data') + } + + // sane default for invalid fee data from the node + // see: https://github.com/dogecoin/dogecoin/issues/3385 + if (fast.satsPerKiloByte <= 0) fast.satsPerKiloByte = 500000000 // 5 DOGE per kB + if (average.satsPerKiloByte <= 0) average.satsPerKiloByte = 100000000 // 1 DOGE per kB + if (slow.satsPerKiloByte <= 0) slow.satsPerKiloByte = 50000000 // .5 DOGE per kB + + // ensure higher confirmation speeds never have lower fees than lower confirmation speeds + if (slow.satsPerKiloByte > average.satsPerKiloByte) + average.satsPerKiloByte = slow.satsPerKiloByte + if (average.satsPerKiloByte > fast.satsPerKiloByte) + fast.satsPerKiloByte = average.satsPerKiloByte + + const utxos = await this.providers.http.getUtxos({ pubkey }) + + const utxoSelectInput = { from, to, value, opReturnData, utxos, sendMax } + + // We have to round because coinselect library uses sats per byte which cant be decimals + const fastPerByte = String(Math.round(fast.satsPerKiloByte / 1000)) + const averagePerByte = String(Math.round(average.satsPerKiloByte / 1000)) + const slowPerByte = String(Math.round(slow.satsPerKiloByte / 1000)) + + const { fee: fastFee } = utxoSelect({ ...utxoSelectInput, satoshiPerByte: fastPerByte }) + const { fee: averageFee } = utxoSelect({ ...utxoSelectInput, satoshiPerByte: averagePerByte }) + const { fee: slowFee } = utxoSelect({ ...utxoSelectInput, satoshiPerByte: slowPerByte }) + + return { + fast: { txFee: String(fastFee), chainSpecific: { satoshiPerByte: fastPerByte } }, + average: { txFee: String(averageFee), chainSpecific: { satoshiPerByte: averagePerByte } }, + slow: { txFee: String(slowFee), chainSpecific: { satoshiPerByte: slowPerByte } }, + } + } } diff --git a/src/components/MultiHopTrade/components/AssetChainDropdown.tsx b/src/components/MultiHopTrade/components/AssetChainDropdown.tsx index a00ce9238dd..d1d3ae96463 100644 --- a/src/components/MultiHopTrade/components/AssetChainDropdown.tsx +++ b/src/components/MultiHopTrade/components/AssetChainDropdown.tsx @@ -94,7 +94,8 @@ export const AssetChainDropdown: React.FC = ({ borderRadius='full' color='text.base' isDisabled - variant='ghost' + isLoading={isLoading} + variant={!isLoading ? 'ghost' : undefined} _disabled={disabled} _hover={hover} {...buttonProps} diff --git a/src/components/MultiHopTrade/components/AssetSelection.tsx b/src/components/MultiHopTrade/components/AssetSelection.tsx index c1e7304bef5..e052d0f61ed 100644 --- a/src/components/MultiHopTrade/components/AssetSelection.tsx +++ b/src/components/MultiHopTrade/components/AssetSelection.tsx @@ -31,6 +31,7 @@ const TradeAssetAwaitingAsset = () => { type TradeAssetSelectProps = { assetId?: AssetId isReadOnly?: boolean + isLoading: boolean onAssetClick?: () => void onAssetChange: (asset: Asset) => void } @@ -40,11 +41,16 @@ export const TradeAssetSelectWithAsset: React.FC = ({ onAssetChange, assetId, isReadOnly, + isLoading, }) => { const assets = useAppSelector(selectAssets) const asset = useAppSelector(state => selectAssetById(state, assetId ?? '')) - const { data, isLoading, isError } = useGetRelatedAssetIdsQuery(assetId ?? '') + const { + data, + isLoading: isRelatedAssetsLoading, + isError, + } = useGetRelatedAssetIdsQuery(assetId ?? '') const handleAssetChange = useCallback( (assetId: AssetId) => { @@ -80,6 +86,7 @@ export const TradeAssetSelectWithAsset: React.FC = ({ isDisabled={isReadOnly} _disabled={disabledStyle} rightIcon={rightIcon} + isLoading={isLoading || isRelatedAssetsLoading} > {icon} {asset?.symbol} @@ -89,7 +96,7 @@ export const TradeAssetSelectWithAsset: React.FC = ({ assetIds={data} assetId={assetId} onClick={handleAssetChange} - isLoading={isLoading} + isLoading={isLoading || isRelatedAssetsLoading} isError={isError} /> diff --git a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx index 7a8c9956bd8..0c54b7ddb8b 100644 --- a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx @@ -557,9 +557,10 @@ export const TradeInput = memo(() => { assetId={sellAsset.assetId} onAssetClick={handleSellAssetClick} onAssetChange={setSellAsset} + isLoading={isSupportedAssetsLoading} /> ), - [handleSellAssetClick, sellAsset.assetId, setSellAsset], + [handleSellAssetClick, isSupportedAssetsLoading, sellAsset.assetId, setSellAsset], ) const buyTradeAssetSelect = useMemo( @@ -568,9 +569,10 @@ export const TradeInput = memo(() => { assetId={buyAsset.assetId} onAssetClick={handleBuyAssetClick} onAssetChange={setBuyAsset} + isLoading={isSupportedAssetsLoading} /> ), - [buyAsset.assetId, handleBuyAssetClick, setBuyAsset], + [buyAsset.assetId, handleBuyAssetClick, isSupportedAssetsLoading, setBuyAsset], ) return ( diff --git a/src/pages/Lending/Pool/components/Borrow/BorrowInput.tsx b/src/pages/Lending/Pool/components/Borrow/BorrowInput.tsx index 8ac7cd8ee33..5c0fc45f219 100644 --- a/src/pages/Lending/Pool/components/Borrow/BorrowInput.tsx +++ b/src/pages/Lending/Pool/components/Borrow/BorrowInput.tsx @@ -95,7 +95,8 @@ export const BorrowInput = ({ const translate = useTranslate() const history = useHistory() - const { data: borrowAssets } = useLendingSupportedAssets({ type: 'borrow' }) + const { data: borrowAssets, isLoading: isLendingSupportedAssetsLoading } = + useLendingSupportedAssets({ type: 'borrow' }) const collateralAsset = useAppSelector(state => selectAssetById(state, collateralAssetId)) @@ -346,9 +347,10 @@ export const BorrowInput = ({ onAssetClick={noop} onAssetChange={handleAssetChange} isReadOnly + isLoading={isLendingSupportedAssetsLoading} /> ) - }, [collateralAssetId, handleAssetChange]) + }, [collateralAssetId, handleAssetChange, isLendingSupportedAssetsLoading]) const borrowAssetSelectComponent = useMemo(() => { return ( @@ -356,9 +358,15 @@ export const BorrowInput = ({ assetId={borrowAsset?.assetId ?? ''} onAssetClick={handleBorrowAssetClick} onAssetChange={handleAssetChange} + isLoading={isLendingSupportedAssetsLoading} /> ) - }, [borrowAsset?.assetId, handleAssetChange, handleBorrowAssetClick]) + }, [ + borrowAsset?.assetId, + handleAssetChange, + handleBorrowAssetClick, + isLendingSupportedAssetsLoading, + ]) const quoteErrorTranslation = useMemo(() => { if (_isSmartContractAddress) return 'trade.errors.smartContractWalletNotSupported' diff --git a/src/pages/Lending/Pool/components/Repay/RepayInput.tsx b/src/pages/Lending/Pool/components/Repay/RepayInput.tsx index a088fc2e023..36cde195c0c 100644 --- a/src/pages/Lending/Pool/components/Repay/RepayInput.tsx +++ b/src/pages/Lending/Pool/components/Repay/RepayInput.tsx @@ -126,7 +126,8 @@ export const RepayInput = ({ const swapIcon = useMemo(() => , []) - const { data: lendingSupportedAssets } = useLendingSupportedAssets({ type: 'borrow' }) + const { data: lendingSupportedAssets, isLoading: isLendingSupportedAssetsLoading } = + useLendingSupportedAssets({ type: 'borrow' }) useEffect(() => { if (!(lendingSupportedAssets && collateralAsset)) return @@ -159,9 +160,15 @@ export const RepayInput = ({ // Users have the possibility to repay in any supported asset, not only their collateral/borrowed asset // https://docs.thorchain.org/thorchain-finance/lending#loan-repayment-closeflow isReadOnly={false} + isLoading={isLendingSupportedAssetsLoading} /> ) - }, [handleAssetChange, handleRepaymentAssetClick, repaymentAsset?.assetId]) + }, [ + handleAssetChange, + handleRepaymentAssetClick, + isLendingSupportedAssetsLoading, + repaymentAsset?.assetId, + ]) const collateralAssetSelectComponent = useMemo(() => { return ( @@ -170,9 +177,15 @@ export const RepayInput = ({ onAssetClick={handleRepaymentAssetClick} onAssetChange={handleAssetChange} isReadOnly + isLoading={isLendingSupportedAssetsLoading} /> ) - }, [collateralAssetId, handleAssetChange, handleRepaymentAssetClick]) + }, [ + collateralAssetId, + handleAssetChange, + handleRepaymentAssetClick, + isLendingSupportedAssetsLoading, + ]) const handleSeenNotice = useCallback(() => setSeenNotice(true), [])