Skip to content

Commit

Permalink
useSwapPrice
Browse files Browse the repository at this point in the history
  • Loading branch information
SamueleA committed Oct 7, 2024
1 parent 4736cee commit 42831d6
Show file tree
Hide file tree
Showing 5 changed files with 404 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import {
useBalances,
useContractInfo,
useCoinPrices,
useSwapQuotes,
useSwapPrices,
useSwapQuote,
compareAddress,
TRANSACTION_CONFIRMATIONS_DEFAULT,
sendTransactions,
SwapQuotesWithCurrencyInfo
SwapPricesWithCurrencyInfo
} from '@0xsequence/kit'
import { findSupportedNetwork } from '@0xsequence/network'
import Fuse from 'fuse.js'
Expand Down Expand Up @@ -75,18 +76,30 @@ export const PayWithCrypto = ({ settings, disableButtons, setDisableButtons }: P

const { data: currencyInfoData, isLoading: isLoadingCurrencyInfo } = useContractInfo(chainId, currencyAddress)

const {
data: swapQuotes = [],
isLoading: swapQuotesIsLoading,
isError: swapQuotesIsError
} = useSwapQuotes({
const { data: swapPrices = [], isLoading: swapPricesIsLoading } = useSwapPrices({
userAddress: userAddress ?? '',
currencyAddress: settings?.currencyAddress,
buyCurrencyAddress: settings?.currencyAddress,
chainId: chainId,
currencyAmount: price,
buyAmount: price,
withContractInfo: true
})

const disableSwapQuote = !selectedCurrency || compareAddress(settings.currencyAddress, selectedCurrency || '')

const { data: swapQuote, isLoading: isLoadingSwapQuote } = useSwapQuote(
{
userAddress: userAddress ?? '',
buyCurrencyAddress: settings?.currencyAddress,
buyAmount: price,
chainId: chainId,
sellCurrencyAddress: selectedCurrency || '',
includeApprove: true
},
{
disabled: !selectedCurrency
}
)

const nativeToken = [
{
chainId,
Expand All @@ -95,24 +108,29 @@ export const PayWithCrypto = ({ settings, disableButtons, setDisableButtons }: P
]

const swapTokens = [
...swapQuotes.map(quote => ({
...swapPrices.map(price => ({
chainId,
contractAddress: quote.info?.address || ''
contractAddress: price.info?.address || ''
}))
]

const { data: mainCoinPrice = [], isLoading: mainCoinsPricesIsLoading } = useCoinPrices([...nativeToken])

const disableCoinPricesQuery = swapQuotesIsLoading
const disableCoinPricesQuery = swapPricesIsLoading

const { data: swapTokensPrices = [], isLoading: swapTokensPricesIsLoading } = useCoinPrices(
[...swapTokens],
disableCoinPricesQuery
)

const isLoading = allowanceIsLoading || currencyBalanceIsLoading || isLoadingCurrencyInfo || mainCoinsPricesIsLoading
const isLoading =
allowanceIsLoading ||
currencyBalanceIsLoading ||
isLoadingCurrencyInfo ||
mainCoinsPricesIsLoading ||
(!disableSwapQuote && isLoadingSwapQuote)

const swapsIsLoading = swapQuotesIsLoading || swapTokensPricesIsLoading
const swapsIsLoading = swapPricesIsLoading || swapTokensPricesIsLoading

interface IndexedData {
index: number
Expand All @@ -128,12 +146,12 @@ export const PayWithCrypto = ({ settings, disableButtons, setDisableButtons }: P
symbol: currencyInfoData?.symbol || '',
currencyAddress
},
...swapQuotes.map((quote, index) => {
...swapPrices.map((price, index) => {
return {
index: index + 1,
name: quote.info?.name || 'Unknown',
symbol: quote.info?.symbol || '',
currencyAddress: quote.info?.address || ''
name: price.info?.name || 'Unknown',
symbol: price.info?.symbol || '',
currencyAddress: price.info?.address || ''
}
})
]
Expand Down Expand Up @@ -223,8 +241,8 @@ export const PayWithCrypto = ({ settings, disableButtons, setDisableButtons }: P
setDisableButtons(false)
}

const onClickPurchaseSwap = async (swapQuote: SwapQuotesWithCurrencyInfo) => {
if (!walletClient || !userAddress || !publicClient || !userAddress || !connector) {
const onClickPurchaseSwap = async (swapPrice: SwapPricesWithCurrencyInfo) => {
if (!walletClient || !userAddress || !publicClient || !userAddress || !connector || !swapQuote) {
return
}

Expand All @@ -242,27 +260,27 @@ export const PayWithCrypto = ({ settings, disableButtons, setDisableButtons }: P
args: [targetContractAddress, price]
})

const isSwapNativeToken = compareAddress(currencyAddress, swapQuote.quote.currencyAddress)
const isSwapNativeToken = compareAddress(currencyAddress, swapPrice.price.currencyAddress)

const transactions = [
// Swap quote optional approve step
...(swapQuote.quote.approveData && !isSwapNativeToken
...(swapQuote?.approveData && !isSwapNativeToken
? [
{
to: swapQuote.quote.currencyAddress as Hex,
data: swapQuote.quote.approveData as Hex,
to: swapPrice.price.currencyAddress as Hex,
data: swapQuote.approveData as Hex,
chain: chainId
}
]
: []),
// Swap quote tx
{
to: swapQuote.quote.to as Hex,
data: swapQuote.quote.transactionData as Hex,
to: swapQuote.to as Hex,
data: swapQuote.transactionData as Hex,
chain: chainId,
...(isSwapNativeToken
? {
value: BigInt(swapQuote.quote.price)
value: BigInt(swapQuote.price)
}
: {})
},
Expand Down Expand Up @@ -350,25 +368,25 @@ export const PayWithCrypto = ({ settings, disableButtons, setDisableButtons }: P
)
const exchangeRate = foundCoinPrice?.price?.value || 0

const swapQuote = swapQuotes?.find(quote => compareAddress(quote.info?.address || '', coin.currencyAddress))
const swapPrice = swapPrices?.find(price => compareAddress(price.info?.address || '', coin.currencyAddress))
const currencyInfoNotFound =
!swapQuote || !swapQuote.info || swapQuote?.info?.decimals === undefined || !swapQuote.balance?.balance
!swapPrice || !swapPrice.info || swapPrice?.info?.decimals === undefined || !swapPrice.balance?.balance
if (currencyInfoNotFound || !enableSwapPayments) {
return null
}
const swapQuotePriceFormatted = formatUnits(BigInt(swapQuote.quote.price), swapQuote.info?.decimals || 18)
const balanceFormatted = formatUnits(BigInt(swapQuote.balance?.balance || 0), swapQuote.info?.decimals || 18)
const swapQuoteAddress = swapQuote.info?.address || ''
const swapQuotePriceFormatted = formatUnits(BigInt(swapPrice.price.price), swapPrice.info?.decimals || 18)
const balanceFormatted = formatUnits(BigInt(swapPrice.balance?.balance || 0), swapPrice.info?.decimals || 18)
const swapQuoteAddress = swapPrice.info?.address || ''

const priceFiat = (exchangeRate * Number(swapQuotePriceFormatted)).toFixed(2)

return (
<CryptoOption
key={swapQuoteAddress}
currencyName={swapQuote.info?.name || 'Unknown'}
currencyName={swapPrice.info?.name || 'Unknown'}
chainId={chainId}
iconUrl={swapQuote.info?.logoURI}
symbol={swapQuote.info?.symbol || ''}
iconUrl={swapPrice.info?.logoURI}
symbol={swapPrice.info?.symbol || ''}
onClick={() => {
setSelectedCurrency(swapQuoteAddress)
}}
Expand All @@ -387,10 +405,10 @@ export const PayWithCrypto = ({ settings, disableButtons, setDisableButtons }: P
}

const onClickPurchase = () => {
if (selectedCurrency === currencyAddress) {
if (compareAddress(selectedCurrency || '', currencyAddress)) {
onPurchaseMainCurrency()
} else {
const foundSwap = swapQuotes?.find(quote => quote.info?.address === selectedCurrency)
const foundSwap = swapPrices?.find(price => price.info?.address === selectedCurrency)
if (foundSwap) {
onClickPurchaseSwap(foundSwap)
}
Expand Down
20 changes: 10 additions & 10 deletions packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@0xsequence/api": "^2.0.9",
"@0xsequence/auth": "^2.0.9",
"@0xsequence/core": "^2.0.9",
"@0xsequence/api": "0.0.0-20241007154931",
"@0xsequence/auth": "0.0.0-20241007154931",
"@0xsequence/core": "0.0.0-20241007154931",
"@0xsequence/design-system": "^1.7.8",
"@0xsequence/ethauth": "^1.0.0",
"@0xsequence/indexer": "^2.0.9",
"@0xsequence/metadata": "^2.0.9",
"@0xsequence/network": "^2.0.9",
"@0xsequence/provider": "^2.0.9",
"@0xsequence/utils": "^2.0.9",
"@0xsequence/waas": "^2.0.9",
"@0xsequence/indexer": "0.0.0-20241007154931",
"@0xsequence/metadata": "0.0.0-20241007154931",
"@0xsequence/network": "0.0.0-20241007154931",
"@0xsequence/provider": "0.0.0-20241007154931",
"@0xsequence/utils": "0.0.0-20241007154931",
"@0xsequence/waas": "0.0.0-20241007154931",
"framer-motion": "^8.5.2",
"uuid": "^10.0.0"
},
Expand All @@ -64,7 +64,7 @@
"wagmi": ">= 2.0.0"
},
"devDependencies": {
"0xsequence": "^2.0.9",
"0xsequence": "0.0.0-20241007154931",
"@tanstack/react-query": "^5.37.1",
"@types/uuid": "^9.0.8",
"ethers": "6.13.0",
Expand Down
78 changes: 50 additions & 28 deletions packages/kit/src/hooks/data.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { SequenceAPIClient, Token, SwapQuote } from '@0xsequence/api'
import { SequenceAPIClient, Token, SwapPrice, GetSwapQuoteArgs } from '@0xsequence/api'
import { ContractType, Page, SequenceIndexer, TokenBalance } from '@0xsequence/indexer'
import { ContractInfo, SequenceMetadata } from '@0xsequence/metadata'
import { findSupportedNetwork } from '@0xsequence/network'
import { useInfiniteQuery, useQuery } from '@tanstack/react-query'
import { zeroAddress } from 'viem'
import { blobsToProofsErrorType, zeroAddress } from 'viem'

import { compareAddress } from '../utils/helpers'

Expand Down Expand Up @@ -358,36 +358,37 @@ export const useTransactionHistory = (args: UseTransactionHistoryArgs) => {
})
}

export type SwapQuotesWithCurrencyInfo = {
quote: SwapQuote
export type SwapPricesWithCurrencyInfo = {
price: SwapPrice
info: ContractInfo | undefined
balance: TokenBalance | undefined
}

const getSwapQuotes = async (
const getSwapPrices = async (
apiClient: SequenceAPIClient,
metadataClient: SequenceMetadata,
indexerClient: SequenceIndexer,
args: UseSwapQuotesArgs
): Promise<SwapQuotesWithCurrencyInfo[]> => {
if (!args.chainId || !args.userAddress || !args.currencyAddress || !args.currencyAmount || args.currencyAmount === '0') {
args: UseSwapPricesArgs
): Promise<SwapPricesWithCurrencyInfo[]> => {
if (!args.chainId || !args.userAddress || !args.buyCurrencyAddress || !args.buyAmount || args.buyAmount === '0') {
return []
}

try {
const res = await apiClient.getSwapQuotes({
...args,
includeApprove: true
const { withContractInfo, ...swapPricesArgs } = args

const res = await apiClient.getSwapPrices({
...swapPricesArgs,
})

if (res.swapQuotes === null) {
if (res.swapPrices === null) {
return []
}

const currencyInfoMap = new Map<string, Promise<ContractInfo | undefined>>()
if (args.withContractInfo) {
res?.swapQuotes.forEach(quote => {
const { currencyAddress } = quote
if (withContractInfo) {
res?.swapPrices.forEach(price => {
const { currencyAddress } = price
if (currencyAddress && !currencyInfoMap.has(currencyAddress)) {
currencyInfoMap.set(
currencyAddress,
Expand All @@ -403,8 +404,8 @@ const getSwapQuotes = async (
}

const currencyBalanceInfoMap = new Map<string, Promise<TokenBalance>>()
res?.swapQuotes.forEach(quote => {
const { currencyAddress } = quote
res?.swapPrices.forEach(price => {
const { currencyAddress } = price
if (currencyAddress && !currencyBalanceInfoMap.has(currencyAddress)) {
currencyBalanceInfoMap.set(
currencyAddress,
Expand All @@ -423,10 +424,10 @@ const getSwapQuotes = async (
})

return Promise.all(
res?.swapQuotes.map(async quote => ({
quote,
info: (await currencyInfoMap.get(quote.currencyAddress)) || undefined,
balance: (await currencyBalanceInfoMap.get(quote.currencyAddress)) || undefined
res?.swapPrices.map(async price => ({
price,
info: (await currencyInfoMap.get(price.currencyAddress)) || undefined,
balance: (await currencyBalanceInfoMap.get(price.currencyAddress)) || undefined
})) || []
)
} catch (e) {
Expand All @@ -435,29 +436,50 @@ const getSwapQuotes = async (
}
}

interface UseSwapQuotesArgs {
interface UseSwapPricesArgs {
userAddress: string
currencyAddress: string
currencyAmount: string
buyCurrencyAddress: string
buyAmount: string
chainId: number
withContractInfo?: boolean
}

export const useSwapQuotes = (args: UseSwapQuotesArgs) => {
export const useSwapPrices = (args: UseSwapPricesArgs) => {
const apiClient = useAPIClient()
const metadataClient = useMetadataClient()
const indexerClient = useIndexerClient(args.chainId)

const enabled =
!!args.chainId && !!args.userAddress && !!args.currencyAddress && !!args.currencyAmount && args.currencyAmount !== '0'
!!args.chainId && !!args.userAddress && !!args.buyCurrencyAddress && !!args.buyAmount && args.buyAmount !== '0'

return useQuery({
queryKey: ['swapQuotes', args],
queryFn: () => getSwapQuotes(apiClient, metadataClient, indexerClient, args),
queryKey: ['swapPrices', args],
queryFn: () => getSwapPrices(apiClient, metadataClient, indexerClient, args),
retry: true,
// We must keep a long staletime to avoid the list of quotes being refreshed while the user is doing the transactions
// Instead, we will invalidate the query manually
staleTime: time.oneHour,
enabled
})
}

interface UseSwapQuoteOptions {
disabled?: boolean
}

export const useSwapQuote = (args:GetSwapQuoteArgs, options: UseSwapQuoteOptions) => {
const apiClient = useAPIClient()
const { disabled = false } = options

return useQuery({
queryKey: ['useSwapQuote', args],
queryFn: async () => {
const res = await apiClient.getSwapQuote(args)

return res.swapQuote
},
retry: true,
staleTime: time.oneMinute * 1,
enabled: !disabled || !args.userAddress || !args.chainId || !args.buyCurrencyAddress
})
}
Loading

0 comments on commit 42831d6

Please sign in to comment.