Skip to content

Commit

Permalink
feat: rfox refactor (#7079)
Browse files Browse the repository at this point in the history
  • Loading branch information
gomesalexandre authored Jun 11, 2024
1 parent 0652b49 commit 1eb71c3
Show file tree
Hide file tree
Showing 40 changed files with 1,742 additions and 1,524 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"@keepkey/hdwallet-keepkey-rest": "1.40.42",
"@keepkey/keepkey-sdk": "0.2.57",
"@lifi/sdk": "^2.5.0",
"@lukemorales/query-key-factory": "^1.3.2",
"@lukemorales/query-key-factory": "^1.3.4",
"@metamask/detect-provider": "^1.2.0",
"@react-spring/web": "^9.5.2",
"@reduxjs/toolkit": "^1.9.3",
Expand All @@ -111,7 +111,7 @@
"@shapeshiftoss/types": "workspace:^",
"@shapeshiftoss/unchained-client": "workspace:^",
"@sniptt/monads": "^0.5.10",
"@tanstack/react-query": "^5.0.5",
"@tanstack/react-query": "^5.40.1",
"@uniswap/sdk": "^3.0.3",
"@uniswap/sdk-core": "^4.0.9",
"@uniswap/v3-sdk": "^3.10.0",
Expand Down
82 changes: 82 additions & 0 deletions src/hooks/queries/useEvmFees.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { ChainId } from '@shapeshiftoss/caip'
import { skipToken, useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { reactQueries } from 'react-queries'
import { selectEvmFees } from 'react-queries/selectors'
import { useWallet } from 'hooks/useWallet/useWallet'
import {
assertGetEvmChainAdapter,
getFeesWithWallet,
type GetFeesWithWalletArgs,
isGetFeesWithWalletArgs,
} from 'lib/utils/evm'
import {
selectFeeAssetByChainId,
selectMarketDataByAssetIdUserCurrency,
} from 'state/slices/selectors'
import { useAppSelector } from 'state/store'

type UseEvmFeesProps = Omit<
GetFeesWithWalletArgs,
'wallet' | 'adapter' | 'accountNumber' | 'data'
> & {
accountNumber: number | undefined
data: string | undefined
chainId: ChainId
enabled?: boolean
staleTime?: number
refetchInterval: number | false | undefined
refetchIntervalInBackground: boolean
}

export const useEvmFees = (props: UseEvmFeesProps) => {
const wallet = useWallet().state.wallet

const { enabled, staleTime, refetchInterval, refetchIntervalInBackground, input } = useMemo(
() => {
const {
enabled = true,
staleTime,
refetchInterval,
refetchIntervalInBackground,
...input
} = props

return {
enabled,
staleTime,
refetchInterval,
refetchIntervalInBackground,
input,
}
},
// eslint-disable-next-line react-hooks/exhaustive-deps
Object.values(props),
)

const adapter = useMemo(() => assertGetEvmChainAdapter(input.chainId), [input.chainId])

const feeAsset = useAppSelector(state => selectFeeAssetByChainId(state, input.chainId))
const feeAssetMarketData = useAppSelector(state =>
selectMarketDataByAssetIdUserCurrency(state, feeAsset?.assetId ?? ''),
)

const getFeesWithWalletInput = useMemo(
() => ({ ...input, adapter, wallet }),
[adapter, input, wallet],
)

const query = useQuery({
queryKey: reactQueries.common.evmFees(input).queryKey,
queryFn:
isGetFeesWithWalletArgs(getFeesWithWalletInput) && enabled
? () => getFeesWithWallet(getFeesWithWalletInput)
: skipToken,
select: feeAsset ? fees => selectEvmFees(fees, feeAsset, feeAssetMarketData) : undefined,
staleTime,
refetchInterval,
refetchIntervalInBackground,
})

return query
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ethChainId } from '@shapeshiftoss/caip'
import { type HDWallet, supportsETH } from '@shapeshiftoss/hdwallet-core'
import type {
GetEvmTradeQuoteInput,
SingleHopTradeQuoteSteps,
Expand All @@ -14,6 +15,23 @@ import { v4 as uuid } from 'uuid'
import { fetchArbitrumBridgeSwap } from '../utils/fetchArbitrumBridgeSwap'
import { assertValidTrade } from '../utils/helpers'

export type GetEvmTradeQuoteInputWithWallet = Omit<GetEvmTradeQuoteInput, 'supportsEIP1559'> & {
wallet: HDWallet
}

export const getTradeQuoteWithWallet = async (inputWithWallet: GetEvmTradeQuoteInputWithWallet) => {
const { wallet, ...input } = inputWithWallet
const supportsEIP1559 = supportsETH(wallet) && (await wallet.ethSupportsEIP1559())

return getTradeQuote({
...input,
sellAsset: input.sellAsset,
buyAsset: input.buyAsset,
accountNumber: input.accountNumber,
supportsEIP1559,
})
}

export async function getTradeQuote(
input: GetEvmTradeQuoteInput,
): Promise<Result<TradeQuote, SwapErrorRight>> {
Expand Down
2 changes: 2 additions & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ export type DeepPick<T, K extends string> = T extends object
export type RequireFields<T, K extends keyof T> = {
[P in keyof T]: P extends K ? NonNullable<T[P]> : T[P]
}

export type PartialFields<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
21 changes: 20 additions & 1 deletion src/lib/utils/evm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { ContractType } from 'contracts/types'
import { encodeFunctionData, getAddress } from 'viem'
import { getChainAdapterManager } from 'context/PluginProvider/chainAdapterSingleton'
import { bn, bnOrZero } from 'lib/bignumber/bignumber'
import type { PartialFields } from 'lib/types'

import { getSupportedChainIdsByChainNamespace } from '.'

Expand Down Expand Up @@ -81,16 +82,34 @@ type GetFeesArgs = GetFeesCommonArgs & {
supportsEIP1559: boolean
}

type GetFeesWithWalletArgs = GetFeesCommonArgs & {
export type GetFeesWithWalletArgs = GetFeesCommonArgs & {
accountNumber: number
wallet: HDWallet
}

export type MaybeGetFeesWithWalletArgs = PartialFields<
Omit<GetFeesWithWalletArgs, 'wallet'>,
'adapter' | 'accountNumber' | 'data'
> & {
wallet: HDWallet | null
}

export type Fees = evm.Fees & {
gasLimit: string
networkFeeCryptoBaseUnit: string
}

export type EvmFees = {
fees: Fees
txFeeFiat: string
networkFeeCryptoBaseUnit: string
}

export const isGetFeesWithWalletArgs = (
input: MaybeGetFeesWithWalletArgs,
): input is GetFeesWithWalletArgs =>
Boolean(input.adapter && input.accountNumber !== undefined && input.wallet && input.data)

export const getFeesWithWallet = async (args: GetFeesWithWalletArgs): Promise<Fees> => {
const { accountNumber, adapter, wallet, ...rest } = args

Expand Down
2 changes: 1 addition & 1 deletion src/pages/Lending/Pool/components/Repay/RepayInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ export const RepayInput = ({
assetId: repaymentAsset?.assetId,
spender: inboundAddressData?.router,
from: userAddress,
amount: toBaseUnit(
amountCryptoBaseUnit: toBaseUnit(
// Add 5% buffer to the repayment allowance to avoid asset rates fluctuations ending up in more asset needed to repay
confirmedQuote?.repaymentAmountCryptoPrecision ?? 0,
repaymentAsset?.precision ?? 0,
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Lending/hooks/useLendingCloseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export const useLendingQuoteCloseQuery = ({
repaymentAccountId: _repaymentAccountId,
collateralAccountId: _collateralAccountId,
enabled = true,
}: UseLendingQuoteCloseQueryProps & QueryObserverOptions) => {
}: UseLendingQuoteCloseQueryProps & Pick<QueryObserverOptions, 'enabled'>) => {
const { data: lendingPositionData } = useLendingPositionData({
assetId: _collateralAssetId,
accountId: _collateralAccountId,
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Lending/hooks/useLendingQuoteQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export const useLendingQuoteOpenQuery = ({
borrowAssetId: _borrowAssetId,
depositAmountCryptoPrecision: _depositAmountCryptoPrecision,
enabled = true,
}: UseLendingQuoteQueryProps & QueryObserverOptions) => {
}: UseLendingQuoteQueryProps & Pick<QueryObserverOptions, 'enabled'>) => {
const [_borrowAssetReceiveAddress, setBorrowAssetReceiveAddress] = useState<string | null>(null)

const wallet = useWallet().state.wallet
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Lending/hooks/useRepaymentLockData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const useRepaymentLockData = ({
// Let the parent pass its own query options
// enabled will be used in conjunction with this hook's own isRepaymentLockQueryEnabled to determine whether or not to run the query
enabled = true,
}: UseLendingPositionDataProps & QueryObserverOptions) => {
}: UseLendingPositionDataProps & Pick<QueryObserverOptions, 'enabled'>) => {
const { data: blockHeight } = useQuery({
// @lukemorales/query-key-factory only returns queryFn and queryKey - all others will be ignored in the returned object
// We use the block query to get the current height, so we obviously need to mark it stale at the end of each THOR block
Expand Down
22 changes: 5 additions & 17 deletions src/pages/RFOX/components/ChangeAddress/ChangeAddress.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { fromAccountId } from '@shapeshiftoss/caip'
import { useQueryClient } from '@tanstack/react-query'
import { foxStakingV1Abi } from 'contracts/abis/FoxStakingV1'
import { RFOX_PROXY_CONTRACT_ADDRESS } from 'contracts/constants'
import { AnimatePresence } from 'framer-motion'
import React, { lazy, Suspense, useCallback, useState } from 'react'
import { MemoryRouter, Route, Switch, useLocation } from 'react-router'
import { makeSuspenseful } from 'utils/makeSuspenseful'
import type { Address } from 'viem'
import { getAddress } from 'viem'
import { arbitrum } from 'viem/chains'
import { useReadContract } from 'wagmi'
import { useStakingInfoQuery } from 'pages/RFOX/hooks/useStakingInfoQuery'

import type { ChangeAddressRouteProps, RfoxChangeAddressQuote } from './types'
import { ChangeAddressRoutePaths } from './types'
Expand Down Expand Up @@ -61,17 +56,10 @@ export const ChangeAddressRoutes: React.FC<ChangeAddressRouteProps> = ({ headerC
const [changeAddressTxid, setChangeAddressTxid] = useState<string | undefined>()
const [confirmedQuote, setConfirmedQuote] = useState<RfoxChangeAddressQuote | undefined>()

const { queryKey: stakingInfoQueryKey } = useReadContract({
abi: foxStakingV1Abi,
address: RFOX_PROXY_CONTRACT_ADDRESS,
functionName: 'stakingInfo',
args: [
// actually defined by the time we actually consume the queryKey
confirmedQuote
? getAddress(fromAccountId(confirmedQuote.stakingAssetAccountId).account)
: ('' as Address),
],
chainId: arbitrum.id,
const { queryKey: stakingInfoQueryKey } = useStakingInfoQuery({
stakingAssetAccountAddress: confirmedQuote
? fromAccountId(confirmedQuote.stakingAssetAccountId).account
: undefined,
})

const handleTxConfirmed = useCallback(async () => {
Expand Down
50 changes: 26 additions & 24 deletions src/pages/RFOX/components/ChangeAddress/ChangeAddressConfirm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ import {
} from '@chakra-ui/react'
import { fromAccountId, fromAssetId } from '@shapeshiftoss/caip'
import { CONTRACT_INTERACTION } from '@shapeshiftoss/chain-adapters'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useMutation } from '@tanstack/react-query'
import { foxStakingV1Abi } from 'contracts/abis/FoxStakingV1'
import { RFOX_PROXY_CONTRACT_ADDRESS } from 'contracts/constants'
import { useCallback, useMemo } from 'react'
import { useTranslate } from 'react-polyglot'
import { reactQueries } from 'react-queries'
import { useHistory } from 'react-router'
import { encodeFunctionData } from 'viem'
import { Amount } from 'components/Amount/Amount'
import type { RowProps } from 'components/Row/Row'
import { Row } from 'components/Row/Row'
import { SlideTransition } from 'components/SlideTransition'
import { RawText } from 'components/Text'
import { useEvmFees } from 'hooks/queries/useEvmFees'
import { useWallet } from 'hooks/useWallet/useWallet'
import { middleEllipsis } from 'lib/utils'
import {
Expand All @@ -36,7 +36,6 @@ import {
selectAccountNumberByAccountId,
selectAssetById,
selectFeeAssetByChainId,
selectMarketDataByAssetIdUserCurrency,
selectTxById,
} from 'state/slices/selectors'
import { serializeTxIndex } from 'state/slices/txHistorySlice/utils'
Expand Down Expand Up @@ -66,8 +65,9 @@ export const ChangeAddressConfirm: React.FC<
const feeAsset = useAppSelector(state =>
selectFeeAssetByChainId(state, fromAssetId(confirmedQuote.stakingAssetId).chainId),
)
const feeAssetMarketData = useAppSelector(state =>
selectMarketDataByAssetIdUserCurrency(state, feeAsset?.assetId ?? ''),
const adapter = useMemo(
() => (feeAsset ? assertGetEvmChainAdapter(fromAssetId(feeAsset.assetId).chainId) : undefined),
[feeAsset],
)

const stakingAssetAccountAddress = useMemo(
Expand All @@ -93,28 +93,25 @@ export const ChangeAddressConfirm: React.FC<
})
}, [confirmedQuote.newRuneAddress])

const isGetChangeAddressFeesEnabled = useMemo(
() => Boolean(wallet && feeAsset && feeAssetMarketData),
[wallet, feeAsset, feeAssetMarketData],
const changeAddressFeesQueryInput = useMemo(
() => ({
to: RFOX_PROXY_CONTRACT_ADDRESS,
chainId: fromAssetId(confirmedQuote.stakingAssetId).chainId,
accountNumber: stakingAssetAccountNumber,
data: callData,
value: '0',
}),
[callData, confirmedQuote.stakingAssetId, stakingAssetAccountNumber],
)

const {
data: changeAddressFees,
isLoading: isChangeAddressFeesLoading,
isSuccess: isChangeAddressFeesSuccess,
} = useQuery({
...reactQueries.common.evmFees({
to: RFOX_PROXY_CONTRACT_ADDRESS,
from: stakingAssetAccountAddress,
accountNumber: stakingAssetAccountNumber!, // see isGetChangeAddressFeesEnabled
data: callData,
value: '0', // contract call
wallet: wallet!, // see isGetChangeAddressFeesEnabled
feeAsset: feeAsset!, // see isGetChangeAddressFeesEnabled
feeAssetMarketData: feeAssetMarketData!, // see isGetChangeAddressFeesEnabled
}),
} = useEvmFees({
...changeAddressFeesQueryInput,
enabled: true,
staleTime: 30_000,
enabled: isGetChangeAddressFeesEnabled,
// Ensures fees are refetched at an interval, including when the app is in the background
refetchIntervalInBackground: true,
// Yeah this is arbitrary but come on, Arb is cheap
Expand All @@ -136,9 +133,14 @@ export const ChangeAddressConfirm: React.FC<
isSuccess: isChangeAddressMutationSuccess,
} = useMutation({
mutationFn: async () => {
if (!wallet || stakingAssetAccountNumber === undefined || !stakingAsset || !callData) return

const adapter = assertGetEvmChainAdapter(stakingAsset.chainId)
if (
!wallet ||
stakingAssetAccountNumber === undefined ||
!stakingAsset ||
!callData ||
!adapter
)
return

const buildCustomTxInput = await createBuildCustomTxInput({
accountNumber: stakingAssetAccountNumber,
Expand Down Expand Up @@ -221,7 +223,7 @@ export const ChangeAddressConfirm: React.FC<
<IconButton onClick={handleGoBack} variant='ghost' aria-label='back' icon={backIcon} />
</Flex>
<Flex textAlign='center'>{translate('common.confirm')}</Flex>
<Flex flex={1}></Flex>
<Flex flex={1} />
</CardHeader>
<CardBody>
<Stack spacing={6}>{changeAddressCard}</Stack>
Expand Down
Loading

0 comments on commit 1eb71c3

Please sign in to comment.