From 99509e1c4fbc675a6c9540ec5388793827dfb35a Mon Sep 17 00:00:00 2001 From: woody <125113430+woodenfurniture@users.noreply.github.com> Date: Tue, 20 Feb 2024 01:51:56 +1100 Subject: [PATCH 01/11] chore: add validation for quotes with incorrect sell amount (#6241) --- .../TradeInput/getQuoteErrorTranslation.ts | 1 + src/state/apis/swapper/helpers/validateTradeQuote.ts | 7 +++++++ src/state/apis/swapper/swapperApi.ts | 12 ++++++++++-- src/state/apis/swapper/types.ts | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/components/MultiHopTrade/components/TradeInput/getQuoteErrorTranslation.ts b/src/components/MultiHopTrade/components/TradeInput/getQuoteErrorTranslation.ts index 6e546a7bb6e..1900b2e75b6 100644 --- a/src/components/MultiHopTrade/components/TradeInput/getQuoteErrorTranslation.ts +++ b/src/components/MultiHopTrade/components/TradeInput/getQuoteErrorTranslation.ts @@ -48,6 +48,7 @@ export const getQuoteErrorTranslation = ( case SwapperTradeQuoteError.InternalError: case TradeQuoteValidationError.QueryFailed: case SwapperTradeQuoteError.QueryFailed: + case TradeQuoteValidationError.QuoteSellAmountInvalid: return 'trade.errors.quoteError' default: assertUnreachable(error) diff --git a/src/state/apis/swapper/helpers/validateTradeQuote.ts b/src/state/apis/swapper/helpers/validateTradeQuote.ts index 075f6bd14a0..8ba75e8ebd3 100644 --- a/src/state/apis/swapper/helpers/validateTradeQuote.ts +++ b/src/state/apis/swapper/helpers/validateTradeQuote.ts @@ -42,6 +42,7 @@ export const validateTradeQuote = async ( isTradingActiveOnSellPool, isTradingActiveOnBuyPool, sendAddress, + inputSellAmountCryptoBaseUnit, }: { swapperName: SwapperName quote: TradeQuote | undefined @@ -51,6 +52,7 @@ export const validateTradeQuote = async ( // TODO(gomes): this should most likely live in the quote alongside the receiveAddress, // summoning @woodenfurniture WRT implications of that, this works for now sendAddress: string | undefined + inputSellAmountCryptoBaseUnit: string }, ): Promise<{ errors: ErrorWithMeta[] @@ -252,6 +254,10 @@ export const validateTradeQuote = async ( return false })() + // ensure the trade is not selling an amount higher than the user input + const invalidQuoteSellAmount = + inputSellAmountCryptoBaseUnit !== firstHop.sellAmountIncludingProtocolFeesCryptoBaseUnit + return { errors: [ !!disableSmartContractSwap && { @@ -300,6 +306,7 @@ export const validateTradeQuote = async ( }, }, feesExceedsSellAmount && { error: TradeQuoteValidationError.SellAmountBelowTradeFee }, + invalidQuoteSellAmount && { error: TradeQuoteValidationError.QuoteSellAmountInvalid }, ...insufficientBalanceForProtocolFeesErrors, ].filter(isTruthy), diff --git a/src/state/apis/swapper/swapperApi.ts b/src/state/apis/swapper/swapperApi.ts index c3ad82a5930..a42c14bae6f 100644 --- a/src/state/apis/swapper/swapperApi.ts +++ b/src/state/apis/swapper/swapperApi.ts @@ -36,8 +36,15 @@ export const swapperApi = createApi({ getTradeQuote: build.query, TradeQuoteRequest>({ queryFn: async (tradeQuoteInput: TradeQuoteRequest, { dispatch, getState }) => { const state = getState() as ReduxState - const { swapperName, sendAddress, receiveAddress, sellAsset, buyAsset, affiliateBps } = - tradeQuoteInput + const { + swapperName, + sendAddress, + receiveAddress, + sellAsset, + buyAsset, + affiliateBps, + sellAmountIncludingProtocolFeesCryptoBaseUnit, + } = tradeQuoteInput const isCrossAccountTrade = sendAddress !== receiveAddress const featureFlags: FeatureFlags = selectFeatureFlags(state) @@ -158,6 +165,7 @@ export const swapperApi = createApi({ isTradingActiveOnSellPool, isTradingActiveOnBuyPool, sendAddress, + inputSellAmountCryptoBaseUnit: sellAmountIncludingProtocolFeesCryptoBaseUnit, }) return { id: quoteSource, diff --git a/src/state/apis/swapper/types.ts b/src/state/apis/swapper/types.ts index 52a1c91290e..c495438b40d 100644 --- a/src/state/apis/swapper/types.ts +++ b/src/state/apis/swapper/types.ts @@ -35,6 +35,7 @@ export enum TradeQuoteValidationError { InsufficientSecondHopFeeAssetBalance = 'InsufficientSecondHopFeeAssetBalance', InsufficientFundsForProtocolFee = 'InsufficientFundsForProtocolFee', IntermediaryAssetNotNotSupportedByWallet = 'IntermediaryAssetNotNotSupportedByWallet', + QuoteSellAmountInvalid = 'QuoteSellAmountInvalid', QueryFailed = 'QueryFailed', UnknownError = 'UnknownError', } From 0d9178622f4c6d92bc33590700000544d7c3e953 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:15:56 -0700 Subject: [PATCH 02/11] =?UTF-8?q?fix:=20remove=20all=20hardcoded=20Swapper?= =?UTF-8?q?Name.Thorchain=20in=20isTradingActive=20ch=E2=80=A6=20(#6237)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TradeInput/components/TradeQuotes/TradeQuote.tsx | 2 ++ .../ThorchainSaversManager/Deposit/components/Confirm.tsx | 2 ++ .../Overview/ThorchainSaversOverview.tsx | 2 ++ .../Withdraw/components/Confirm.tsx | 2 ++ src/pages/ThorChainLP/AvailablePools.tsx | 2 ++ src/pages/ThorChainLP/Pool/Pool.tsx | 2 ++ .../components/AddLiquitity/AddLiquidityInput.tsx | 2 ++ src/pages/ThorChainLP/components/ReusableLpConfirm.tsx | 2 ++ .../components/ReusableLpStatus/TransactionRow.tsx | 2 ++ src/react-queries/hooks/useIsTradingActive.ts | 8 ++++---- src/react-queries/selectors/index.ts | 6 ++++-- src/state/apis/swapper/swapperApi.ts | 4 ++++ 12 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/components/MultiHopTrade/components/TradeInput/components/TradeQuotes/TradeQuote.tsx b/src/components/MultiHopTrade/components/TradeInput/components/TradeQuotes/TradeQuote.tsx index 01911997dd7..0ce93e9ba77 100644 --- a/src/components/MultiHopTrade/components/TradeInput/components/TradeQuotes/TradeQuote.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/components/TradeQuotes/TradeQuote.tsx @@ -66,10 +66,12 @@ export const TradeQuoteLoaded: FC = ({ const sellAsset = useAppSelector(selectInputSellAsset) const { isTradingActive: isTradingActiveOnBuyPool } = useIsTradingActive({ assetId: buyAsset.assetId, + swapperName: quoteData.swapperName, enabled: true, }) const { isTradingActive: isTradingActiveOnSellPool } = useIsTradingActive({ assetId: sellAsset.assetId, + swapperName: quoteData.swapperName, enabled: true, }) diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx index c03c81581bd..2635d197611 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Deposit/components/Confirm.tsx @@ -13,6 +13,7 @@ import { fromAccountId, fromAssetId, toAssetId } from '@shapeshiftoss/caip' import { CONTRACT_INTERACTION, FeeDataKey } from '@shapeshiftoss/chain-adapters' import type { BuildCustomTxInput } from '@shapeshiftoss/chain-adapters/src/evm/types' import { supportsETH } from '@shapeshiftoss/hdwallet-core' +import { SwapperName } from '@shapeshiftoss/swapper' import type { Asset, KnownChainIds } from '@shapeshiftoss/types' import { getConfig } from 'config' import { getOrCreateContractByType } from 'contracts/contractManager' @@ -541,6 +542,7 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { const { isTradingActive, refetch: refetchIsTradingActive } = useIsTradingActive({ assetId, enabled: !!assetId, + swapperName: SwapperName.Thorchain, }) const handleDeposit = useCallback(async () => { diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx index 9b8043cb63c..908c89e1852 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Overview/ThorchainSaversOverview.tsx @@ -14,6 +14,7 @@ import { import { QueryStatus } from '@reduxjs/toolkit/dist/query' import type { AccountId } from '@shapeshiftoss/caip' import { toAssetId } from '@shapeshiftoss/caip' +import { SwapperName } from '@shapeshiftoss/swapper' import type { Asset } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' import BigNumber from 'bignumber.js' @@ -131,6 +132,7 @@ export const ThorchainSaversOverview: React.FC = ({ const { isTradingActive, isLoading: isTradingActiveLoading } = useIsTradingActive({ assetId, enabled: !!assetId, + swapperName: SwapperName.Thorchain, }) useEffect(() => { diff --git a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx index a4dfdfaf5b6..d2f1bfcb4d2 100644 --- a/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx +++ b/src/features/defi/providers/thorchain-savers/components/ThorchainSaversManager/Withdraw/components/Confirm.tsx @@ -15,6 +15,7 @@ import { bchChainId, fromAccountId, fromAssetId, toAssetId } from '@shapeshiftos import { FeeDataKey } from '@shapeshiftoss/chain-adapters' import type { BuildCustomTxInput } from '@shapeshiftoss/chain-adapters/src/evm/types' import { supportsETH } from '@shapeshiftoss/hdwallet-core' +import { SwapperName } from '@shapeshiftoss/swapper' import { getConfig } from 'config' import { getOrCreateContractByType } from 'contracts/contractManager' import { ContractType } from 'contracts/types' @@ -595,6 +596,7 @@ export const Confirm: React.FC = ({ accountId, onNext }) => { const { isTradingActive, refetch: refetchIsTradingActive } = useIsTradingActive({ assetId, enabled: !!assetId, + swapperName: SwapperName.Thorchain, }) const handleConfirm = useCallback(async () => { diff --git a/src/pages/ThorChainLP/AvailablePools.tsx b/src/pages/ThorChainLP/AvailablePools.tsx index e045ab3f720..5668c78c67a 100644 --- a/src/pages/ThorChainLP/AvailablePools.tsx +++ b/src/pages/ThorChainLP/AvailablePools.tsx @@ -1,6 +1,7 @@ import type { GridProps } from '@chakra-ui/react' import { Box, Button, Flex, SimpleGrid, Skeleton, Stack, Tag } from '@chakra-ui/react' import { thorchainAssetId } from '@shapeshiftoss/caip' +import { SwapperName } from '@shapeshiftoss/swapper' import { useQuery } from '@tanstack/react-query' import { useCallback, useMemo } from 'react' import { reactQueries } from 'react-queries' @@ -54,6 +55,7 @@ const PoolButton = ({ pool }: PoolButtonProps) => { const { isTradingActive, isLoading: isTradingActiveLoading } = useIsTradingActive({ assetId: pool?.assetId, enabled: !!pool, + swapperName: SwapperName.Thorchain, }) const handlePoolClick = useCallback(() => { diff --git a/src/pages/ThorChainLP/Pool/Pool.tsx b/src/pages/ThorChainLP/Pool/Pool.tsx index 05b1f18c15f..d6f4b4c45e6 100644 --- a/src/pages/ThorChainLP/Pool/Pool.tsx +++ b/src/pages/ThorChainLP/Pool/Pool.tsx @@ -14,6 +14,7 @@ import { } from '@chakra-ui/react' import type { AccountId, AssetId } from '@shapeshiftoss/caip' import { thorchainAssetId } from '@shapeshiftoss/caip' +import { SwapperName } from '@shapeshiftoss/swapper' import { useQuery } from '@tanstack/react-query' import type { Property } from 'csstype' import React, { useCallback, useMemo } from 'react' @@ -105,6 +106,7 @@ export const Pool = () => { const { isTradingActive, isLoading: isTradingActiveLoading } = useIsTradingActive({ assetId: foundPool?.assetId, enabled: !!foundPool, + swapperName: SwapperName.Thorchain, }) const poolAssetIds = useMemo(() => { diff --git a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx index 2d3f438cc8f..79edb03de12 100644 --- a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx +++ b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx @@ -18,6 +18,7 @@ import { } from '@chakra-ui/react' import type { AccountId, AssetId, ChainId } from '@shapeshiftoss/caip' import { fromAssetId, thorchainAssetId, thorchainChainId } from '@shapeshiftoss/caip' +import { SwapperName } from '@shapeshiftoss/swapper' import type { Asset, KnownChainIds, MarketData } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' @@ -433,6 +434,7 @@ export const AddLiquidityInput: React.FC = ({ const { isTradingActive, isLoading: isTradingActiveLoading } = useIsTradingActive({ assetId: poolAsset?.assetId, enabled: !!poolAsset, + swapperName: SwapperName.Thorchain, }) const poolAccountId = useMemo( diff --git a/src/pages/ThorChainLP/components/ReusableLpConfirm.tsx b/src/pages/ThorChainLP/components/ReusableLpConfirm.tsx index 7b1378552b8..0a39d8969a1 100644 --- a/src/pages/ThorChainLP/components/ReusableLpConfirm.tsx +++ b/src/pages/ThorChainLP/components/ReusableLpConfirm.tsx @@ -13,6 +13,7 @@ import { } from '@chakra-ui/react' import type { AssetId } from '@shapeshiftoss/caip' import { thorchainAssetId } from '@shapeshiftoss/caip' +import { SwapperName } from '@shapeshiftoss/swapper' import type { Asset } from '@shapeshiftoss/types' import React, { useMemo } from 'react' import { FaPlus } from 'react-icons/fa' @@ -184,6 +185,7 @@ export const ReusableLpConfirm: React.FC = ({ const { isTradingActive, isLoading: isTradingActiveLoading } = useIsTradingActive({ assetId: pool?.assetId, enabled: !!pool, + swapperName: SwapperName.Thorchain, }) const confirmCopy = useMemo(() => { diff --git a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx index c1f033ac801..b8c460db866 100644 --- a/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx +++ b/src/pages/ThorChainLP/components/ReusableLpStatus/TransactionRow.tsx @@ -23,6 +23,7 @@ import { type FeeDataEstimate, FeeDataKey, } from '@shapeshiftoss/chain-adapters' +import { SwapperName } from '@shapeshiftoss/swapper' import type { KnownChainIds } from '@shapeshiftoss/types' import { TxStatus } from '@shapeshiftoss/unchained-client' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' @@ -108,6 +109,7 @@ export const TransactionRow: React.FC = ({ } = useIsTradingActive({ assetId: poolAssetId, enabled: !txId, + swapperName: SwapperName.Thorchain, }) const runeAccountId = accountIdsByChainId[thorchainChainId] diff --git a/src/react-queries/hooks/useIsTradingActive.ts b/src/react-queries/hooks/useIsTradingActive.ts index f1431a48703..f52953efb2c 100644 --- a/src/react-queries/hooks/useIsTradingActive.ts +++ b/src/react-queries/hooks/useIsTradingActive.ts @@ -12,9 +12,11 @@ import { thorchainBlockTimeMs } from 'lib/utils/thorchain/constants' export const useIsTradingActive = ({ assetId, enabled, + swapperName, }: { assetId: AssetId | undefined enabled: boolean + swapperName: SwapperName }) => { const [ { @@ -49,15 +51,13 @@ export const useIsTradingActive = ({ }) const isTradingActive = useMemo(() => { - if (isMimirLoading || !mimir || !assetId) return - return selectIsTradingActive({ assetId, inboundAddressResponse: inboundAddressesData, - swapperName: SwapperName.Thorchain, + swapperName, mimir, }) - }, [assetId, inboundAddressesData, isMimirLoading, mimir]) + }, [assetId, inboundAddressesData, mimir, swapperName]) const refetch = useCallback(async () => { const { data: mimirResponse } = await refetchMimir() diff --git a/src/react-queries/selectors/index.ts b/src/react-queries/selectors/index.ts index 463d0d7f13e..3d4ed894960 100644 --- a/src/react-queries/selectors/index.ts +++ b/src/react-queries/selectors/index.ts @@ -28,7 +28,7 @@ export const selectIsTradingActive = ({ }: { assetId: AssetId | undefined swapperName: SwapperName - mimir: Record + mimir: Record | undefined inboundAddressResponse: InboundAddressResponse | undefined }): boolean => { switch (swapperName) { @@ -39,7 +39,9 @@ export const selectIsTradingActive = ({ if (sellAssetIsRune) { // The sell asset is RUNE, there is no inbound address data to check against // Check the HALTTHORCHAIN flag on the mimir endpoint instead - return Object.entries(mimir).some(([k, v]) => k === 'HALTTHORCHAIN' && v === 0) + return Boolean( + mimir && Object.entries(mimir).some(([k, v]) => k === 'HALTTHORCHAIN' && v === 0), + ) } // We have inboundAddressData for the sell asset, check if it is halted diff --git a/src/state/apis/swapper/swapperApi.ts b/src/state/apis/swapper/swapperApi.ts index a42c14bae6f..03bf2d4aee3 100644 --- a/src/state/apis/swapper/swapperApi.ts +++ b/src/state/apis/swapper/swapperApi.ts @@ -1,6 +1,7 @@ import { createApi } from '@reduxjs/toolkit/dist/query/react' import type { ChainId } from '@shapeshiftoss/caip' import { type AssetId, fromAssetId } from '@shapeshiftoss/caip' +import { SwapperName } from '@shapeshiftoss/swapper' import { reactQueries } from 'react-queries' import { selectInboundAddressData, selectIsTradingActive } from 'react-queries/selectors' import { queryClient } from 'context/QueryClientProvider/queryClient' @@ -115,6 +116,9 @@ export const swapperApi = createApi({ const [isTradingActiveOnSellPool, isTradingActiveOnBuyPool] = await Promise.all( [sellAsset.assetId, buyAsset.assetId].map(async assetId => { + // We only need to fetch inbound_address and mimir for THORChain - this avoids overfetching for other swappers + if (swapperName !== SwapperName.Thorchain) return true + const inboundAddresses = await queryClient.fetchQuery({ ...reactQueries.thornode.inboundAddresses(), // Go stale instantly From 61ee963e5a850a6057e59ccf96c00282cf62bfe4 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:30:44 -0700 Subject: [PATCH 03/11] feat: switch ethers JSON-RPC provider to StaticJsonRpcProvider (#6223) --- __mocks__/ethers.ts | 2 +- packages/caip/src/adapters/yearn/utils.ts | 2 +- .../src/evm/bnbsmartchain/parser/bep20.ts | 4 ++-- .../src/evm/ethereum/parser/uniV2.ts | 4 ++-- .../src/evm/ethereum/parser/weth.ts | 4 ++-- packages/unchained-client/src/evm/parser/erc20.ts | 4 ++-- packages/unchained-client/src/evm/parser/index.ts | 4 ++-- packages/unchained-client/src/evm/parser/nft.ts | 4 ++-- scripts/generateAssetData/ethereum/yearnVaults.ts | 2 +- src/lib/ethersProviderSingleton.ts | 6 +++--- src/lib/investor/investor-foxy/api/api.ts | 8 ++++---- src/lib/investor/investor-foxy/foxycli.ts | 4 ++++ src/lib/market-service/foxy/foxy.test.ts | 2 ++ src/lib/market-service/foxy/foxy.ts | 14 ++++++++++++-- src/lib/market-service/market-service-manager.ts | 6 ++++-- src/lib/market-service/market-service.test.ts | 2 ++ .../walletConnectToDapps/hooks/useGetAbi.tsx | 2 +- src/state/apis/foxy/foxyApiSingleton.ts | 2 ++ .../marketServiceManagerSingleton.ts | 3 +++ 19 files changed, 52 insertions(+), 27 deletions(-) diff --git a/__mocks__/ethers.ts b/__mocks__/ethers.ts index fe9e44e6223..1f2617e84dd 100644 --- a/__mocks__/ethers.ts +++ b/__mocks__/ethers.ts @@ -15,7 +15,7 @@ const ethersMock = { ...ethers, providers: { JsonRpcProvider: vi.fn(), - JsonRpcBatchProvider: vi.fn(), + StaticJsonRpcProvider: vi.fn(), }, Contract: vi.fn().mockImplementation(address => ({ decimals: () => { diff --git a/packages/caip/src/adapters/yearn/utils.ts b/packages/caip/src/adapters/yearn/utils.ts index e546b294ec4..65dd160c843 100644 --- a/packages/caip/src/adapters/yearn/utils.ts +++ b/packages/caip/src/adapters/yearn/utils.ts @@ -10,7 +10,7 @@ import { toChainId } from '../../chainId/chainId' import { CHAIN_NAMESPACE, CHAIN_REFERENCE } from '../../constants' const network = 1 // 1 for mainnet -const provider = new ethers.providers.JsonRpcBatchProvider(process.env.REACT_APP_ETHEREUM_NODE_URL) +const provider = new ethers.providers.StaticJsonRpcProvider(process.env.REACT_APP_ETHEREUM_NODE_URL) const yearnSdk = new Yearn(network, { provider }) export const writeFiles = async (data: Record>) => { diff --git a/packages/unchained-client/src/evm/bnbsmartchain/parser/bep20.ts b/packages/unchained-client/src/evm/bnbsmartchain/parser/bep20.ts index ce703cdc7fa..75dd0009853 100644 --- a/packages/unchained-client/src/evm/bnbsmartchain/parser/bep20.ts +++ b/packages/unchained-client/src/evm/bnbsmartchain/parser/bep20.ts @@ -16,11 +16,11 @@ export interface TxMetadata extends BaseTxMetadata { interface ParserArgs { chainId: ChainId - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider } export class Parser implements SubParser { - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider readonly chainId: ChainId readonly abiInterface = new ethers.utils.Interface(bep20) diff --git a/packages/unchained-client/src/evm/ethereum/parser/uniV2.ts b/packages/unchained-client/src/evm/ethereum/parser/uniV2.ts index 02929d69afb..2ced225eb75 100644 --- a/packages/unchained-client/src/evm/ethereum/parser/uniV2.ts +++ b/packages/unchained-client/src/evm/ethereum/parser/uniV2.ts @@ -23,11 +23,11 @@ export interface TxMetadata extends BaseTxMetadata { export interface ParserArgs { chainId: ChainId - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider } export class Parser implements SubParser { - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider readonly chainId: ChainId readonly wethContract: string readonly abiInterface = new ethers.utils.Interface(UNIV2_ABI) diff --git a/packages/unchained-client/src/evm/ethereum/parser/weth.ts b/packages/unchained-client/src/evm/ethereum/parser/weth.ts index 704cc270d00..cbb9458bf15 100644 --- a/packages/unchained-client/src/evm/ethereum/parser/weth.ts +++ b/packages/unchained-client/src/evm/ethereum/parser/weth.ts @@ -16,11 +16,11 @@ export interface TxMetadata extends BaseTxMetadata { export interface ParserArgs { chainId: ChainId - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider } export class Parser implements SubParser { - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider readonly chainId: ChainId readonly wethContract: string readonly abiInterface = new ethers.utils.Interface(WETH_ABI) diff --git a/packages/unchained-client/src/evm/parser/erc20.ts b/packages/unchained-client/src/evm/parser/erc20.ts index 51a17cfa4b6..99f15b9885e 100644 --- a/packages/unchained-client/src/evm/parser/erc20.ts +++ b/packages/unchained-client/src/evm/parser/erc20.ts @@ -16,11 +16,11 @@ export interface TxMetadata extends BaseTxMetadata { interface ParserArgs { chainId: ChainId - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider } export class Parser implements SubParser { - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider readonly chainId: ChainId readonly abiInterface = new ethers.utils.Interface(ERC20_ABI) diff --git a/packages/unchained-client/src/evm/parser/index.ts b/packages/unchained-client/src/evm/parser/index.ts index e05ff2ec060..ada84ef49d1 100644 --- a/packages/unchained-client/src/evm/parser/index.ts +++ b/packages/unchained-client/src/evm/parser/index.ts @@ -26,7 +26,7 @@ export class BaseTransactionParser { assetId: AssetId protected readonly api: Api - protected readonly provider: ethers.providers.JsonRpcBatchProvider + protected readonly provider: ethers.providers.StaticJsonRpcProvider private parsers: SubParser[] = [] @@ -34,7 +34,7 @@ export class BaseTransactionParser { this.chainId = args.chainId this.assetId = args.assetId this.api = args.api - this.provider = new ethers.providers.JsonRpcBatchProvider(args.rpcUrl) + this.provider = new ethers.providers.StaticJsonRpcProvider(args.rpcUrl) } /** diff --git a/packages/unchained-client/src/evm/parser/nft.ts b/packages/unchained-client/src/evm/parser/nft.ts index 09ede124ef7..8acc5997db9 100644 --- a/packages/unchained-client/src/evm/parser/nft.ts +++ b/packages/unchained-client/src/evm/parser/nft.ts @@ -18,13 +18,13 @@ export interface TxMetadata extends BaseTxMetadata { interface ParserArgs { chainId: ChainId api: Api - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider } const supportedTokenTypes = ['ERC721', 'ERC1155', 'BEP721', 'BEP1155'] export class Parser implements SubParser { - provider: ethers.providers.JsonRpcBatchProvider + provider: ethers.providers.StaticJsonRpcProvider readonly chainId: ChainId readonly api: Api diff --git a/scripts/generateAssetData/ethereum/yearnVaults.ts b/scripts/generateAssetData/ethereum/yearnVaults.ts index 3df057030e9..65ebf5ea880 100644 --- a/scripts/generateAssetData/ethereum/yearnVaults.ts +++ b/scripts/generateAssetData/ethereum/yearnVaults.ts @@ -9,7 +9,7 @@ import { ethereum } from '../baseAssets' import { colorMap } from '../colorMap' const network = 1 // 1 for mainnet -const provider = new ethers.providers.JsonRpcBatchProvider(process.env.ETHEREUM_NODE_URL) +const provider = new ethers.providers.StaticJsonRpcProvider(process.env.ETHEREUM_NODE_URL) export const yearnSdk = new Yearn(network, { provider }) const explorerData = { diff --git a/src/lib/ethersProviderSingleton.ts b/src/lib/ethersProviderSingleton.ts index b208071a4a2..a66c8686dd8 100644 --- a/src/lib/ethersProviderSingleton.ts +++ b/src/lib/ethersProviderSingleton.ts @@ -29,13 +29,13 @@ export const rpcUrlByChainId = (chainId: EvmChainId): string => { } } -const ethersProviders: Map = new Map() +const ethersProviders: Map = new Map() export const getEthersProvider = ( chainId: EvmChainId = KnownChainIds.EthereumMainnet, -): providers.JsonRpcBatchProvider => { +): providers.StaticJsonRpcProvider => { if (!ethersProviders.has(chainId)) { - const provider = new providers.JsonRpcBatchProvider(rpcUrlByChainId(chainId)) + const provider = new providers.StaticJsonRpcProvider(rpcUrlByChainId(chainId)) ethersProviders.set(chainId, provider) return provider } else { diff --git a/src/lib/investor/investor-foxy/api/api.ts b/src/lib/investor/investor-foxy/api/api.ts index eb45b5d9d55..c74d047279c 100644 --- a/src/lib/investor/investor-foxy/api/api.ts +++ b/src/lib/investor/investor-foxy/api/api.ts @@ -60,6 +60,7 @@ type EthereumChainReference = export type ConstructorArgs = { adapter: EvmBaseAdapter providerUrl: string + provider: ethers.providers.StaticJsonRpcProvider foxyAddresses: FoxyAddressesType chainReference?: EthereumChainReference } @@ -83,9 +84,8 @@ const TOKE_IPFS_URL = 'https://ipfs.tokemaklabs.xyz/ipfs' export class FoxyApi { public adapter: EvmBaseAdapter - public provider: ethers.providers.JsonRpcBatchProvider + public provider: ethers.providers.StaticJsonRpcProvider private providerUrl: string - public jsonRpcProvider: ethers.providers.JsonRpcBatchProvider private foxyStakingContracts: ethers.Contract[] private liquidityReserveContracts: ethers.Contract[] private readonly ethereumChainReference: ChainReference @@ -96,10 +96,10 @@ export class FoxyApi { providerUrl, foxyAddresses, chainReference = CHAIN_REFERENCE.EthereumMainnet, + provider, }: ConstructorArgs) { this.adapter = adapter - this.provider = new ethers.providers.JsonRpcBatchProvider(providerUrl) - this.jsonRpcProvider = new ethers.providers.JsonRpcBatchProvider(providerUrl) + this.provider = provider this.foxyStakingContracts = foxyAddresses.map( addresses => new ethers.Contract(addresses.staking, foxyStakingAbi, this.provider), ) diff --git a/src/lib/investor/investor-foxy/foxycli.ts b/src/lib/investor/investor-foxy/foxycli.ts index d77cbfb2020..4c717810d98 100644 --- a/src/lib/investor/investor-foxy/foxycli.ts +++ b/src/lib/investor/investor-foxy/foxycli.ts @@ -4,6 +4,7 @@ import { NativeHDWallet } from '@shapeshiftoss/hdwallet-native' import { WithdrawType } from '@shapeshiftoss/types' import * as unchained from '@shapeshiftoss/unchained-client' import dotenv from 'dotenv' +import { ethers } from 'ethers' import readline from 'readline-sync' import { bnOrZero } from 'lib/bignumber/bignumber' @@ -54,6 +55,9 @@ const main = async (): Promise => { adapter: ethChainAdapter, providerUrl: process.env.ARCHIVE_NODE || 'http://127.0.0.1:8545/', foxyAddresses, + provider: new ethers.providers.StaticJsonRpcProvider( + process.env.ARCHIVE_NODE || 'http://127.0.0.1:8545/', + ), }) const accountNumber = 0 diff --git a/src/lib/market-service/foxy/foxy.test.ts b/src/lib/market-service/foxy/foxy.test.ts index 7c8898d27a3..d4a006b1950 100644 --- a/src/lib/market-service/foxy/foxy.test.ts +++ b/src/lib/market-service/foxy/foxy.test.ts @@ -1,5 +1,6 @@ import { HistoryTimeframe } from '@shapeshiftoss/types' import type { AxiosInstance } from 'axios' +import { ethers } from 'ethers' import { beforeAll, describe, expect, it, vi } from 'vitest' import { bn } from 'lib/bignumber/bignumber' @@ -7,6 +8,7 @@ import { FOXY_ASSET_ID, FoxyMarketService } from './foxy' import { fox, mockFoxyMarketData } from './foxyMockData' const foxyMarketService = new FoxyMarketService({ + provider: new ethers.providers.StaticJsonRpcProvider(''), providerUrls: { jsonRpcProviderUrl: 'dummy', unchainedEthereumHttpUrl: '', diff --git a/src/lib/market-service/foxy/foxy.ts b/src/lib/market-service/foxy/foxy.ts index 5b7a7a7ce9a..712cc074629 100644 --- a/src/lib/market-service/foxy/foxy.ts +++ b/src/lib/market-service/foxy/foxy.ts @@ -7,6 +7,7 @@ import type { PriceHistoryArgs, } from '@shapeshiftoss/types' import * as unchained from '@shapeshiftoss/unchained-client' +import type { ethers } from 'ethers' import { foxyAddresses, FoxyApi } from 'lib/investor/investor-foxy' import type { MarketService } from '../api' @@ -19,11 +20,19 @@ const FOXY_ASSET_PRECISION = '18' export class FoxyMarketService extends CoinGeckoMarketService implements MarketService { providerUrls: ProviderUrls - - constructor({ providerUrls }: { providerUrls: ProviderUrls }) { + provider: ethers.providers.StaticJsonRpcProvider + + constructor({ + providerUrls, + provider, + }: { + providerUrls: ProviderUrls + provider: ethers.providers.StaticJsonRpcProvider + }) { super() this.providerUrls = providerUrls + this.provider = provider } async findAll() { @@ -71,6 +80,7 @@ export class FoxyMarketService extends CoinGeckoMarketService implements MarketS adapter: ethChainAdapter, providerUrl: this.providerUrls.jsonRpcProviderUrl, foxyAddresses, + provider: this.provider, }) const tokenContractAddress = foxyAddresses[0].foxy diff --git a/src/lib/market-service/market-service-manager.ts b/src/lib/market-service/market-service-manager.ts index 352b0d0a8bf..736f706a075 100644 --- a/src/lib/market-service/market-service-manager.ts +++ b/src/lib/market-service/market-service-manager.ts @@ -6,6 +6,7 @@ import type { MarketDataArgs, PriceHistoryArgs, } from '@shapeshiftoss/types' +import type { ethers } from 'ethers' import { AssetService } from 'lib/asset-service' // import { Yearn } from '@yfi/sdk' @@ -25,6 +26,7 @@ export type ProviderUrls = { export type MarketServiceManagerArgs = { yearnChainReference: 1 | 250 | 1337 | 42161 // from @yfi/sdk providerUrls: ProviderUrls + provider: ethers.providers.StaticJsonRpcProvider } export class MarketServiceManager { @@ -32,7 +34,7 @@ export class MarketServiceManager { assetService: AssetService constructor(args: MarketServiceManagerArgs) { - const { providerUrls } = args + const { providerUrls, provider } = args // TODO(0xdef1cafe): after chain agnosticism, we need to dependency inject a chainReference here // YearnVaultMarketCapService deps @@ -48,7 +50,7 @@ export class MarketServiceManager { // Yearn is currently borked upstream // new YearnVaultMarketCapService({ yearnSdk }), // new YearnTokenMarketCapService({ yearnSdk }), - new FoxyMarketService({ providerUrls }), + new FoxyMarketService({ providerUrls, provider }), ] this.assetService = new AssetService() diff --git a/src/lib/market-service/market-service.test.ts b/src/lib/market-service/market-service.test.ts index 42662230677..93560c39bb1 100644 --- a/src/lib/market-service/market-service.test.ts +++ b/src/lib/market-service/market-service.test.ts @@ -1,4 +1,5 @@ import { HistoryTimeframe } from '@shapeshiftoss/types' +import { ethers } from 'ethers' import { describe, expect, it, vi } from 'vitest' import { CoinGeckoMarketService } from './coingecko/coingecko' @@ -99,6 +100,7 @@ describe('market service', () => { const marketServiceManagerArgs = { coinGeckoAPIKey: 'dummyCoingeckoApiKey', yearnChainReference: 1 as const, + provider: new ethers.providers.StaticJsonRpcProvider(''), providerUrls: { jsonRpcProviderUrl: '', unchainedEthereumWsUrl: '', diff --git a/src/plugins/walletConnectToDapps/hooks/useGetAbi.tsx b/src/plugins/walletConnectToDapps/hooks/useGetAbi.tsx index 085f62d965d..c41df7d08b0 100644 --- a/src/plugins/walletConnectToDapps/hooks/useGetAbi.tsx +++ b/src/plugins/walletConnectToDapps/hooks/useGetAbi.tsx @@ -28,7 +28,7 @@ export const useGetAbi = ( const { to: contractAddress, data } = transactionParams const provider = useMemo( - () => new ethers.providers.JsonRpcBatchProvider(getConfig().REACT_APP_ETHEREUM_NODE_URL), + () => new ethers.providers.StaticJsonRpcProvider(getConfig().REACT_APP_ETHEREUM_NODE_URL), [], ) diff --git a/src/state/apis/foxy/foxyApiSingleton.ts b/src/state/apis/foxy/foxyApiSingleton.ts index 97d35bc547f..e2d09623c58 100644 --- a/src/state/apis/foxy/foxyApiSingleton.ts +++ b/src/state/apis/foxy/foxyApiSingleton.ts @@ -1,6 +1,7 @@ import type { EvmBaseAdapter } from '@shapeshiftoss/chain-adapters' import { KnownChainIds } from '@shapeshiftoss/types' import { getConfig } from 'config' +import { getEthersProvider } from 'lib/ethersProviderSingleton' import { foxyAddresses, FoxyApi } from 'lib/investor/investor-foxy' import { assertGetEvmChainAdapter } from 'lib/utils/evm' @@ -19,6 +20,7 @@ export const getFoxyApi = (): FoxyApi => { ) as EvmBaseAdapter, providerUrl: getConfig()[RPC_PROVIDER_ENV], foxyAddresses, + provider: getEthersProvider(KnownChainIds.EthereumMainnet), }) _foxyApi = foxyApi diff --git a/src/state/slices/marketDataSlice/marketServiceManagerSingleton.ts b/src/state/slices/marketDataSlice/marketServiceManagerSingleton.ts index 6e1bb819e56..239d3759ade 100644 --- a/src/state/slices/marketDataSlice/marketServiceManagerSingleton.ts +++ b/src/state/slices/marketDataSlice/marketServiceManagerSingleton.ts @@ -1,5 +1,7 @@ // do not directly use or export, singleton +import { KnownChainIds } from '@shapeshiftoss/types' import { getConfig } from 'config' +import { getEthersProvider } from 'lib/ethersProviderSingleton' import { MarketServiceManager } from 'lib/market-service' let _marketServiceManager: MarketServiceManager | undefined @@ -11,6 +13,7 @@ export const getMarketServiceManager: GetMarketServiceManager = () => { if (!_marketServiceManager) { _marketServiceManager = new MarketServiceManager({ yearnChainReference: 1, // CHAIN_REFERENCE.EthereumMainnet is '1', yearn requires strict number union + provider: getEthersProvider(KnownChainIds.EthereumMainnet), providerUrls: { jsonRpcProviderUrl: config.REACT_APP_ETHEREUM_NODE_URL, unchainedEthereumHttpUrl: config.REACT_APP_UNCHAINED_ETHEREUM_HTTP_URL, From 8e07882fbd532f5036020425be5d615f4493f148 Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 19 Feb 2024 15:14:19 -0700 Subject: [PATCH 04/11] chore: cut over nownodes prod (#6251) --- .env.app | 8 ++++---- .env.private | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.env.app b/.env.app index 9ff7327cc3e..f5f3412f819 100644 --- a/.env.app +++ b/.env.app @@ -35,13 +35,13 @@ REACT_APP_UNCHAINED_THORCHAIN_HTTP_URL=https://api.thorchain.shapeshift.com REACT_APP_UNCHAINED_THORCHAIN_WS_URL=wss://api.thorchain.shapeshift.com # nodes -REACT_APP_ETHEREUM_NODE_URL=https://daemon.ethereum.shapeshift.com +REACT_APP_ETHEREUM_NODE_URL=https://nownodes.shapeshift.com/ethereum REACT_APP_AVALANCHE_NODE_URL=https://daemon.avalanche.shapeshift.com/ext/bc/C/rpc REACT_APP_OPTIMISM_NODE_URL=https://daemon.optimism.shapeshift.com -REACT_APP_BNBSMARTCHAIN_NODE_URL=https://daemon.bnbsmartchain.shapeshift.com -REACT_APP_POLYGON_NODE_URL=https://daemon.polygon.shapeshift.com +REACT_APP_BNBSMARTCHAIN_NODE_URL=https://nownodes.shapeshift.com/bnbsmartchain +REACT_APP_POLYGON_NODE_URL=https://nownodes.shapeshift.com/polygon REACT_APP_GNOSIS_NODE_URL=https://daemon.gnosis.shapeshift.com -REACT_APP_ARBITRUM_NODE_URL=https://daemon.arbitrum.shapeshift.com +REACT_APP_ARBITRUM_NODE_URL=https://nownodes.shapeshift.com/arbitrum REACT_APP_ARBITRUM_NOVA_NODE_URL=https://daemon.arbitrum-nova.shapeshift.com REACT_APP_THORCHAIN_NODE_URL=https://daemon.thorchain.shapeshift.com diff --git a/.env.private b/.env.private index 54ae279ce13..bb61c16f9aa 100644 --- a/.env.private +++ b/.env.private @@ -33,13 +33,13 @@ REACT_APP_UNCHAINED_THORCHAIN_HTTP_URL=https://api.thorchain.shapeshift.com REACT_APP_UNCHAINED_THORCHAIN_WS_URL=wss://api.thorchain.shapeshift.com # nodes -REACT_APP_ETHEREUM_NODE_URL=https://daemon.ethereum.shapeshift.com +REACT_APP_ETHEREUM_NODE_URL=https://nownodes.shapeshift.com/ethereum REACT_APP_AVALANCHE_NODE_URL=https://daemon.avalanche.shapeshift.com/ext/bc/C/rpc REACT_APP_OPTIMISM_NODE_URL=https://daemon.optimism.shapeshift.com -REACT_APP_BNBSMARTCHAIN_NODE_URL=https://daemon.bnbsmartchain.shapeshift.com -REACT_APP_POLYGON_NODE_URL=https://daemon.polygon.shapeshift.com +REACT_APP_BNBSMARTCHAIN_NODE_URL=https://nownodes.shapeshift.com/bnbsmartchain +REACT_APP_POLYGON_NODE_URL=https://nownodes.shapeshift.com/polygon REACT_APP_GNOSIS_NODE_URL=https://daemon.gnosis.shapeshift.com -REACT_APP_ARBITRUM_NODE_URL=https://daemon.arbitrum.shapeshift.com +REACT_APP_ARBITRUM_NODE_URL=https://nownodes.shapeshift.com/arbitrum REACT_APP_ARBITRUM_NOVA_NODE_URL=https://daemon.arbitrum-nova.shapeshift.com REACT_APP_THORCHAIN_NODE_URL=https://daemon.thorchain.shapeshift.com From d0cf9c3b7706ebf0de190e5a906f786b8fbdaf8f Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 19 Feb 2024 15:25:07 -0700 Subject: [PATCH 05/11] chore: bump hdwallet v1.53.4 (#6255) --- package.json | 26 ++++----- yarn.lock | 158 +++++++++++++++++++++++++-------------------------- 2 files changed, 92 insertions(+), 92 deletions(-) diff --git a/package.json b/package.json index 8b2133e9f40..25e6b7478d2 100644 --- a/package.json +++ b/package.json @@ -88,19 +88,19 @@ "@shapeshiftoss/caip": "workspace:^", "@shapeshiftoss/chain-adapters": "workspace:^", "@shapeshiftoss/errors": "workspace:^", - "@shapeshiftoss/hdwallet-coinbase": "1.53.3", - "@shapeshiftoss/hdwallet-core": "1.53.3", - "@shapeshiftoss/hdwallet-keepkey": "1.53.3", - "@shapeshiftoss/hdwallet-keepkey-webusb": "1.53.3", - "@shapeshiftoss/hdwallet-keplr": "1.53.3", - "@shapeshiftoss/hdwallet-ledger": "1.53.3", - "@shapeshiftoss/hdwallet-ledger-webusb": "1.53.3", - "@shapeshiftoss/hdwallet-metamask": "1.53.3", - "@shapeshiftoss/hdwallet-native": "1.53.3", - "@shapeshiftoss/hdwallet-native-vault": "1.53.3", - "@shapeshiftoss/hdwallet-shapeshift-multichain": "1.53.3", - "@shapeshiftoss/hdwallet-walletconnectv2": "1.53.3", - "@shapeshiftoss/hdwallet-xdefi": "1.53.3", + "@shapeshiftoss/hdwallet-coinbase": "1.53.4", + "@shapeshiftoss/hdwallet-core": "1.53.4", + "@shapeshiftoss/hdwallet-keepkey": "1.53.4", + "@shapeshiftoss/hdwallet-keepkey-webusb": "1.53.4", + "@shapeshiftoss/hdwallet-keplr": "1.53.4", + "@shapeshiftoss/hdwallet-ledger": "1.53.4", + "@shapeshiftoss/hdwallet-ledger-webusb": "1.53.4", + "@shapeshiftoss/hdwallet-metamask": "1.53.4", + "@shapeshiftoss/hdwallet-native": "1.53.4", + "@shapeshiftoss/hdwallet-native-vault": "1.53.4", + "@shapeshiftoss/hdwallet-shapeshift-multichain": "1.53.4", + "@shapeshiftoss/hdwallet-walletconnectv2": "1.53.4", + "@shapeshiftoss/hdwallet-xdefi": "1.53.4", "@shapeshiftoss/swapper": "workspace:^", "@shapeshiftoss/types": "workspace:^", "@shapeshiftoss/unchained-client": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 21b42e77066..acb94139162 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9553,15 +9553,15 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/hdwallet-coinbase@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-coinbase@npm:1.53.3" +"@shapeshiftoss/hdwallet-coinbase@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-coinbase@npm:1.53.4" dependencies: "@coinbase/wallet-sdk": ^3.6.6 - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 eth-rpc-errors: ^4.0.3 lodash: ^4.17.21 - checksum: 0b91dee44b0ac6488d6d54c7537d5f28461e2dda16cb018870743e94dfe2e35c6165a1800bf15e60d721cdd4689595bb695ed4ada668cae9b62a9ce830c11cd8 + checksum: 7d85e6f97eb957b3b20110ceafc6ccb6c203a2940b8767cf7588f28f06c3d09ecc3a3c260a3187e0f486b0e16ce1d0480979ce9a2f9cf4d2618cfc3ef39b507b languageName: node linkType: hard @@ -9591,9 +9591,9 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/hdwallet-core@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-core@npm:1.53.3" +"@shapeshiftoss/hdwallet-core@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-core@npm:1.53.4" dependencies: "@shapeshiftoss/proto-tx-builder": ^0.8.0 eip-712: ^1.0.0 @@ -9601,30 +9601,30 @@ __metadata: lodash: ^4.17.21 rxjs: ^6.4.0 type-assertions: ^1.1.0 - checksum: fee0540ff5fb70b09f35f124d4536e588f9223db95b9fb6a68d543b5c0ed7c1fe7a21035e5ff77720189f945d9bac01a1926c0e0abb9c5bcca8ab9f7d2012a9e + checksum: 50e632b63aecf2293856bd131bfaebde15d3071751e04aadafa35c82f3622c94afd0a7c5ca91fc6dc0828517505976934241090dff31c419167ff5e3836af8c3 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-keepkey-webusb@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-keepkey-webusb@npm:1.53.3" +"@shapeshiftoss/hdwallet-keepkey-webusb@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-keepkey-webusb@npm:1.53.4" dependencies: - "@shapeshiftoss/hdwallet-core": 1.53.3 - "@shapeshiftoss/hdwallet-keepkey": 1.53.3 - checksum: 92eb46c6a5702787b4497597ac73570755ce5795f427f4d82aaf395fc747daf67f06cc84077065fd4b812fed1d9cf5ba451fdc3a7dd5890daa885ee0073fd38e + "@shapeshiftoss/hdwallet-core": 1.53.4 + "@shapeshiftoss/hdwallet-keepkey": 1.53.4 + checksum: 4ed8b8347d24b97bb4df58bbdccc0b5b286cdbdfb433f999be94091b7df116ae45504c7409899c9c6844e7967e98e495d8961257496ba1d1de92dcd68e3d80cf languageName: node linkType: hard -"@shapeshiftoss/hdwallet-keepkey@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-keepkey@npm:1.53.3" +"@shapeshiftoss/hdwallet-keepkey@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-keepkey@npm:1.53.4" dependencies: "@ethereumjs/common": ^2.4.0 "@ethereumjs/tx": ^3.3.0 "@keepkey/device-protocol": ^7.12.2 "@metamask/eth-sig-util": ^7.0.0 "@shapeshiftoss/bitcoinjs-lib": 5.2.0-shapeshift.2 - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 "@shapeshiftoss/proto-tx-builder": ^0.8.0 bignumber.js: ^9.0.1 bnb-javascript-sdk-nobroadcast: ^2.16.14 @@ -9636,27 +9636,27 @@ __metadata: p-lazy: ^3.1.0 semver: ^7.3.8 tiny-secp256k1: ^1.1.6 - checksum: f30b58d56d628ab420e3fae14f530a948baa52731ba7b56759cd3cc961665976c7115a7ee66a0314989c87e4e52624939ed67cf97c8687476ea8eb12f882c185 + checksum: ea9c6c66a1b845126006aa09616de3a08dcc983ad614fcdcdcd7f38109185dda9c2107290bc42f50e53463152fae8886200994a815b93707e2b13950eb6d3fc0 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-keplr@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-keplr@npm:1.53.3" +"@shapeshiftoss/hdwallet-keplr@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-keplr@npm:1.53.4" dependencies: "@shapeshiftoss/caip": 8.15.0 - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 "@shapeshiftoss/proto-tx-builder": ^0.8.0 "@shapeshiftoss/types": 3.1.3 base64-js: ^1.5.1 lodash: ^4.17.21 - checksum: 4fc540a8820fe9e8b044c73bdde28b7f6755dc6301145d8e31aaba0ecde0c18b76c80ce8495b0e364cc05be7516e16e2fcefc6ee62a2f161f54c5f3a0d729a9f + checksum: ab0eea564bfdf38f216e0570d8f4790d1c04d51d17a970f950c5af7532bf511f8aa6f2b6ccc2143fbfb123090391c8a13a462209c0c878270bfe8c6fd461f191 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-ledger-webusb@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-ledger-webusb@npm:1.53.3" +"@shapeshiftoss/hdwallet-ledger-webusb@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-ledger-webusb@npm:1.53.4" dependencies: "@ledgerhq/hw-app-btc": ^10.0.8 "@ledgerhq/hw-app-eth": ^6.9.0 @@ -9664,23 +9664,23 @@ __metadata: "@ledgerhq/hw-transport-webusb": ^6.7.0 "@ledgerhq/live-common": ^21.8.2 "@ledgerhq/logs": ^6.10.1 - "@shapeshiftoss/hdwallet-core": 1.53.3 - "@shapeshiftoss/hdwallet-ledger": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 + "@shapeshiftoss/hdwallet-ledger": 1.53.4 "@types/w3c-web-usb": ^1.0.4 p-queue: ^7.4.1 - checksum: 86157eb7b283ee37ef0d9625ad75a6f7acd58e86f7f71e0dfa8a2a2c99cd830b22a89cb9b59b4764c8b32f80260d023ea322c2bddfb68cd1a1482e23a42be25f + checksum: 4a9ec1ce4a2651a9214d738783c138d5d108f9dd0bacc286e0ac6e8055490b71f440e01343a18a41f3563e23c1c04c6ae1a93d2607052ffbb339256b2f8d637b languageName: node linkType: hard -"@shapeshiftoss/hdwallet-ledger@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-ledger@npm:1.53.3" +"@shapeshiftoss/hdwallet-ledger@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-ledger@npm:1.53.4" dependencies: "@ethereumjs/common": ^2.4.0 "@ethereumjs/tx": ^3.3.0 "@ledgerhq/hw-app-cosmos": ^6.29.1 "@shapeshiftoss/bitcoinjs-lib": 5.2.0-shapeshift.2 - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 base64-js: ^1.5.1 bchaddrjs: ^0.4.4 bitcoinjs-message: ^2.0.0 @@ -9688,28 +9688,28 @@ __metadata: ethereumjs-tx: 1.3.7 ethereumjs-util: ^6.1.0 lodash: ^4.17.21 - checksum: 10ecdf3ff61d6a161eb4ade19832c1292c5e01c91fa52ad87a11975b1b36284722fe0571a7f4d67143faf8c30ab45ef08fff6708ab8cad6173abe091292dc247 + checksum: 92180b18d7f1c38e64e73cc9a7e2d331a25d87ecb78ff646d77a8bbb19373db4545e7be6961da1be7a380ec4fdf7aac3596296302e7852d6632b7ab12f6dc7b7 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-metamask@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-metamask@npm:1.53.3" +"@shapeshiftoss/hdwallet-metamask@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-metamask@npm:1.53.4" dependencies: "@metamask/detect-provider": ^1.2.0 "@metamask/onboarding": ^1.0.1 - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 eth-rpc-errors: ^4.0.3 lodash: ^4.17.21 - checksum: 0c4e2f19201155218c3faffb9a108bb837408c56391ddd56f8e61d4e8d04e12f46fcf036485017aeb2ea118c3980ca50cc2297b41bd436ef30fe0444730d551a + checksum: 1d02c0e752c06895ab992c06b2888521c031b0a6f53d07fabe9df4d61ae24a5073f1cda017160d30aed9f7e3722ef6dcd1e1b9c6114aad223f7d56085fc31656 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-native-vault@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-native-vault@npm:1.53.3" +"@shapeshiftoss/hdwallet-native-vault@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-native-vault@npm:1.53.4" dependencies: - "@shapeshiftoss/hdwallet-native": 1.53.3 + "@shapeshiftoss/hdwallet-native": 1.53.4 bip39: ^3.0.4 hash-wasm: ^4.9.0 idb-keyval: ^6.0.3 @@ -9718,17 +9718,17 @@ __metadata: type-assertions: ^1.1.0 uuid: ^8.3.2 web-encoding: ^1.1.0 - checksum: 1768f57cc7cd6b58f22c85d0914fe7afce187a296a8ab72b79f31b164c8c0c4515baad6c237739a75ccd5d56656f60c65aeeb9caaec18cb3b7b95c3eb4629293 + checksum: 7ad5e73394240d73e1ccb5bb36984945d640ff34ba0e19ef616cce719ed1bdb5d0641940450114a9dcab6150d6aa488edd2aa1ac681f5b2f8473ef37dad58425 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-native@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-native@npm:1.53.3" +"@shapeshiftoss/hdwallet-native@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-native@npm:1.53.4" dependencies: "@shapeshiftoss/bitcoinjs-lib": 5.2.0-shapeshift.2 "@shapeshiftoss/fiosdk": 1.2.1-shapeshift.6 - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 "@shapeshiftoss/proto-tx-builder": ^0.8.0 "@zxing/text-encoding": ^0.9.0 bchaddrjs: ^0.4.9 @@ -9749,7 +9749,7 @@ __metadata: tendermint-tx-builder: ^1.0.9 tiny-secp256k1: ^1.1.6 web-encoding: ^1.1.0 - checksum: 42fd593fd80e9fe4d4faede7e8b928e5aadd414e4f8746b4546cef2d0b42a11e648e8ad672b4c0db156a4a94c783ddf947b97fd5d555c55f0bd220407aa5e03f + checksum: 2288b3d8f89fd68d58e2536f0b5f8031f57f4d9c9d98a599c752ce993d6a9ab99a5e19f707040049bd41c46f39d204af519bc3705979d69635d4e70dd5e57d9b languageName: node linkType: hard @@ -9784,41 +9784,41 @@ __metadata: languageName: node linkType: hard -"@shapeshiftoss/hdwallet-shapeshift-multichain@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-shapeshift-multichain@npm:1.53.3" +"@shapeshiftoss/hdwallet-shapeshift-multichain@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-shapeshift-multichain@npm:1.53.4" dependencies: "@metamask/detect-provider": ^1.2.0 "@metamask/onboarding": ^1.0.1 "@shapeshiftoss/common-api": ^9.3.0 - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 "@shapeshiftoss/metamask-snaps-adapter": ^1.0.8 "@shapeshiftoss/metamask-snaps-types": ^1.0.8 eth-rpc-errors: ^4.0.3 lodash: ^4.17.21 - checksum: 7ac6cddd86c2ae55f30252449c7d9b54ef5343067971f1fa66fca1ac0f849583bddbbabd663452a18dcf914bcb5cc0032e8e9d48ef8fcacc706f00b21921d318 + checksum: bccbdeab6ab59e8b9890acb57e67dce3be2b77e56472df76970922829b25f7f21b70665a49df8cb1fd3f21f972eecc9f083a5aa7c1905aa51a7437be940b8b53 languageName: node linkType: hard -"@shapeshiftoss/hdwallet-walletconnectv2@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-walletconnectv2@npm:1.53.3" +"@shapeshiftoss/hdwallet-walletconnectv2@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-walletconnectv2@npm:1.53.4" dependencies: - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 "@walletconnect/ethereum-provider": ^2.10.1 "@walletconnect/modal": ^2.6.2 ethers: ^5.6.5 - checksum: 96dfd68a1cca47e8365779e19b2aaa4949e608ab8287c70220090d65e6c2097d54685e9ce00f6cce4a8a877038c3870a67bec141ce31f0da9aff675ef2e80428 + checksum: 97478193d36aa00e35e9edf195e41db83dbd0091e41b9ff3ba11af4faccbfa151b87c71ed1a769fe73120bffb8d72f130ceb7b1996d3325449facf5a6ae1935b languageName: node linkType: hard -"@shapeshiftoss/hdwallet-xdefi@npm:1.53.3": - version: 1.53.3 - resolution: "@shapeshiftoss/hdwallet-xdefi@npm:1.53.3" +"@shapeshiftoss/hdwallet-xdefi@npm:1.53.4": + version: 1.53.4 + resolution: "@shapeshiftoss/hdwallet-xdefi@npm:1.53.4" dependencies: - "@shapeshiftoss/hdwallet-core": 1.53.3 + "@shapeshiftoss/hdwallet-core": 1.53.4 lodash: ^4.17.21 - checksum: d86389201759e5544ba0741d6b824e1438827043279e3147e4b6a0038548ad2fcee2c4ea615e9732bdbf5f01c28ec80d2e4d588c61f5dd26d6e51cad0741981e + checksum: d9029b8aacbca6600546787630416e04e8fc5f9584f071f0c616702014f8127acd6100c503f30f7ade9f5c19540ae15699befbf5491d5c466f22244fea79a77e languageName: node linkType: hard @@ -9995,19 +9995,19 @@ __metadata: "@shapeshiftoss/caip": "workspace:^" "@shapeshiftoss/chain-adapters": "workspace:^" "@shapeshiftoss/errors": "workspace:^" - "@shapeshiftoss/hdwallet-coinbase": 1.53.3 - "@shapeshiftoss/hdwallet-core": 1.53.3 - "@shapeshiftoss/hdwallet-keepkey": 1.53.3 - "@shapeshiftoss/hdwallet-keepkey-webusb": 1.53.3 - "@shapeshiftoss/hdwallet-keplr": 1.53.3 - "@shapeshiftoss/hdwallet-ledger": 1.53.3 - "@shapeshiftoss/hdwallet-ledger-webusb": 1.53.3 - "@shapeshiftoss/hdwallet-metamask": 1.53.3 - "@shapeshiftoss/hdwallet-native": 1.53.3 - "@shapeshiftoss/hdwallet-native-vault": 1.53.3 - "@shapeshiftoss/hdwallet-shapeshift-multichain": 1.53.3 - "@shapeshiftoss/hdwallet-walletconnectv2": 1.53.3 - "@shapeshiftoss/hdwallet-xdefi": 1.53.3 + "@shapeshiftoss/hdwallet-coinbase": 1.53.4 + "@shapeshiftoss/hdwallet-core": 1.53.4 + "@shapeshiftoss/hdwallet-keepkey": 1.53.4 + "@shapeshiftoss/hdwallet-keepkey-webusb": 1.53.4 + "@shapeshiftoss/hdwallet-keplr": 1.53.4 + "@shapeshiftoss/hdwallet-ledger": 1.53.4 + "@shapeshiftoss/hdwallet-ledger-webusb": 1.53.4 + "@shapeshiftoss/hdwallet-metamask": 1.53.4 + "@shapeshiftoss/hdwallet-native": 1.53.4 + "@shapeshiftoss/hdwallet-native-vault": 1.53.4 + "@shapeshiftoss/hdwallet-shapeshift-multichain": 1.53.4 + "@shapeshiftoss/hdwallet-walletconnectv2": 1.53.4 + "@shapeshiftoss/hdwallet-xdefi": 1.53.4 "@shapeshiftoss/swapper": "workspace:^" "@shapeshiftoss/types": "workspace:^" "@shapeshiftoss/unchained-client": "workspace:^" From 2289c38b9cab0975f7a90924dcd297ca21f02ebe Mon Sep 17 00:00:00 2001 From: kevin <35275952+kaladinlight@users.noreply.github.com> Date: Mon, 19 Feb 2024 15:33:57 -0700 Subject: [PATCH 06/11] revert fix: temporarily disable eip-1559 for keepkey (#6254) --- .../Modals/Send/hooks/useFormSend/useFormSend.test.tsx | 4 ---- src/components/Modals/Send/utils.ts | 3 +-- .../components/MultiHopTradeConfirm/hooks/useApprovalTx.tsx | 3 +-- .../MultiHopTradeConfirm/hooks/useTradeExecution.tsx | 5 ++--- .../hooks/useGetTradeQuotes/getTradeQuoteArgs.ts | 5 ++--- .../defi/providers/fox-farming/hooks/useFoxFarming.ts | 5 ++--- src/lib/investor/investor-foxy/api/api.ts | 2 -- src/lib/utils/evm.ts | 5 ++--- src/react-queries/index.ts | 5 ++--- 9 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx b/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx index 5123f56da1e..7684df201ee 100644 --- a/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx +++ b/src/components/Modals/Send/hooks/useFormSend/useFormSend.test.tsx @@ -170,7 +170,6 @@ describe.each([ wallet: { supportsOfflineSigning: vi.fn().mockReturnValue(true), ethSupportsEIP1559: vi.fn().mockReturnValue(walletSupportsEIP1559), - getVendor: vi.fn().mockReturnValue('Native'), }, }, }) as unknown as IWalletContext, @@ -231,7 +230,6 @@ describe.each([ wallet: { supportsOfflineSigning: vi.fn().mockReturnValue(true), ethSupportsEIP1559: vi.fn().mockReturnValue(walletSupportsEIP1559), - getVendor: vi.fn().mockReturnValue('Native'), }, }, }) as unknown as IWalletContext, @@ -298,7 +296,6 @@ describe.each([ supportsOfflineSigning: vi.fn().mockReturnValue(false), supportsBroadcast: vi.fn().mockReturnValue(true), ethSupportsEIP1559: vi.fn().mockReturnValue(walletSupportsEIP1559), - getVendor: vi.fn().mockReturnValue('Native'), }, }, }) as unknown as IWalletContext, @@ -354,7 +351,6 @@ describe.each([ supportsOfflineSigning: vi.fn().mockReturnValue(false), supportsBroadcast: vi.fn().mockReturnValue(true), ethSupportsEIP1559: vi.fn().mockReturnValue(walletSupportsEIP1559), - getVendor: vi.fn().mockReturnValue('Native'), } as unknown as HDWallet const toaster = vi.fn() as unknown as CreateToastFnReturn const signAndBroadcastTransaction = vi.fn().mockResolvedValue('txid') diff --git a/src/components/Modals/Send/utils.ts b/src/components/Modals/Send/utils.ts index f37d468753f..80b8425e6e2 100644 --- a/src/components/Modals/Send/utils.ts +++ b/src/components/Modals/Send/utils.ts @@ -18,7 +18,7 @@ import { checkIsSnapInstalled, } from 'hooks/useIsSnapInstalled/useIsSnapInstalled' import { bn, bnOrZero } from 'lib/bignumber/bignumber' -import { assertGetChainAdapter, isKeepKeyHDWallet, tokenOrUndefined } from 'lib/utils' +import { assertGetChainAdapter, tokenOrUndefined } from 'lib/utils' import { assertGetCosmosSdkChainAdapter } from 'lib/utils/cosmosSdk' import { assertGetEvmChainAdapter, getSupportedEvmChainIds } from 'lib/utils/evm' import { assertGetUtxoChainAdapter } from 'lib/utils/utxo' @@ -141,7 +141,6 @@ export const handleSend = async ({ chainSpecific: { gasPrice, gasLimit, maxFeePerGas, maxPriorityFeePerGas }, } = fees const shouldUseEIP1559Fees = - !isKeepKeyHDWallet(wallet) && (await wallet.ethSupportsEIP1559()) && maxFeePerGas !== undefined && maxPriorityFeePerGas !== undefined diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useApprovalTx.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useApprovalTx.tsx index b81a8f79fc7..a0fdc73f505 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useApprovalTx.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useApprovalTx.tsx @@ -5,7 +5,6 @@ import type { TradeQuoteStep } from '@shapeshiftoss/swapper' import { useEffect, useState } from 'react' import { usePoll } from 'hooks/usePoll/usePoll' import { useWallet } from 'hooks/useWallet/useWallet' -import { isKeepKeyHDWallet } from 'lib/utils' import { assertGetEvmChainAdapter } from 'lib/utils/evm' import { selectHopSellAccountId } from 'state/slices/tradeQuoteSlice/selectors' import { useAppSelector } from 'state/store' @@ -39,7 +38,7 @@ export const useApprovalTx = ( // This accidentally works since all EVM chains share the same address, so there's no need // to call adapter.getAddress() later down the call stack const from = fromAccountId(sellAssetAccountId).account - const supportsEIP1559 = !isKeepKeyHDWallet(wallet) && (await wallet.ethSupportsEIP1559()) + const supportsEIP1559 = await wallet.ethSupportsEIP1559() const { buildCustomTxInput, networkFeeCryptoBaseUnit } = await getApprovalTxData({ tradeQuoteStep, diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useTradeExecution.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useTradeExecution.tsx index a7d218bdb89..0802e85acf6 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useTradeExecution.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useTradeExecution.tsx @@ -13,7 +13,7 @@ import { useErrorHandler } from 'hooks/useErrorToast/useErrorToast' import { useWallet } from 'hooks/useWallet/useWallet' import { MixPanelEvent } from 'lib/mixpanel/types' import { TradeExecution } from 'lib/swapper/tradeExecution' -import { assertUnreachable, isKeepKeyHDWallet } from 'lib/utils' +import { assertUnreachable } from 'lib/utils' import { assertGetCosmosSdkChainAdapter } from 'lib/utils/cosmosSdk' import { assertGetEvmChainAdapter, signAndBroadcast } from 'lib/utils/evm' import { assertGetUtxoChainAdapter } from 'lib/utils/utxo' @@ -203,8 +203,7 @@ export const useTradeExecution = (hopIndex: number) => { case CHAIN_NAMESPACE.Evm: { const adapter = assertGetEvmChainAdapter(stepSellAssetChainId) const from = await adapter.getAddress({ accountNumber, wallet }) - const supportsEIP1559 = - !isKeepKeyHDWallet(wallet) && supportsETH(wallet) && (await wallet.ethSupportsEIP1559()) + const supportsEIP1559 = supportsETH(wallet) && (await wallet.ethSupportsEIP1559()) const output = await execution.execEvmTransaction({ swapperName, diff --git a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteArgs.ts b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteArgs.ts index c659f79f897..4f57a8b6144 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteArgs.ts +++ b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/getTradeQuoteArgs.ts @@ -6,7 +6,7 @@ import type { GetTradeQuoteInput } from '@shapeshiftoss/swapper' import type { Asset, UtxoAccountType } from '@shapeshiftoss/types' import type { TradeQuoteInputCommonArgs } from 'components/MultiHopTrade/types' import { toBaseUnit } from 'lib/math' -import { assertUnreachable, isKeepKeyHDWallet } from 'lib/utils' +import { assertUnreachable } from 'lib/utils' import { assertGetCosmosSdkChainAdapter } from 'lib/utils/cosmosSdk' import { assertGetEvmChainAdapter } from 'lib/utils/evm' import { assertGetUtxoChainAdapter } from 'lib/utils/utxo' @@ -65,8 +65,7 @@ export const getTradeQuoteArgs = async ({ switch (chainNamespace) { case CHAIN_NAMESPACE.Evm: { - const supportsEIP1559 = - !isKeepKeyHDWallet(wallet) && supportsETH(wallet) && (await wallet.ethSupportsEIP1559()) + const supportsEIP1559 = supportsETH(wallet) && (await wallet.ethSupportsEIP1559()) const sellAssetChainAdapter = assertGetEvmChainAdapter(sellAsset.chainId) const sendAddress = await sellAssetChainAdapter.getAddress({ accountNumber: sellAccountNumber, diff --git a/src/features/defi/providers/fox-farming/hooks/useFoxFarming.ts b/src/features/defi/providers/fox-farming/hooks/useFoxFarming.ts index 62731789f85..2bfb66c8dc2 100644 --- a/src/features/defi/providers/fox-farming/hooks/useFoxFarming.ts +++ b/src/features/defi/providers/fox-farming/hooks/useFoxFarming.ts @@ -9,7 +9,7 @@ import { encodeFunctionData, getAddress } from 'viem' import { useFoxEth } from 'context/FoxEthProvider/FoxEthProvider' import { useWallet } from 'hooks/useWallet/useWallet' import { toBaseUnit } from 'lib/math' -import { isKeepKeyHDWallet, isValidAccountNumber } from 'lib/utils' +import { isValidAccountNumber } from 'lib/utils' import { assertGetEvmChainAdapter, buildAndBroadcast, @@ -220,8 +220,7 @@ export const useFoxFarming = ( from: userAddress, to: contractAddress, value: '0', - supportsEIP1559: - !isKeepKeyHDWallet(wallet) && supportsETH(wallet) && (await wallet.ethSupportsEIP1559()), + supportsEIP1559: supportsETH(wallet) && (await wallet.ethSupportsEIP1559()), }) }, [adapter, contractAddress, foxFarmingContract, wallet], diff --git a/src/lib/investor/investor-foxy/api/api.ts b/src/lib/investor/investor-foxy/api/api.ts index c74d047279c..2d788a113ee 100644 --- a/src/lib/investor/investor-foxy/api/api.ts +++ b/src/lib/investor/investor-foxy/api/api.ts @@ -14,7 +14,6 @@ import { ethers } from 'ethers' import { toLower } from 'lodash' import { bn, bnOrZero } from 'lib/bignumber/bignumber' import { MAX_ALLOWANCE } from 'lib/investor/constants' -import { isKeepKeyHDWallet } from 'lib/utils' import { DefiType } from 'state/slices/opportunitiesSlice/types' import { erc20Abi } from '../abi/erc20-abi' @@ -129,7 +128,6 @@ export class FoxyApi { chainSpecific: { gasPrice, gasLimit, maxFeePerGas, maxPriorityFeePerGas }, } = payload.estimatedFees.fast const shouldUseEIP1559Fees = - !isKeepKeyHDWallet(wallet) && (await wallet.ethSupportsEIP1559()) && maxFeePerGas !== undefined && maxPriorityFeePerGas !== undefined diff --git a/src/lib/utils/evm.ts b/src/lib/utils/evm.ts index 726b6719e92..2483e03949e 100644 --- a/src/lib/utils/evm.ts +++ b/src/lib/utils/evm.ts @@ -20,7 +20,7 @@ import { encodeFunctionData, getAddress } from 'viem' import { getChainAdapterManager } from 'context/PluginProvider/chainAdapterSingleton' import { bn, bnOrZero } from 'lib/bignumber/bignumber' -import { getSupportedChainIdsByChainNamespace, isKeepKeyHDWallet } from '.' +import { getSupportedChainIdsByChainNamespace } from '.' type GetApproveContractDataArgs = { approvalAmountCryptoBaseUnit: string @@ -96,8 +96,7 @@ export const getFeesWithWallet = async (args: GetFeesWithWalletArgs): Promise Date: Mon, 19 Feb 2024 15:24:02 -0800 Subject: [PATCH 07/11] feat: unify Tx allowance logic (#6217) * feat: unify Tx allowance logic * feat: cleaner * feat: refactor useIsApprovalNeeded to use a selector on allowanceCryptobaseUnit * feat: cleanup * feat: more cleanup * feat: cleanup * chore: do not inline types * feat: more cleanup * feat: cleanup unused type * feat: consume selectAllowanceCryptoBaseUnit in useIsApprovalNeeded * feat: extract GetAllowanceErr in react-queries/types --------- Co-authored-by: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Co-authored-by: Apotheosis <97164662+0xApotheosis@users.noreply.github.com> --- .../MultiHopTradeConfirm/hooks/helpers.ts | 70 +---------- .../hooks/useAllowance.tsx | 113 ------------------ .../hooks/useAllowanceApproval.tsx | 1 - .../hooks/useIsApprovalInitiallyNeeded.tsx | 22 +--- .../hooks/useIsApprovalNeeded.tsx | 75 +++++------- .../AddLiquitity/AddLiquidityInput.tsx | 16 +-- src/react-queries/hooks/selectors/index.ts | 22 ++++ src/react-queries/hooks/useAllowance.ts | 21 ++++ src/react-queries/index.ts | 31 +++-- src/react-queries/types/index.ts | 4 + 10 files changed, 112 insertions(+), 263 deletions(-) delete mode 100644 src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowance.tsx create mode 100644 src/react-queries/hooks/selectors/index.ts create mode 100644 src/react-queries/hooks/useAllowance.ts create mode 100644 src/react-queries/types/index.ts diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/helpers.ts b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/helpers.ts index 30ba14c2f69..e146a60d5f6 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/helpers.ts +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/helpers.ts @@ -1,71 +1,9 @@ -import type { AccountId, AssetId, ChainId } from '@shapeshiftoss/caip' -import { fromAccountId, fromAssetId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' -import { type evm, type EvmChainAdapter, evmChainIds } from '@shapeshiftoss/chain-adapters' -import { type ETHWallet, type HDWallet } from '@shapeshiftoss/hdwallet-core' -import { isLedger } from '@shapeshiftoss/hdwallet-ledger' +import { fromAssetId } from '@shapeshiftoss/caip' +import { type evm, type EvmChainAdapter } from '@shapeshiftoss/chain-adapters' +import { type ETHWallet } from '@shapeshiftoss/hdwallet-core' import type { TradeQuote } from '@shapeshiftoss/swapper' -import type { Result } from '@sniptt/monads' -import { Err, Ok } from '@sniptt/monads' import { MAX_ALLOWANCE } from 'lib/swapper/swappers/utils/constants' -import { assertGetChainAdapter } from 'lib/utils' -import { getApproveContractData, getErc20Allowance, getFees } from 'lib/utils/evm' - -export type GetAllowanceArgs = { - accountNumber: number - allowanceContract: string - chainId: ChainId - assetId: AssetId - wallet: HDWallet - accountId: AccountId -} - -export enum GetAllowanceErr { - NotEVMChain = 'NotEVMChain', - IsFeeAsset = 'IsFeeAsset', - MissingArgs = 'MissingArgs', -} - -export const getAllowance = async ({ - accountNumber, - allowanceContract, - chainId, - assetId, - wallet, - accountId, -}: GetAllowanceArgs): Promise> => { - const adapter = assertGetChainAdapter(chainId) - - if (!wallet) throw new Error('no wallet available') - - // No approval needed for selling a non-EVM asset - if (!evmChainIds.includes(chainId as EvmChainId)) { - return Err(GetAllowanceErr.NotEVMChain) - } - - // No approval needed for selling a fee asset - if (assetId === adapter.getFeeAssetId()) { - return Err(GetAllowanceErr.IsFeeAsset) - } - - const fetchUnchainedAddress = Boolean(wallet && isLedger(wallet)) - const from = await adapter.getAddress({ - wallet, - accountNumber, - pubKey: fetchUnchainedAddress ? fromAccountId(accountId).account : undefined, - }) - - const { assetReference: sellAssetContractAddress } = fromAssetId(assetId) - - const allowanceOnChainCryptoBaseUnit = await getErc20Allowance({ - address: sellAssetContractAddress, - spender: allowanceContract, - from, - chainId, - }) - - return Ok(allowanceOnChainCryptoBaseUnit) -} +import { getApproveContractData, getFees } from 'lib/utils/evm' export const getApprovalTxData = async ({ tradeQuoteStep, diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowance.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowance.tsx deleted file mode 100644 index feda28622ae..00000000000 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowance.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import type { AccountId } from '@shapeshiftoss/caip' -import type { EvmChainId } from '@shapeshiftoss/chain-adapters' -import { evmChainIds } from '@shapeshiftoss/chain-adapters' -import type { TradeQuoteStep } from '@shapeshiftoss/swapper' -import { Err, type Result } from '@sniptt/monads' -import type { QueryFunction } from '@tanstack/react-query' -import { useQuery } from '@tanstack/react-query' -import { useCallback, useMemo } from 'react' -import type { GetAllowanceArgs } from 'components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/helpers' -import { - getAllowance, - GetAllowanceErr, -} from 'components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/helpers' -import { useWallet } from 'hooks/useWallet/useWallet' - -import { useEvmBlockNumber } from './useBlockNumber' - -type QueryKeyArgs = Partial> & { blockNumber: bigint | undefined } - -const queryKey = ({ - accountNumber, - allowanceContract, - blockNumber, - chainId, - assetId, - accountId, -}: QueryKeyArgs) => - [ - 'useAllowance', - { - accountNumber, - allowanceContract, - blockNumber: blockNumber?.toString(), // manual stringify of bigint since it's not JSON serializable by default - chainId, - assetId, - accountId, - }, - ] as const - -export function useAllowance( - tradeQuoteStep: TradeQuoteStep | undefined, - sellAssetAccountId: AccountId | undefined, - watch: boolean, -) { - const { - state: { wallet }, - } = useWallet() - - const { - accountNumber, - allowanceContract, - sellAsset: { chainId, assetId }, - } = useMemo( - () => - tradeQuoteStep ?? { - accountNumber: undefined, - allowanceContract: undefined, - sellAsset: { chainId: undefined, assetId: undefined }, - }, - [tradeQuoteStep], - ) - - const queryFn: QueryFunction< - Result, - ReturnType - > = useCallback( - ({ queryKey: [_, { accountNumber, allowanceContract, chainId, assetId, accountId }] }) => { - if ( - !wallet || - accountNumber === undefined || - allowanceContract === undefined || - chainId === undefined || - assetId === undefined || - accountId === undefined - ) { - return Err(GetAllowanceErr.MissingArgs) - } - - return getAllowance({ - accountNumber, - allowanceContract, - chainId, - assetId, - accountId, - wallet, - }) - }, - [wallet], - ) - - const maybeEvmChainId = useMemo(() => { - const isEvmChainId = chainId && evmChainIds.includes(chainId as EvmChainId) - if (!isEvmChainId) return - return chainId as EvmChainId - }, [chainId]) - - const blockNumber = useEvmBlockNumber(maybeEvmChainId, watch) - - const allowanceQuery = useQuery({ - queryKey: queryKey({ - accountNumber, - allowanceContract, - blockNumber, - chainId, - assetId, - accountId: sellAssetAccountId, - }), - queryFn, - enabled: Boolean(watch && wallet && blockNumber), - }) - - return allowanceQuery -} diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowanceApproval.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowanceApproval.tsx index 6c4cf203f5f..d7627d986b9 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowanceApproval.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowanceApproval.tsx @@ -33,7 +33,6 @@ export const useAllowanceApproval = ( const { isLoading: isApprovalNeededLoading, isApprovalNeeded } = useIsApprovalNeeded( tradeQuoteStep, sellAssetAccountId, - true, ) useEffect(() => { diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalInitiallyNeeded.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalInitiallyNeeded.tsx index 159b7a916e0..5a97f68c7a6 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalInitiallyNeeded.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalInitiallyNeeded.tsx @@ -1,7 +1,6 @@ import type { AccountId } from '@shapeshiftoss/caip' -import { isEvmChainId } from '@shapeshiftoss/chain-adapters' import type { TradeQuoteStep } from '@shapeshiftoss/swapper' -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useState } from 'react' import { selectFirstHopSellAccountId, selectSecondHopSellAccountId } from 'state/slices/selectors' import { selectFirstHop, @@ -17,30 +16,13 @@ const useIsApprovalInitiallyNeededForHop = ( tradeQuoteStep: TradeQuoteStep | undefined, sellAssetAccountId: AccountId | undefined, ) => { - const { - sellAsset: { chainId }, - } = useMemo( - () => - tradeQuoteStep ?? { - sellAsset: { chainId: undefined }, - }, - [tradeQuoteStep], - ) - const [watchIsApprovalNeeded, setWatchIsApprovalNeeded] = useState( - Boolean(chainId && isEvmChainId(chainId)), - ) const [isApprovalInitiallyNeeded, setIsApprovalInitiallyNeeded] = useState() - const { isLoading, isApprovalNeeded } = useIsApprovalNeeded( - tradeQuoteStep, - sellAssetAccountId, - watchIsApprovalNeeded, - ) + const { isLoading, isApprovalNeeded } = useIsApprovalNeeded(tradeQuoteStep, sellAssetAccountId) useEffect(() => { // stop polling on first result if (!isLoading && isApprovalNeeded !== undefined) { - setWatchIsApprovalNeeded(false) setIsApprovalInitiallyNeeded(isApprovalNeeded) } }, [isApprovalNeeded, isLoading]) diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx index 3065751e609..b3d4c520d7e 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx @@ -1,54 +1,45 @@ -import type { AccountId } from '@shapeshiftoss/caip' -import { type EvmChainId, evmChainIds } from '@shapeshiftoss/chain-adapters' +import { type AccountId, fromAccountId } from '@shapeshiftoss/caip' import type { TradeQuoteStep } from '@shapeshiftoss/swapper' -import { useMemo } from 'react' -import { useAllowance } from 'components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useAllowance' +import type { Result } from '@sniptt/monads' +import { useQuery } from '@tanstack/react-query' +import { useCallback } from 'react' +import { reactQueries } from 'react-queries' +import { selectAllowanceCryptoBaseUnit } from 'react-queries/hooks/selectors' +import type { GetAllowanceErr } from 'react-queries/types' import { bn } from 'lib/bignumber/bignumber' -import { assertUnreachable } from 'lib/utils' - -import { GetAllowanceErr } from './helpers' export const useIsApprovalNeeded = ( tradeQuoteStep: TradeQuoteStep | undefined, sellAssetAccountId: AccountId | undefined, - watch: boolean, ) => { - const { isLoading, data } = useAllowance(tradeQuoteStep, sellAssetAccountId, watch) - - const isApprovalNeeded = useMemo(() => { - if (tradeQuoteStep === undefined) return undefined - - const isEvmChainId = evmChainIds.includes(tradeQuoteStep.sellAsset.chainId as EvmChainId) - - if (!isEvmChainId) return false - - if (data?.isErr()) { - const error = data.unwrapErr() - // the error type is a GetAllowanceErr enum so we can handle all cases with exhaustiveness - // checking to prevent returning the wrong value if we add more error cases - switch (error) { - case GetAllowanceErr.IsFeeAsset: - case GetAllowanceErr.NotEVMChain: - // approval not required - return false - case GetAllowanceErr.MissingArgs: - // not known yet - return undefined - default: - assertUnreachable(error) - } - } - - const allowanceOnChainCryptoBaseUnit = data?.unwrap() - return allowanceOnChainCryptoBaseUnit !== undefined - ? bn(allowanceOnChainCryptoBaseUnit).lt( - tradeQuoteStep.sellAmountIncludingProtocolFeesCryptoBaseUnit, - ) - : undefined - }, [data, tradeQuoteStep]) + const selectIsApprovalNeeded = useCallback( + (data: Result) => { + if (tradeQuoteStep === undefined) return undefined + + const allowanceCryptoBaseUnit = selectAllowanceCryptoBaseUnit(data) + return allowanceCryptoBaseUnit !== undefined + ? bn(allowanceCryptoBaseUnit).lt( + tradeQuoteStep.sellAmountIncludingProtocolFeesCryptoBaseUnit, + ) + : undefined + }, + [tradeQuoteStep], + ) + + const { data: isApprovalNeeded, isLoading: isApprovalNeededLoading } = useQuery({ + ...reactQueries.common.allowanceCryptoBaseUnit( + tradeQuoteStep?.sellAsset.assetId, + tradeQuoteStep?.allowanceContract, + sellAssetAccountId ? fromAccountId(sellAssetAccountId)?.account : undefined, + ), + refetchInterval: 15_000, + enabled: Boolean(true), + select: selectIsApprovalNeeded, + }) return { - isLoading: isApprovalNeeded === undefined || tradeQuoteStep === undefined || isLoading, + isLoading: + isApprovalNeeded === undefined || tradeQuoteStep === undefined || isApprovalNeededLoading, isApprovalNeeded, } } diff --git a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx index 79edb03de12..df65442b763 100644 --- a/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx +++ b/src/pages/ThorChainLP/components/AddLiquitity/AddLiquidityInput.tsx @@ -27,6 +27,7 @@ import { BiErrorCircle, BiSolidBoltCircle } from 'react-icons/bi' import { FaPlus } from 'react-icons/fa' import { useTranslate } from 'react-polyglot' import { reactQueries } from 'react-queries' +import { useAllowance } from 'react-queries/hooks/useAllowance' import { useIsTradingActive } from 'react-queries/hooks/useIsTradingActive' import { useQuoteEstimatedFeesQuery } from 'react-queries/hooks/useQuoteEstimatedFeesQuery' import { selectInboundAddressData } from 'react-queries/selectors' @@ -509,17 +510,10 @@ export const AddLiquidityInput: React.FC = ({ queryClient, ]) - const { data: allowanceData, isLoading: isAllowanceDataLoading } = useQuery({ - refetchInterval: 30_000, - enabled: - poolAsset && - walletSupportsOpportunity === true && - isToken(fromAssetId(poolAsset.assetId).assetReference), - ...reactQueries.common.allowanceCryptoBaseUnit( - poolAsset?.assetId, - inboundAddressesData?.router, - poolAssetAccountAddress, - ), + const { data: allowanceData, isLoading: isAllowanceDataLoading } = useAllowance({ + assetId: poolAsset?.assetId, + spender: inboundAddressesData?.router, + from: poolAssetAccountAddress, }) const isApprovalRequired = useMemo(() => { diff --git a/src/react-queries/hooks/selectors/index.ts b/src/react-queries/hooks/selectors/index.ts new file mode 100644 index 00000000000..875476bd05d --- /dev/null +++ b/src/react-queries/hooks/selectors/index.ts @@ -0,0 +1,22 @@ +import type { Result } from '@sniptt/monads' +import { GetAllowanceErr } from 'react-queries/types' +import { assertUnreachable } from 'lib/utils' + +export const selectAllowanceCryptoBaseUnit = (data: Result) => { + if (data.isErr()) { + const error = data.unwrapErr() + // the error type is a GetAllowanceErr enum so we can handle all cases with exhaustiveness + // checking to prevent returning the wrong value if we add more error cases + switch (error) { + case GetAllowanceErr.IsFeeAsset: + case GetAllowanceErr.NotEVMChain: + // No allowance for fee assets or non-EVM chains, this should not run, but just in case + return undefined + default: + assertUnreachable(error) + } + } + + const allowanceCryptoBaseUnit = data.unwrap() + return allowanceCryptoBaseUnit +} diff --git a/src/react-queries/hooks/useAllowance.ts b/src/react-queries/hooks/useAllowance.ts new file mode 100644 index 00000000000..3814d736318 --- /dev/null +++ b/src/react-queries/hooks/useAllowance.ts @@ -0,0 +1,21 @@ +import { useQuery } from '@tanstack/react-query' +import { reactQueries } from 'react-queries' + +import { selectAllowanceCryptoBaseUnit } from './selectors' + +type UseAllowanceArgs = { + assetId: string | undefined + spender: string | undefined + from: string | undefined +} + +export const useAllowance = ({ assetId, spender, from }: UseAllowanceArgs) => { + const query = useQuery({ + ...reactQueries.common.allowanceCryptoBaseUnit(assetId, spender, from), + refetchInterval: 15_000, + enabled: Boolean(assetId && spender && from), + select: selectAllowanceCryptoBaseUnit, + }) + + return query +} diff --git a/src/react-queries/index.ts b/src/react-queries/index.ts index e1d8d2a515c..cd298ed2a06 100644 --- a/src/react-queries/index.ts +++ b/src/react-queries/index.ts @@ -1,10 +1,11 @@ import { createMutationKeys, createQueryKeys, mergeQueryKeys } from '@lukemorales/query-key-factory' import { type AssetId, fromAssetId } from '@shapeshiftoss/caip' -import { CONTRACT_INTERACTION } from '@shapeshiftoss/chain-adapters' +import type { EvmChainId } from '@shapeshiftoss/chain-adapters' +import { CONTRACT_INTERACTION, evmChainIds } from '@shapeshiftoss/chain-adapters' import type { HDWallet } from '@shapeshiftoss/hdwallet-core' import { supportsETH } from '@shapeshiftoss/hdwallet-core' -import type { KnownChainIds } from '@shapeshiftoss/types' -import { Ok } from '@sniptt/monads' +import type { Result } from '@sniptt/monads' +import { Err, Ok } from '@sniptt/monads' import axios from 'axios' import { getConfig } from 'config' import type { @@ -14,19 +15,20 @@ import type { } from 'lib/swapper/swappers/ThorchainSwapper/types' import { assetIdToPoolAssetId } from 'lib/swapper/swappers/ThorchainSwapper/utils/poolAssetHelpers/poolAssetHelpers' import { thorService } from 'lib/swapper/swappers/ThorchainSwapper/utils/thorService' -import { isToken } from 'lib/utils' +import { assertGetChainAdapter } from 'lib/utils' import { assertGetEvmChainAdapter, buildAndBroadcast, getApproveContractData, getErc20Allowance, getFees, - getSupportedEvmChainIds, } from 'lib/utils/evm' import type { ThorchainBlock } from 'lib/utils/thorchain/lending/types' import type { MidgardSwapHistoryResponse } from 'lib/utils/thorchain/lp/types' import { thorchainLp } from 'pages/ThorChainLP/queries/queries' +import { GetAllowanceErr } from './types' + const common = createQueryKeys('common', { allowanceCryptoBaseUnit: ( assetId: AssetId | undefined, @@ -34,15 +36,24 @@ const common = createQueryKeys('common', { from: string | undefined, ) => ({ queryKey: ['allowanceCryptoBaseUnit', assetId, spender, from], - queryFn: async () => { + queryFn: async (): Promise> => { if (!assetId) throw new Error('assetId is required') if (!spender) throw new Error('spender is required') if (!from) throw new Error('from address is required') const { chainId, assetReference } = fromAssetId(assetId) - if (!isToken(assetReference)) return null - const supportedEvmChainIds = getSupportedEvmChainIds() - if (!supportedEvmChainIds.includes(chainId as KnownChainIds)) return null + + if (!evmChainIds.includes(chainId as EvmChainId)) { + return Err(GetAllowanceErr.NotEVMChain) + } + + // Asserts and makes the query error (i.e isError) if this errors - *not* a monadic error + const adapter = assertGetChainAdapter(chainId) + + // No approval needed for selling a fee asset + if (assetId === adapter.getFeeAssetId()) { + return Err(GetAllowanceErr.IsFeeAsset) + } const allowanceOnChainCryptoBaseUnit = await getErc20Allowance({ address: assetReference, @@ -51,7 +62,7 @@ const common = createQueryKeys('common', { chainId, }) - return allowanceOnChainCryptoBaseUnit + return Ok(allowanceOnChainCryptoBaseUnit) }, }), }) diff --git a/src/react-queries/types/index.ts b/src/react-queries/types/index.ts new file mode 100644 index 00000000000..20d75785abd --- /dev/null +++ b/src/react-queries/types/index.ts @@ -0,0 +1,4 @@ +export enum GetAllowanceErr { + NotEVMChain = 'NotEVMChain', + IsFeeAsset = 'IsFeeAsset', +} From db7f8765010274703867b5b2f547d3efb7c8bdc0 Mon Sep 17 00:00:00 2001 From: gomes <17035424+gomesalexandre@users.noreply.github.com> Date: Mon, 19 Feb 2024 15:26:39 -0800 Subject: [PATCH 08/11] feat: thorchain lending 100% repayment buffer (#6238) --- src/pages/Lending/hooks/useLendingCloseQuery.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/pages/Lending/hooks/useLendingCloseQuery.ts b/src/pages/Lending/hooks/useLendingCloseQuery.ts index 6bf73b56765..34a24313207 100644 --- a/src/pages/Lending/hooks/useLendingCloseQuery.ts +++ b/src/pages/Lending/hooks/useLendingCloseQuery.ts @@ -35,7 +35,7 @@ type UseLendingQuoteCloseQueryProps = { const selectLendingCloseQueryData = memoize( ({ - data, + data: quote, collateralAssetMarketData, repaymentAssetMarketData, repaymentPercent, @@ -45,7 +45,6 @@ const selectLendingCloseQueryData = memoize( repaymentAssetMarketData: MarketData repaymentPercent: number }): LendingQuoteClose => { - const quote = data const userCurrencyToUsdRate = selectUserCurrencyToUsdRate(store.getState()) const quoteLoanCollateralDecreaseCryptoPrecision = fromThorBaseUnit( @@ -105,8 +104,14 @@ const selectLendingCloseQueryData = memoize( bn(quoteOutboundDelayMs).plus(quoteInboundConfirmationMs), ).toNumber() - const repaymentAmountCryptoPrecision = fromThorBaseUnit(quote.expected_amount_in).toString() - const repaymentAmountFiatUserCurrency = fromThorBaseUnit(data.expected_amount_in) + // Add a 1% buffer to the expected_amount_in to account for the fact that the amount THOR returns to us *should* guarantee + // a collateral refund, but sometimes doesn't + const safeExpectedAmountIn = bnOrZero(quote.expected_amount_in) + .times(repaymentPercent === 100 ? '1.01' : '1') + .toFixed() + + const repaymentAmountCryptoPrecision = fromThorBaseUnit(safeExpectedAmountIn).toString() + const repaymentAmountFiatUserCurrency = fromThorBaseUnit(safeExpectedAmountIn) .times(repaymentAssetMarketData.price) .toString() const repaymentAmountFiatUsd = bn(repaymentAmountFiatUserCurrency) From a7ca22c3fd06737006f9e2f0d86286e66a5963e6 Mon Sep 17 00:00:00 2001 From: Apotheosis <0xapotheosis@gmail.com> Date: Tue, 20 Feb 2024 17:51:29 +1100 Subject: [PATCH 09/11] fix: trades from native assets & utxos (#6260) fix: native allowance --- .../MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx index b3d4c520d7e..2b885d61738 100644 --- a/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx +++ b/src/components/MultiHopTrade/components/MultiHopTradeConfirm/hooks/useIsApprovalNeeded.tsx @@ -21,7 +21,7 @@ export const useIsApprovalNeeded = ( ? bn(allowanceCryptoBaseUnit).lt( tradeQuoteStep.sellAmountIncludingProtocolFeesCryptoBaseUnit, ) - : undefined + : false }, [tradeQuoteStep], ) From d4e6b47f9551e78657e7a3cd2ac95e34c4e0f74d Mon Sep 17 00:00:00 2001 From: woody <125113430+woodenfurniture@users.noreply.github.com> Date: Wed, 21 Feb 2024 10:14:17 +1100 Subject: [PATCH 10/11] fix: allow quote sell amounts to be lower than user input sell amount (#6265) * fix: allow quote sell amounts to be lower than user input sell amount * fix: fix logic, add a teeny tiny thrshold so cowswap works --- src/state/apis/swapper/helpers/validateTradeQuote.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/state/apis/swapper/helpers/validateTradeQuote.ts b/src/state/apis/swapper/helpers/validateTradeQuote.ts index 8ba75e8ebd3..f18004f1b8e 100644 --- a/src/state/apis/swapper/helpers/validateTradeQuote.ts +++ b/src/state/apis/swapper/helpers/validateTradeQuote.ts @@ -254,9 +254,11 @@ export const validateTradeQuote = async ( return false })() - // ensure the trade is not selling an amount higher than the user input - const invalidQuoteSellAmount = - inputSellAmountCryptoBaseUnit !== firstHop.sellAmountIncludingProtocolFeesCryptoBaseUnit + // Ensure the trade is not selling an amount higher than the user input, within a very safe threshold. + // Threshold is required because cowswap sometimes quotes a sell amount a teeny-tiny bit more than you input. + const invalidQuoteSellAmount = bn(inputSellAmountCryptoBaseUnit) + .times('1.0000000000001') + .lt(firstHop.sellAmountIncludingProtocolFeesCryptoBaseUnit) return { errors: [ From b4773861a713f379dc79d2c0b709d698a0d0e470 Mon Sep 17 00:00:00 2001 From: woody <125113430+woodenfurniture@users.noreply.github.com> Date: Thu, 22 Feb 2024 02:11:26 +1100 Subject: [PATCH 11/11] fix: consolidate matching buy account id selection logic (#6268) --- .../MultiHopTrade/hooks/useAccountIds.tsx | 35 ++------------ .../useGetTradeQuotes/useGetTradeQuotes.tsx | 5 +- .../MultiHopTrade/hooks/useReceiveAddress.tsx | 23 +++++---- src/state/slices/tradeInputSlice/selectors.ts | 48 +++++++++++++------ 4 files changed, 51 insertions(+), 60 deletions(-) diff --git a/src/components/MultiHopTrade/hooks/useAccountIds.tsx b/src/components/MultiHopTrade/hooks/useAccountIds.tsx index e90df7366c4..85f82ac7d86 100644 --- a/src/components/MultiHopTrade/hooks/useAccountIds.tsx +++ b/src/components/MultiHopTrade/hooks/useAccountIds.tsx @@ -1,12 +1,6 @@ import type { AccountId } from '@shapeshiftoss/caip' -import { useCallback, useMemo } from 'react' -import { - selectAccountIdByAccountNumberAndChainId, - selectAccountNumberByAccountId, - selectFirstHopSellAccountId, - selectInputBuyAsset, - selectLastHopBuyAccountId, -} from 'state/slices/selectors' +import { useCallback } from 'react' +import { selectFirstHopSellAccountId, selectLastHopBuyAccountId } from 'state/slices/selectors' import { tradeInput } from 'state/slices/tradeInputSlice/tradeInputSlice' import { useAppDispatch, useAppSelector } from 'state/store' @@ -19,31 +13,8 @@ export const useAccountIds = (): { const dispatch = useAppDispatch() // Default sellAssetAccountId selection - const sellAssetAccountId = useAppSelector(selectFirstHopSellAccountId) - const sellAssetAccountNumberFilter = useMemo( - () => ({ accountId: sellAssetAccountId }), - [sellAssetAccountId], - ) - const sellAssetAccountNumber = useAppSelector(state => - selectAccountNumberByAccountId(state, sellAssetAccountNumberFilter), - ) - - // Default buyAssetAccountId selection - - const accountIdsByAccountNumberAndChainId = useAppSelector( - selectAccountIdByAccountNumberAndChainId, - ) - const inputBuyAsset = useAppSelector(selectInputBuyAsset) - const maybeMatchingBuyAccountId = - accountIdsByAccountNumberAndChainId[sellAssetAccountNumber as number]?.[inputBuyAsset?.chainId] - - // We always default the buy asset account to be synchronized with the sellAssetAccountNumber - // - if this isn't possible, i.e there is no matching account number on the buy side, we default to the highest balance - // - if this was to fail for any reason, we default to the first account number as a default - const buyAssetAccountId = useAppSelector(state => - selectLastHopBuyAccountId(state, { accountId: maybeMatchingBuyAccountId }), - ) + const buyAssetAccountId = useAppSelector(selectLastHopBuyAccountId) // Setters - the selectors above initially select a *default* value, but eventually onAccountIdChange may fire if the user changes the account diff --git a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx index f498ce16001..396cc4e0f26 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx +++ b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx @@ -124,10 +124,7 @@ export const useGetTradeQuotes = () => { const isDebouncing = debouncedSellAmountCryptoPrecision !== sellAmountCryptoPrecision const sellAccountId = useAppSelector(selectFirstHopSellAccountId) - // No need to pass a sellAssetAccountId to synchronize the buy account here - by the time this is called, we already have a valid buyAccountId - const buyAccountId = useAppSelector(state => - selectLastHopBuyAccountId(state, { accountId: undefined }), - ) + const buyAccountId = useAppSelector(selectLastHopBuyAccountId) const userslippageTolerancePercentageDecimal = useAppSelector(selectUserSlippagePercentageDecimal) diff --git a/src/components/MultiHopTrade/hooks/useReceiveAddress.tsx b/src/components/MultiHopTrade/hooks/useReceiveAddress.tsx index abdbf45fbbb..4da30b6e32c 100644 --- a/src/components/MultiHopTrade/hooks/useReceiveAddress.tsx +++ b/src/components/MultiHopTrade/hooks/useReceiveAddress.tsx @@ -7,7 +7,6 @@ import { useWallet } from 'hooks/useWallet/useWallet' import { selectPortfolioAccountMetadataByAccountId } from 'state/slices/portfolioSlice/selectors' import { isUtxoAccountId } from 'state/slices/portfolioSlice/utils' import { - selectFirstHopSellAccountId, selectInputBuyAsset, selectLastHopBuyAccountId, selectManualReceiveAddress, @@ -39,11 +38,7 @@ export const useReceiveAddress = ({ // Selectors const buyAsset = useAppSelector(selectInputBuyAsset) - const sellAssetAccountId = useAppSelector(selectFirstHopSellAccountId) - - const buyAccountId = useAppSelector(state => - selectLastHopBuyAccountId(state, { accountId: sellAssetAccountId }), - ) + const buyAccountId = useAppSelector(selectLastHopBuyAccountId) const buyAccountMetadata = useAppSelector(state => selectPortfolioAccountMetadataByAccountId(state, { accountId: buyAccountId }), ) @@ -51,9 +46,15 @@ export const useReceiveAddress = ({ const getReceiveAddressFromBuyAsset = useCallback( async (buyAsset: Asset) => { - if (!wallet) return - if (!buyAccountId) return - if (!buyAccountMetadata) return + if (!wallet) { + return + } + if (!buyAccountId) { + return + } + if (!buyAccountMetadata) { + return + } if (isUtxoAccountId(buyAccountId) && !buyAccountMetadata.accountType) throw new Error(`Missing accountType for UTXO account ${buyAccountId}`) const buyAssetChainId = buyAsset.chainId @@ -62,7 +63,9 @@ export const useReceiveAddress = ({ * do NOT remove * super dangerous - don't use the wrong bip44 params to generate receive addresses */ - if (buyAssetChainId !== buyAssetAccountChainId) return + if (buyAssetChainId !== buyAssetAccountChainId) { + return + } const receiveAddress = await getReceiveAddress({ asset: buyAsset, wallet, diff --git a/src/state/slices/tradeInputSlice/selectors.ts b/src/state/slices/tradeInputSlice/selectors.ts index a517033982f..80060a736cb 100644 --- a/src/state/slices/tradeInputSlice/selectors.ts +++ b/src/state/slices/tradeInputSlice/selectors.ts @@ -4,14 +4,17 @@ import { bn } from 'lib/bignumber/bignumber' import { toBaseUnit } from 'lib/math' import type { ReduxState } from 'state/reducer' import { createDeepEqualOutputSelector } from 'state/selector-utils' -import { selectAccountIdParamFromFilter } from 'state/selectors' import { selectPortfolioCryptoBalanceBaseUnitByFilter, selectWalletAccountIds, } from '../common-selectors' import { selectCryptoMarketData, selectUserCurrencyToUsdRate } from '../marketDataSlice/selectors' -import { selectPortfolioAssetAccountBalancesSortedUserCurrency } from '../portfolioSlice/selectors' +import { + selectAccountIdByAccountNumberAndChainId, + selectPortfolioAccountMetadata, + selectPortfolioAssetAccountBalancesSortedUserCurrency, +} from '../portfolioSlice/selectors' import { getFirstAccountIdByChainId, getHighestUserCurrencyBalanceAccountByAssetId, @@ -105,23 +108,40 @@ export const selectLastHopSellAccountId = selectFirstHopSellAccountId export const selectLastHopBuyAccountId = createSelector( selectTradeInput, selectInputBuyAsset, - selectPortfolioAssetAccountBalancesSortedUserCurrency, selectWalletAccountIds, - selectAccountIdParamFromFilter, - (tradeInput, buyAsset, accountIdAssetValues, accountIds, maybeMatchingBuyAccountId) => { + selectAccountIdByAccountNumberAndChainId, + selectFirstHopSellAccountId, + selectPortfolioAccountMetadata, + ( + tradeInput, + buyAsset, + accountIds, + accountIdByAccountNumberAndChainId, + firstHopSellAccountId, + accountMetadata, + ) => { // return the users selection if it exists - if (tradeInput.buyAssetAccountId) return tradeInput.buyAssetAccountId - // an AccountId was found matching the sell asset's account number, return it - if (maybeMatchingBuyAccountId) return maybeMatchingBuyAccountId + if (tradeInput.buyAssetAccountId) { + return tradeInput.buyAssetAccountId + } - const highestFiatBalanceBuyAccountId = getHighestUserCurrencyBalanceAccountByAssetId( - accountIdAssetValues, - buyAsset.assetId, - ) - const firstBuyAssetAccountId = getFirstAccountIdByChainId(accountIds, buyAsset.chainId) + // maybe convert the account id to an account number + const maybeMatchingBuyAccountNumber = firstHopSellAccountId + ? accountMetadata[firstHopSellAccountId]?.bip44Params.accountNumber + : undefined + + // maybe convert account number to account id on the buy asset chain + const maybeMatchingBuyAccountId = maybeMatchingBuyAccountNumber + ? accountIdByAccountNumberAndChainId[maybeMatchingBuyAccountNumber]?.[buyAsset.chainId] + : undefined + + // an AccountId was found matching the sell asset's account number and chainId, return it + if (maybeMatchingBuyAccountId) { + return maybeMatchingBuyAccountId + } // otherwise return a sane default - return highestFiatBalanceBuyAccountId ?? firstBuyAssetAccountId + return getFirstAccountIdByChainId(accountIds, buyAsset.chainId) }, )