From d404b27325651c8ce24dd92ec87757aae79c0c85 Mon Sep 17 00:00:00 2001 From: woody <125113430+woodenfurniture@users.noreply.github.com> Date: Thu, 21 Nov 2024 20:32:35 +1100 Subject: [PATCH 1/2] feat: hide native assets in asset selection modals for limit orders (#8121) --- .../AssetSelection/AssetSelection.tsx | 7 +++- .../AssetChainDropdown/AssetChainDropdown.tsx | 19 +++++++---- .../TradeAssetSearchModal.tsx | 7 ++++ .../components/LimitOrderBuyAsset.tsx | 18 +++++++++-- .../LimitOrder/components/LimitOrderInput.tsx | 32 ++++++++++++++++++- .../SharedTradeInput/SharedTradeInputBody.tsx | 18 ++++++++--- .../components/TradeInput/TradeInput.tsx | 16 +++++----- .../TradeAssetSearch/TradeAssetSearch.tsx | 31 ++++++++++++++---- .../components/SearchTermAssetList.tsx | 17 +++++++--- src/state/apis/limit-orders/limitOrderApi.ts | 8 +++-- 10 files changed, 137 insertions(+), 36 deletions(-) diff --git a/src/components/AssetSelection/AssetSelection.tsx b/src/components/AssetSelection/AssetSelection.tsx index 7c95678fc21..00ab1a0ae23 100644 --- a/src/components/AssetSelection/AssetSelection.tsx +++ b/src/components/AssetSelection/AssetSelection.tsx @@ -1,7 +1,7 @@ import { ChevronDownIcon } from '@chakra-ui/icons' import type { ButtonProps, FlexProps } from '@chakra-ui/react' import { Flex } from '@chakra-ui/react' -import type { AssetId } from '@shapeshiftoss/caip' +import type { AssetId, ChainId } from '@shapeshiftoss/caip' import type { Asset } from '@shapeshiftoss/types' import { memo, useCallback, useMemo } from 'react' import { Text } from 'components/Text' @@ -17,6 +17,7 @@ type TradeAssetSelectBaseProps = { isLoading?: boolean buttonProps?: ButtonProps onlyConnectedChains: boolean + chainIdFilterPredicate?: (chainId: ChainId) => boolean } & FlexProps type TradeAssetSelectReadonlyProps = { @@ -43,6 +44,7 @@ export const TradeAssetSelect: React.FC = memo(props => { isLoading, onlyConnectedChains, buttonProps, + chainIdFilterPredicate, flexProps, } = useMemo(() => { const { @@ -54,6 +56,7 @@ export const TradeAssetSelect: React.FC = memo(props => { isLoading, onlyConnectedChains, buttonProps, + chainIdFilterPredicate, ...flexProps } = props return { @@ -65,6 +68,7 @@ export const TradeAssetSelect: React.FC = memo(props => { isLoading, onlyConnectedChains, buttonProps, + chainIdFilterPredicate, flexProps, } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -104,6 +108,7 @@ export const TradeAssetSelect: React.FC = memo(props => { buttonProps={buttonProps} rightIcon={rightIcon} onlyConnectedChains={onlyConnectedChains} + chainIdFilterPredicate={chainIdFilterPredicate} /> ) diff --git a/src/components/AssetSelection/components/AssetChainDropdown/AssetChainDropdown.tsx b/src/components/AssetSelection/components/AssetChainDropdown/AssetChainDropdown.tsx index a545afac03e..c62abf96dca 100644 --- a/src/components/AssetSelection/components/AssetChainDropdown/AssetChainDropdown.tsx +++ b/src/components/AssetSelection/components/AssetChainDropdown/AssetChainDropdown.tsx @@ -8,7 +8,7 @@ import { MenuOptionGroup, Tooltip, } from '@chakra-ui/react' -import type { AssetId } from '@shapeshiftoss/caip' +import { type AssetId, type ChainId, fromAssetId } from '@shapeshiftoss/caip' import { memo, useCallback, useMemo } from 'react' import { useTranslate } from 'react-polyglot' import { getStyledMenuButtonProps } from 'components/AssetSelection/helpers' @@ -30,6 +30,7 @@ type AssetChainDropdownProps = { isError?: boolean onlyConnectedChains: boolean isDisabled?: boolean + chainIdFilterPredicate?: (chainId: ChainId) => boolean } const flexProps = { @@ -46,6 +47,7 @@ export const AssetChainDropdown: React.FC = memo( isLoading, onChangeAsset, onlyConnectedChains, + chainIdFilterPredicate, }) => { const { state: { wallet }, @@ -63,14 +65,19 @@ export const AssetChainDropdown: React.FC = memo( ) const filteredRelatedAssetIds = useMemo(() => { - if (!assetIds?.length) return relatedAssetIds - return relatedAssetIds.filter(relatedAssetId => assetIds.includes(relatedAssetId)) - }, [assetIds, relatedAssetIds]) + const filteredRelatedAssetIds = relatedAssetIds.filter(assetId => { + const { chainId } = fromAssetId(assetId) + return chainIdFilterPredicate?.(chainId) ?? true + }) + if (!assetIds?.length) return filteredRelatedAssetIds + return filteredRelatedAssetIds.filter(relatedAssetId => assetIds.includes(relatedAssetId)) + }, [assetIds, chainIdFilterPredicate, relatedAssetIds]) const renderedChains = useMemo(() => { if (!assetId) return null return filteredRelatedAssetIds.map(relatedAssetId => { - const isSupported = wallet && isAssetSupportedByWallet(relatedAssetId, wallet) + const isSupported = + !onlyConnectedChains || (wallet && isAssetSupportedByWallet(relatedAssetId, wallet)) return ( @@ -78,7 +85,7 @@ export const AssetChainDropdown: React.FC = memo( ) }) - }, [assetId, filteredRelatedAssetIds, wallet]) + }, [assetId, filteredRelatedAssetIds, onlyConnectedChains, wallet]) const handleChangeAsset = useCallback( (value: string | string[]) => { diff --git a/src/components/Modals/TradeAssetSearch/TradeAssetSearchModal.tsx b/src/components/Modals/TradeAssetSearch/TradeAssetSearchModal.tsx index a69a65504b0..fd694f59748 100644 --- a/src/components/Modals/TradeAssetSearch/TradeAssetSearchModal.tsx +++ b/src/components/Modals/TradeAssetSearch/TradeAssetSearchModal.tsx @@ -1,3 +1,4 @@ +import type { ChainId } from '@shapeshiftoss/caip' import type { Asset } from '@shapeshiftoss/types' import type { FC } from 'react' import { memo, useCallback } from 'react' @@ -16,6 +17,8 @@ import { useModal } from 'hooks/useModal/useModal' export type TradeAssetSearchModalProps = TradeAssetSearchProps & { title?: string onAssetClick: Required['onAssetClick'] + assetFilterPredicate?: (asset: Asset) => boolean + chainIdFilterPredicate?: (chainId: ChainId) => boolean } type AssetSearchModalBaseProps = TradeAssetSearchModalProps & { @@ -29,6 +32,8 @@ export const TradeAssetSearchModalBase: FC = ({ isOpen, allowWalletUnsupportedAssets, title = 'common.selectAsset', + assetFilterPredicate, + chainIdFilterPredicate, }) => { const translate = useTranslate() @@ -50,6 +55,8 @@ export const TradeAssetSearchModalBase: FC = ({ ) diff --git a/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderBuyAsset.tsx b/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderBuyAsset.tsx index be1dcc80e4d..38cfdffb531 100644 --- a/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderBuyAsset.tsx +++ b/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderBuyAsset.tsx @@ -1,5 +1,5 @@ import { Flex, FormLabel, Stack } from '@chakra-ui/react' -import type { AccountId } from '@shapeshiftoss/caip' +import type { AccountId, ChainId } from '@shapeshiftoss/caip' import type { Asset } from '@shapeshiftoss/types' import { bnOrZero } from '@shapeshiftoss/utils' import React, { memo, useCallback, useMemo } from 'react' @@ -35,12 +35,22 @@ export type LimitOrderBuyAssetProps = { asset: Asset accountId?: AccountId isInputtingFiatSellAmount: boolean + assetFilterPredicate: (asset: Asset) => boolean + chainIdFilterPredicate: (chainId: ChainId) => boolean onAccountIdChange: AccountDropdownProps['onChange'] onSetBuyAsset: (asset: Asset) => void } export const LimitOrderBuyAsset: React.FC = memo( - ({ asset, accountId, isInputtingFiatSellAmount, onAccountIdChange, onSetBuyAsset }) => { + ({ + asset, + accountId, + isInputtingFiatSellAmount, + assetFilterPredicate, + chainIdFilterPredicate, + onAccountIdChange, + onSetBuyAsset, + }) => { const translate = useTranslate() const buyAssetSearch = useModal('buyTradeAssetSearch') @@ -79,8 +89,10 @@ export const LimitOrderBuyAsset: React.FC = memo( buyAssetSearch.open({ onAssetClick: onSetBuyAsset, title: 'trade.tradeTo', + assetFilterPredicate, + chainIdFilterPredicate, }) - }, [buyAssetSearch, onSetBuyAsset]) + }, [assetFilterPredicate, buyAssetSearch, chainIdFilterPredicate, onSetBuyAsset]) return ( diff --git a/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx b/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx index f210534b564..0fe345c54a9 100644 --- a/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx +++ b/src/components/MultiHopTrade/components/LimitOrder/components/LimitOrderInput.tsx @@ -1,8 +1,18 @@ import { Divider, Stack } from '@chakra-ui/react' import { skipToken } from '@reduxjs/toolkit/query' +import type { ChainId } from '@shapeshiftoss/caip' import { fromAccountId } from '@shapeshiftoss/caip' import { getDefaultSlippageDecimalPercentageForSwapper, SwapperName } from '@shapeshiftoss/swapper' -import { BigNumber, bn, bnOrZero, fromBaseUnit, toBaseUnit } from '@shapeshiftoss/utils' +import { isNativeEvmAsset } from '@shapeshiftoss/swapper/dist/swappers/utils/helpers/helpers' +import { type Asset } from '@shapeshiftoss/types' +import { + BigNumber, + bn, + bnOrZero, + fromBaseUnit, + isEvmChainId, + toBaseUnit, +} from '@shapeshiftoss/utils' import type { FormEvent } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react' import { useFormContext } from 'react-hook-form' @@ -172,6 +182,18 @@ export const LimitOrderInput = ({ [handleFormSubmit], ) + const sellAssetFilterPredicate = useCallback((asset: Asset) => { + return isEvmChainId(asset.chainId) && !isNativeEvmAsset(asset.assetId) + }, []) + + const buyAssetFilterPredicate = useCallback((asset: Asset) => { + return isEvmChainId(asset.chainId) + }, []) + + const chainIdFilterPredicate = useCallback((chainId: ChainId) => { + return isEvmChainId(chainId) + }, []) + const sellAmountCryptoBaseUnit = useMemo(() => { return toBaseUnit(sellAmountCryptoPrecision, sellAsset.precision) }, [sellAmountCryptoPrecision, sellAsset.precision]) @@ -280,10 +302,13 @@ export const LimitOrderInput = ({ sellAsset={sellAsset} sellAccountId={sellAccountId} onSwitchAssets={switchAssets} + isSwitchAssetsDisabled={isNativeEvmAsset(buyAsset.assetId)} onChangeIsInputtingFiatSellAmount={setIsInputtingFiatSellAmount} onChangeSellAmountCryptoPrecision={setSellAmountCryptoPrecision} setSellAsset={setSellAsset} setSellAccountId={setSellAccountId} + assetFilterPredicate={sellAssetFilterPredicate} + chainIdFilterPredicate={chainIdFilterPredicate} > boolean + chainIdFilterPredicate?: (chainId: ChainId) => boolean onSwitchAssets: () => void onChangeIsInputtingFiatSellAmount: (isInputtingFiatSellAmount: boolean) => void onChangeSellAmountCryptoPrecision: (sellAmountCryptoPrecision: string) => void @@ -49,10 +52,13 @@ export const SharedTradeInputBody = ({ children, isInputtingFiatSellAmount, isLoading, + isSwitchAssetsDisabled, sellAmountCryptoPrecision, sellAmountUserCurrency, sellAsset, sellAccountId, + assetFilterPredicate, + chainIdFilterPredicate, onSwitchAssets, onChangeIsInputtingFiatSellAmount, onChangeSellAmountCryptoPrecision, @@ -108,8 +114,10 @@ export const SharedTradeInputBody = ({ sellAssetSearch.open({ onAssetClick: setSellAsset, title: 'trade.tradeFrom', + assetFilterPredicate, + chainIdFilterPredicate, }) - }, [sellAssetSearch, setSellAsset]) + }, [assetFilterPredicate, chainIdFilterPredicate, sellAssetSearch, setSellAsset]) const sellTradeAssetSelect = useMemo( () => ( @@ -118,17 +126,19 @@ export const SharedTradeInputBody = ({ onAssetClick={handleSellAssetClick} onAssetChange={setSellAsset} onlyConnectedChains={true} + chainIdFilterPredicate={chainIdFilterPredicate} /> ), - [handleSellAssetClick, sellAsset.assetId, setSellAsset], + [handleSellAssetClick, sellAsset.assetId, setSellAsset, chainIdFilterPredicate], ) // disable switching assets if the buy asset isn't supported const shouldDisableSwitchAssets = useMemo(() => { + if (isSwitchAssetsDisabled) return true if (!hasWallet) return false return !walletSupportsBuyAssetChain || isLoading - }, [hasWallet, walletSupportsBuyAssetChain, isLoading]) + }, [hasWallet, isSwitchAssetsDisabled, walletSupportsBuyAssetChain, isLoading]) return ( diff --git a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx index 747b537bb38..9a2df11e3ca 100644 --- a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx @@ -1,7 +1,7 @@ import { isLedger } from '@shapeshiftoss/hdwallet-ledger' import { isArbitrumBridgeTradeQuote } from '@shapeshiftoss/swapper/dist/swappers/ArbitrumBridgeSwapper/getTradeQuote/getTradeQuote' import type { ThorTradeQuote } from '@shapeshiftoss/swapper/dist/swappers/ThorchainSwapper/types' -import type { Asset } from '@shapeshiftoss/types' +import { type Asset } from '@shapeshiftoss/types' import { positiveOrZero } from '@shapeshiftoss/utils' import type { FormEvent } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react' @@ -375,23 +375,23 @@ export const TradeInput = ({ isCompact, tradeInputRef, onChangeTab }: TradeInput buyAmountAfterFeesUserCurrency, buyAsset, buyAssetAccountId, - sellAssetAccountId, + buyTradeAssetSelect, + hasUserEnteredAmount, + inputOutputDifferenceDecimalPercentage, isInputtingFiatSellAmount, isLoading, manualReceiveAddress, - sellAsset, sellAmountCryptoPrecision, sellAmountUserCurrency, - hasUserEnteredAmount, + sellAsset, + sellAssetAccountId, translate, - buyTradeAssetSelect, - inputOutputDifferenceDecimalPercentage, + handleChangeSellAmountCryptoPrecision, + handleIsInputtingFiatSellAmountChange, handleSwitchAssets, setBuyAssetAccountId, setSellAsset, setSellAssetAccountId, - handleIsInputtingFiatSellAmountChange, - handleChangeSellAmountCryptoPrecision, ]) const footerContent = useMemo(() => { diff --git a/src/components/TradeAssetSearch/TradeAssetSearch.tsx b/src/components/TradeAssetSearch/TradeAssetSearch.tsx index c9f0d433fa7..81130391485 100644 --- a/src/components/TradeAssetSearch/TradeAssetSearch.tsx +++ b/src/components/TradeAssetSearch/TradeAssetSearch.tsx @@ -45,11 +45,15 @@ export type TradeAssetSearchProps = { onAssetClick?: (asset: Asset) => void formProps?: BoxProps allowWalletUnsupportedAssets?: boolean + assetFilterPredicate?: (asset: Asset) => boolean + chainIdFilterPredicate?: (chainId: ChainId) => boolean } export const TradeAssetSearch: FC = ({ onAssetClick, formProps, allowWalletUnsupportedAssets, + assetFilterPredicate, + chainIdFilterPredicate, }) => { const { walletInfo } = useWallet().state const hasWallet = useMemo(() => Boolean(walletInfo?.deviceId), [walletInfo?.deviceId]) @@ -105,14 +109,20 @@ export const TradeAssetSearch: FC = ({ const popularAssets = useMemo(() => { const unfilteredPopularAssets = popularAssetsByChainId?.[activeChainId] ?? [] - if (allowWalletUnsupportedAssets || !hasWallet) return unfilteredPopularAssets - return unfilteredPopularAssets.filter(asset => walletConnectedChainIds.includes(asset.chainId)) + const filteredPopularAssets = assetFilterPredicate + ? unfilteredPopularAssets.filter(assetFilterPredicate) + : unfilteredPopularAssets + if (allowWalletUnsupportedAssets || !hasWallet) return filteredPopularAssets + + // TODO: move `allowWalletUnsupportedAssets` into `assetFilterPredicate` + return filteredPopularAssets.filter(asset => walletConnectedChainIds.includes(asset.chainId)) }, [ popularAssetsByChainId, activeChainId, allowWalletUnsupportedAssets, hasWallet, walletConnectedChainIds, + assetFilterPredicate, ]) const quickAccessAssets = useMemo(() => { @@ -139,12 +149,16 @@ export const TradeAssetSearch: FC = ({ }, [activeChainId, popularAssets]) const portfolioAssetsSortedByBalanceForChain = useMemo(() => { + const filteredPortfolioAssetsSortedByBalance = assetFilterPredicate + ? portfolioAssetsSortedByBalance.filter(assetFilterPredicate) + : portfolioAssetsSortedByBalance + if (activeChainId === 'All') { - return portfolioAssetsSortedByBalance + return filteredPortfolioAssetsSortedByBalance } - return portfolioAssetsSortedByBalance.filter(asset => asset.chainId === activeChainId) - }, [activeChainId, portfolioAssetsSortedByBalance]) + return filteredPortfolioAssetsSortedByBalance.filter(asset => asset.chainId === activeChainId) + }, [activeChainId, portfolioAssetsSortedByBalance, assetFilterPredicate]) const chainIds: (ChainId | 'All')[] = useMemo(() => { const unsortedChainIds = (() => { @@ -155,10 +169,12 @@ export const TradeAssetSearch: FC = ({ return walletConnectedChainIds })() - const sortedChainIds = sortChainIdsByDisplayName(unsortedChainIds) + const sortedChainIds = sortChainIdsByDisplayName(unsortedChainIds).filter( + chainId => chainIdFilterPredicate?.(chainId) ?? true, + ) return ['All', ...sortedChainIds] - }, [allowWalletUnsupportedAssets, hasWallet, walletConnectedChainIds]) + }, [allowWalletUnsupportedAssets, hasWallet, walletConnectedChainIds, chainIdFilterPredicate]) const quickAccessAssetButtons = useMemo(() => { if (isPopularAssetIdsLoading) { @@ -237,6 +253,7 @@ export const TradeAssetSearch: FC = ({ onAssetClick={handleAssetClick} onImportClick={handleImportIntent} isLoading={isPopularAssetIdsLoading} + assetFilterPredicate={assetFilterPredicate} allowWalletUnsupportedAssets={!hasWallet || allowWalletUnsupportedAssets} /> ) : ( diff --git a/src/components/TradeAssetSearch/components/SearchTermAssetList.tsx b/src/components/TradeAssetSearch/components/SearchTermAssetList.tsx index 7f7574da410..dc3cb5be213 100644 --- a/src/components/TradeAssetSearch/components/SearchTermAssetList.tsx +++ b/src/components/TradeAssetSearch/components/SearchTermAssetList.tsx @@ -23,6 +23,7 @@ export type SearchTermAssetListProps = { activeChainId: ChainId | 'All' searchString: string allowWalletUnsupportedAssets: boolean | undefined + assetFilterPredicate?: (asset: Asset) => boolean onAssetClick: (asset: Asset) => void onImportClick: (asset: Asset) => void } @@ -32,6 +33,7 @@ export const SearchTermAssetList = ({ activeChainId, searchString, allowWalletUnsupportedAssets, + assetFilterPredicate, onAssetClick: handleAssetClick, onImportClick, }: SearchTermAssetListProps) => { @@ -55,16 +57,23 @@ export const SearchTermAssetList = ({ }) const assetsForChain = useMemo(() => { + const _assets = assetFilterPredicate ? assets.filter(assetFilterPredicate) : assets if (activeChainId === 'All') { - if (allowWalletUnsupportedAssets) return assets - return assets.filter(asset => walletConnectedChainIds.includes(asset.chainId)) + if (allowWalletUnsupportedAssets) return _assets + return _assets.filter(asset => walletConnectedChainIds.includes(asset.chainId)) } // Should never happen, but paranoia. if (!allowWalletUnsupportedAssets && !walletConnectedChainIds.includes(activeChainId)) return [] - return assets.filter(asset => asset.chainId === activeChainId) - }, [activeChainId, allowWalletUnsupportedAssets, assets, walletConnectedChainIds]) + return _assets.filter(asset => asset.chainId === activeChainId) + }, [ + activeChainId, + allowWalletUnsupportedAssets, + assets, + walletConnectedChainIds, + assetFilterPredicate, + ]) const customAssets: Asset[] = useMemo( () => diff --git a/src/state/apis/limit-orders/limitOrderApi.ts b/src/state/apis/limit-orders/limitOrderApi.ts index 6a2eff9dd99..321b0f665d8 100644 --- a/src/state/apis/limit-orders/limitOrderApi.ts +++ b/src/state/apis/limit-orders/limitOrderApi.ts @@ -1,5 +1,5 @@ import { createApi } from '@reduxjs/toolkit/dist/query/react' -import type { AssetId } from '@shapeshiftoss/caip' +import type { AssetId, AssetReference } from '@shapeshiftoss/caip' import { type ChainId, fromAssetId } from '@shapeshiftoss/caip' import type { CowSwapQuoteError } from '@shapeshiftoss/swapper' import { @@ -10,10 +10,12 @@ import { getCowswapNetwork, TradeQuoteError, } from '@shapeshiftoss/swapper' +import { COW_SWAP_NATIVE_ASSET_MARKER_ADDRESS } from '@shapeshiftoss/swapper/dist/swappers/CowSwapper/utils/constants' import { getAffiliateAppDataFragmentByChainId, getFullAppData, } from '@shapeshiftoss/swapper/dist/swappers/CowSwapper/utils/helpers/helpers' +import { isNativeEvmAsset } from '@shapeshiftoss/swapper/dist/swappers/utils/helpers/helpers' import type { AxiosError } from 'axios' import axios from 'axios' import { getConfig } from 'config' @@ -81,7 +83,9 @@ export const limitOrderApi = createApi({ const limitOrderQuoteRequest: LimitOrderQuoteRequest = { sellToken: fromAssetId(sellAssetId).assetReference, - buyToken: fromAssetId(buyAssetId).assetReference, + buyToken: !isNativeEvmAsset(buyAssetId) + ? fromAssetId(buyAssetId).assetReference + : (COW_SWAP_NATIVE_ASSET_MARKER_ADDRESS as AssetReference), // TEMP: This type cast is fixed in downstream PR receiver: recipientAddress, sellTokenBalance: CoWSwapSellTokenSource.ERC20, from: sellAccountAddress ?? zeroAddress, // Zero address used to enable quotes without wallet connected From 53916c5eeb4fc64d9118535e35375f96aa415bab Mon Sep 17 00:00:00 2001 From: David Cumps Date: Thu, 21 Nov 2024 11:28:03 +0100 Subject: [PATCH 2/2] fix: chainflip quote time and boost fee (#8150) --- .../swappers/ChainflipSwapper/endpoints.ts | 5 +- ...nflipBaasQuoteEstimatedDurationsSeconds.ts | 79 +++++++++++++++++++ .../models/ChainflipBaasQuoteQuote.ts | 59 ++++++++++++-- .../models/ChainflipBaasQuoteQuoteFee.ts | 12 +-- .../models/ChainflipBaasStatusEgress.ts | 1 + .../swapperApi/getTradeQuote.ts | 9 ++- .../ChainflipSwapper/utils/helpers.ts | 2 +- 7 files changed, 149 insertions(+), 18 deletions(-) create mode 100644 packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteEstimatedDurationsSeconds.ts diff --git a/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts b/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts index b68e0924ce8..abcb44c385b 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/endpoints.ts @@ -16,7 +16,7 @@ import type { UtxoFeeData, } from '../../types' import { isExecutableTradeQuote, isExecutableTradeStep, isToken } from '../../utils' -import { CHAINFLIP_BAAS_COMMISSION } from './constants' +import { CHAINFLIP_BAAS_COMMISSION, CHAINFLIP_BOOST_SWAP_SOURCE } from './constants' import type { ChainflipBaasSwapDepositAddress } from './models/ChainflipBaasSwapDepositAddress' import { getTradeQuote } from './swapperApi/getTradeQuote' import { getTradeRate } from './swapperApi/getTradeRate' @@ -82,6 +82,7 @@ export const chainflipApi: SwapperApi = { minimumPrice, refundAddress: from, commissionBps: serviceCommission, + boostFee: 0, }) if (maybeSwapResponse.isErr()) { @@ -205,6 +206,7 @@ export const chainflipApi: SwapperApi = { minimumPrice, refundAddress: sendAddress, commissionBps: serviceCommission, + boostFee: step.source === CHAINFLIP_BOOST_SWAP_SOURCE ? 10 : 0, }) if (maybeSwapResponse.isErr()) { @@ -277,6 +279,7 @@ export const chainflipApi: SwapperApi = { minimumPrice, refundAddress: from, commissionBps: serviceCommission, + boostFee: 0, }) if (maybeSwapResponse.isErr()) { diff --git a/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteEstimatedDurationsSeconds.ts b/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteEstimatedDurationsSeconds.ts new file mode 100644 index 00000000000..8b55bea72a3 --- /dev/null +++ b/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteEstimatedDurationsSeconds.ts @@ -0,0 +1,79 @@ +// @ts-nocheck +/* tslint:disable */ +/* eslint-disable */ +/** + * Chainflip Broker as a Service + * Run your own Chainflip Broker without any hassle. + * + * The version of the OpenAPI document: v1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + +import { exists, mapValues } from '../runtime'; +/** + * + * @export + * @interface ChainflipBaasQuoteEstimatedDurationsSeconds + */ +export interface ChainflipBaasQuoteEstimatedDurationsSeconds { + /** + * The estimated time for a deposit to be witnessed. + * @type {number} + * @memberof ChainflipBaasQuoteEstimatedDurationsSeconds + */ + readonly deposit?: number; + /** + * The estimated time for a swap to be fully executed. + * @type {number} + * @memberof ChainflipBaasQuoteEstimatedDurationsSeconds + */ + readonly swap?: number; + /** + * The estimated time until the output transaction is included in a block. + * @type {number} + * @memberof ChainflipBaasQuoteEstimatedDurationsSeconds + */ + readonly egress?: number; +} + +/** + * Check if a given object implements the ChainflipBaasQuoteEstimatedDurationsSeconds interface. + */ +export function instanceOfChainflipBaasQuoteEstimatedDurationsSeconds(value: object): boolean { + let isInstance = true; + + return isInstance; +} + +export function ChainflipBaasQuoteEstimatedDurationsSecondsFromJSON(json: any): ChainflipBaasQuoteEstimatedDurationsSeconds { + return ChainflipBaasQuoteEstimatedDurationsSecondsFromJSONTyped(json, false); +} + +export function ChainflipBaasQuoteEstimatedDurationsSecondsFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChainflipBaasQuoteEstimatedDurationsSeconds { + if ((json === undefined) || (json === null)) { + return json; + } + return { + + 'deposit': !exists(json, 'deposit') ? undefined : json['deposit'], + 'swap': !exists(json, 'swap') ? undefined : json['swap'], + 'egress': !exists(json, 'egress') ? undefined : json['egress'], + }; +} + +export function ChainflipBaasQuoteEstimatedDurationsSecondsToJSON(value?: ChainflipBaasQuoteEstimatedDurationsSeconds | null): any { + if (value === undefined) { + return undefined; + } + if (value === null) { + return null; + } + return { + + }; +} + diff --git a/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteQuote.ts b/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteQuote.ts index 24ce7d7e52a..cc29f01215f 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteQuote.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteQuote.ts @@ -20,6 +20,12 @@ import { ChainflipBaasQuoteBoostQuoteFromJSONTyped, ChainflipBaasQuoteBoostQuoteToJSON, } from './ChainflipBaasQuoteBoostQuote'; +import type { ChainflipBaasQuoteEstimatedDurationsSeconds } from './ChainflipBaasQuoteEstimatedDurationsSeconds'; +import { + ChainflipBaasQuoteEstimatedDurationsSecondsFromJSON, + ChainflipBaasQuoteEstimatedDurationsSecondsFromJSONTyped, + ChainflipBaasQuoteEstimatedDurationsSecondsToJSON, +} from './ChainflipBaasQuoteEstimatedDurationsSeconds'; import type { ChainflipBaasQuotePoolInfo } from './ChainflipBaasQuotePoolInfo'; import { ChainflipBaasQuotePoolInfoFromJSON, @@ -63,6 +69,24 @@ export interface ChainflipBaasQuoteQuote { * @memberof ChainflipBaasQuoteQuote */ readonly ingressAmountNative?: string; + /** + * The asset used as intermediary. + * @type {string} + * @memberof ChainflipBaasQuoteQuote + */ + intermediateAsset?: string | null; + /** + * The amount used as intermediary. + * @type {number} + * @memberof ChainflipBaasQuoteQuote + */ + readonly intermediateAmount?: number | null; + /** + * The amount used as intermediary in native units. + * @type {string} + * @memberof ChainflipBaasQuoteQuote + */ + readonly intermediateAmountNative?: string | null; /** * The asset to receive. * @type {string} @@ -87,6 +111,12 @@ export interface ChainflipBaasQuoteQuote { * @memberof ChainflipBaasQuoteQuote */ readonly includedFees?: Array; + /** + * Recommended slippage percentage to use when calculating minimum price. + * @type {number} + * @memberof ChainflipBaasQuoteQuote + */ + readonly recommendedSlippageTolerancePercent?: number; /** * A warning in case liquidity is low and there is a risk of high slippage. * @type {boolean} @@ -98,25 +128,37 @@ export interface ChainflipBaasQuoteQuote { * @type {Array} * @memberof ChainflipBaasQuoteQuote */ - poolInfo?: Array; + readonly poolInfo?: Array; /** * The estimated time the swap will take. * @type {number} * @memberof ChainflipBaasQuoteQuote */ readonly estimatedDurationSeconds?: number; + /** + * + * @type {ChainflipBaasQuoteEstimatedDurationsSeconds} + * @memberof ChainflipBaasQuoteQuote + */ + estimatedDurationsSeconds?: ChainflipBaasQuoteEstimatedDurationsSeconds; + /** + * The estimated price in the destination asset for 1 unit of the source asset. Used for slippage calculations. + * @type {number} + * @memberof ChainflipBaasQuoteQuote + */ + readonly estimatedPrice?: number; /** * The number of "sub-swaps" to perform for a DCA swap. * @type {number} * @memberof ChainflipBaasQuoteQuote */ - chunkIntervalBlocks?: number | null; + readonly chunkIntervalBlocks?: number | null; /** * The delay between the "sub-swaps" of a DCA swap in number of blocks. * @type {number} * @memberof ChainflipBaasQuoteQuote */ - numberOfChunks?: number | null; + readonly numberOfChunks?: number | null; /** * * @type {ChainflipBaasQuoteBoostQuote} @@ -159,13 +201,19 @@ export function ChainflipBaasQuoteQuoteFromJSONTyped(json: any, ignoreDiscrimina 'ingressAsset': !exists(json, 'ingressAsset') ? undefined : json['ingressAsset'], 'ingressAmount': !exists(json, 'ingressAmount') ? undefined : json['ingressAmount'], 'ingressAmountNative': !exists(json, 'ingressAmountNative') ? undefined : json['ingressAmountNative'], + 'intermediateAsset': !exists(json, 'intermediateAsset') ? undefined : json['intermediateAsset'], + 'intermediateAmount': !exists(json, 'intermediateAmount') ? undefined : json['intermediateAmount'], + 'intermediateAmountNative': !exists(json, 'intermediateAmountNative') ? undefined : json['intermediateAmountNative'], 'egressAsset': !exists(json, 'egressAsset') ? undefined : json['egressAsset'], 'egressAmount': !exists(json, 'egressAmount') ? undefined : json['egressAmount'], 'egressAmountNative': !exists(json, 'egressAmountNative') ? undefined : json['egressAmountNative'], 'includedFees': !exists(json, 'includedFees') ? undefined : ((json['includedFees'] as Array).map(ChainflipBaasQuoteQuoteFeeFromJSON)), + 'recommendedSlippageTolerancePercent': !exists(json, 'recommendedSlippageTolerancePercent') ? undefined : json['recommendedSlippageTolerancePercent'], 'lowLiquidityWarning': !exists(json, 'lowLiquidityWarning') ? undefined : json['lowLiquidityWarning'], 'poolInfo': !exists(json, 'poolInfo') ? undefined : ((json['poolInfo'] as Array).map(ChainflipBaasQuotePoolInfoFromJSON)), 'estimatedDurationSeconds': !exists(json, 'estimatedDurationSeconds') ? undefined : json['estimatedDurationSeconds'], + 'estimatedDurationsSeconds': !exists(json, 'estimatedDurationsSeconds') ? undefined : ChainflipBaasQuoteEstimatedDurationsSecondsFromJSON(json['estimatedDurationsSeconds']), + 'estimatedPrice': !exists(json, 'estimatedPrice') ? undefined : json['estimatedPrice'], 'chunkIntervalBlocks': !exists(json, 'chunkIntervalBlocks') ? undefined : json['chunkIntervalBlocks'], 'numberOfChunks': !exists(json, 'numberOfChunks') ? undefined : json['numberOfChunks'], 'boostQuote': !exists(json, 'boostQuote') ? undefined : ChainflipBaasQuoteBoostQuoteFromJSON(json['boostQuote']), @@ -183,10 +231,9 @@ export function ChainflipBaasQuoteQuoteToJSON(value?: ChainflipBaasQuoteQuote | 'ingressAsset': value.ingressAsset, 'ingressAmount': value.ingressAmount, + 'intermediateAsset': value.intermediateAsset, 'egressAsset': value.egressAsset, - 'poolInfo': value.poolInfo === undefined ? undefined : ((value.poolInfo as Array).map(ChainflipBaasQuotePoolInfoToJSON)), - 'chunkIntervalBlocks': value.chunkIntervalBlocks, - 'numberOfChunks': value.numberOfChunks, + 'estimatedDurationsSeconds': ChainflipBaasQuoteEstimatedDurationsSecondsToJSON(value.estimatedDurationsSeconds), 'boostQuote': ChainflipBaasQuoteBoostQuoteToJSON(value.boostQuote), }; } diff --git a/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteQuoteFee.ts b/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteQuoteFee.ts index 754189852f6..975f4dd1bf3 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteQuoteFee.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasQuoteQuoteFee.ts @@ -25,25 +25,25 @@ export interface ChainflipBaasQuoteQuoteFee { * @type {string} * @memberof ChainflipBaasQuoteQuoteFee */ - type?: ChainflipBaasQuoteQuoteFeeTypeEnum; + readonly type?: ChainflipBaasQuoteQuoteFeeTypeEnum; /** * The asset the fee is processed in. * @type {string} * @memberof ChainflipBaasQuoteQuoteFee */ - asset?: string | null; + readonly asset?: string | null; /** * The fee amount. * @type {number} * @memberof ChainflipBaasQuoteQuoteFee */ - amount?: number; + readonly amount?: number; /** * The fee amount in native units. * @type {string} * @memberof ChainflipBaasQuoteQuoteFee */ - amountNative?: string; + readonly amountNative?: string; } @@ -96,10 +96,6 @@ export function ChainflipBaasQuoteQuoteFeeToJSON(value?: ChainflipBaasQuoteQuote } return { - 'type': value.type, - 'asset': value.asset, - 'amount': value.amount, - 'amountNative': value.amountNative, }; } diff --git a/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasStatusEgress.ts b/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasStatusEgress.ts index 0bc245ee830..ab56435015d 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasStatusEgress.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/models/ChainflipBaasStatusEgress.ts @@ -147,3 +147,4 @@ export function ChainflipBaasStatusEgressToJSON(value?: ChainflipBaasStatusEgres 'failure': ChainflipBaasStatusFailureToJSON(value.failure), }; } + diff --git a/packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts b/packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts index 28f3cfecbdf..c6f0e68f5fb 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/swapperApi/getTradeQuote.ts @@ -292,7 +292,9 @@ export const _getTradeQuote = async ( accountNumber, allowanceContract: '0x0', // Chainflip does not use contracts estimatedExecutionTimeMs: - singleQuoteResponse.boostQuote.estimatedDurationSeconds! * 1000, + (singleQuoteResponse.boostQuote.estimatedDurationsSeconds!.deposit! + + singleQuoteResponse.boostQuote.estimatedDurationsSeconds!.swap!) * + 1000, }, ], } @@ -330,7 +332,10 @@ export const _getTradeQuote = async ( sellAsset, accountNumber, allowanceContract: '0x0', // Chainflip does not use contracts - all Txs are sends - estimatedExecutionTimeMs: singleQuoteResponse.estimatedDurationSeconds! * 1000, + estimatedExecutionTimeMs: + (singleQuoteResponse.estimatedDurationsSeconds!.deposit! + + singleQuoteResponse.estimatedDurationsSeconds!.swap!) * + 1000, }, ], } diff --git a/packages/swapper/src/swappers/ChainflipSwapper/utils/helpers.ts b/packages/swapper/src/swappers/ChainflipSwapper/utils/helpers.ts index c8db9aef664..cd2a3955124 100644 --- a/packages/swapper/src/swappers/ChainflipSwapper/utils/helpers.ts +++ b/packages/swapper/src/swappers/ChainflipSwapper/utils/helpers.ts @@ -92,7 +92,7 @@ export const getChainFlipSwap = ({ sourceAsset, destinationAsset, destinationAddress, - boostFee = 10, + boostFee = 0, minimumPrice, refundAddress, retryDurationInBlocks = 10,