0xtrails 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +42 -0
- package/dist/abi.d.ts +37 -0
- package/dist/abi.d.ts.map +1 -0
- package/dist/abi.js +36 -0
- package/dist/apiClient.d.ts +9 -0
- package/dist/apiClient.d.ts.map +1 -0
- package/dist/apiClient.js +18 -0
- package/dist/buffer.d.ts +3 -0
- package/dist/buffer.d.ts.map +1 -0
- package/dist/buffer.js +8 -0
- package/dist/cctp.d.ts +84 -0
- package/dist/cctp.d.ts.map +1 -0
- package/dist/cctp.js +401 -0
- package/dist/chainSwitch.d.ts +7 -0
- package/dist/chainSwitch.d.ts.map +1 -0
- package/dist/chainSwitch.js +33 -0
- package/dist/chains.d.ts +13 -0
- package/dist/chains.d.ts.map +1 -0
- package/dist/chains.js +95 -0
- package/dist/constants.d.ts +11 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +16 -0
- package/dist/encoders.d.ts +7 -0
- package/dist/encoders.d.ts.map +1 -0
- package/dist/encoders.js +8 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.d.ts.map +1 -0
- package/dist/error.js +12 -0
- package/dist/explorer.d.ts +12 -0
- package/dist/explorer.d.ts.map +1 -0
- package/dist/explorer.js +18 -0
- package/dist/gasless.d.ts +116 -0
- package/dist/gasless.d.ts.map +1 -0
- package/dist/gasless.js +297 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/indexerClient.d.ts +9 -0
- package/dist/indexerClient.d.ts.map +1 -0
- package/dist/indexerClient.js +18 -0
- package/dist/intents.d.ts +83 -0
- package/dist/intents.d.ts.map +1 -0
- package/dist/intents.js +288 -0
- package/dist/metaTxnMonitor.d.ts +14 -0
- package/dist/metaTxnMonitor.d.ts.map +1 -0
- package/dist/metaTxnMonitor.js +121 -0
- package/dist/metaTxns.d.ts +6 -0
- package/dist/metaTxns.d.ts.map +1 -0
- package/dist/metaTxns.js +4 -0
- package/dist/paymasterSend.d.ts +90 -0
- package/dist/paymasterSend.d.ts.map +1 -0
- package/dist/paymasterSend.js +329 -0
- package/dist/preconditions.d.ts +11 -0
- package/dist/preconditions.d.ts.map +1 -0
- package/dist/preconditions.js +29 -0
- package/dist/prepareSend.d.ts +102 -0
- package/dist/prepareSend.d.ts.map +1 -0
- package/dist/prepareSend.js +1080 -0
- package/dist/prices.d.ts +18 -0
- package/dist/prices.d.ts.map +1 -0
- package/dist/prices.js +142 -0
- package/dist/queryParams.d.ts +9 -0
- package/dist/queryParams.d.ts.map +1 -0
- package/dist/queryParams.js +66 -0
- package/dist/relaySdk.d.ts +65 -0
- package/dist/relaySdk.d.ts.map +1 -0
- package/dist/relaySdk.js +314 -0
- package/dist/relayer.d.ts +23 -0
- package/dist/relayer.d.ts.map +1 -0
- package/dist/relayer.js +230 -0
- package/dist/sendUserOp.d.ts +140 -0
- package/dist/sendUserOp.d.ts.map +1 -0
- package/dist/sendUserOp.js +388 -0
- package/dist/sequenceWallet.d.ts +79 -0
- package/dist/sequenceWallet.d.ts.map +1 -0
- package/dist/sequenceWallet.js +374 -0
- package/dist/theme.d.ts +3 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +1 -0
- package/dist/toSimpleSmartAccount.d.ts +95 -0
- package/dist/toSimpleSmartAccount.d.ts.map +1 -0
- package/dist/toSimpleSmartAccount.js +373 -0
- package/dist/tokenBalances.d.ts +118 -0
- package/dist/tokenBalances.d.ts.map +1 -0
- package/dist/tokenBalances.js +492 -0
- package/dist/tokens.d.ts +50 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +356 -0
- package/dist/trails.d.ts +128 -0
- package/dist/trails.d.ts.map +1 -0
- package/dist/trails.js +1031 -0
- package/dist/umd/trails.min.js +12610 -0
- package/dist/umd/trails.min.js.map +1 -0
- package/dist/umd.d.ts +24 -0
- package/dist/umd.d.ts.map +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +9 -0
- package/dist/widget/ConstantsUtil-B-_-u8aQ.js +6 -0
- package/dist/widget/add-hVLs3ldJ.js +20 -0
- package/dist/widget/all-wallets-Cwxnx4BT.js +11 -0
- package/dist/widget/app-store-CAAVQjW0.js +22 -0
- package/dist/widget/apple-C3BSbglw.js +23 -0
- package/dist/widget/arrow-bottom-circle-BGU9MmsZ.js +16 -0
- package/dist/widget/arrow-bottom-hS_SA8Gp.js +13 -0
- package/dist/widget/arrow-left-CJZanWz7.js +13 -0
- package/dist/widget/arrow-right-C1qL8EMd.js +13 -0
- package/dist/widget/arrow-top-CbuCmbQs.js +13 -0
- package/dist/widget/bank-CXBEEGbb.js +19 -0
- package/dist/widget/bin-Dqzv3zCZ.js +9 -0
- package/dist/widget/bitcoin-4y3sovZp.js +18 -0
- package/dist/widget/browser-DyOl4_8m.js +1413 -0
- package/dist/widget/browser-t7Fh0sEU.js +19 -0
- package/dist/widget/card-Bo4CZkTs.js +19 -0
- package/dist/widget/ccip-BynehMIN.js +232 -0
- package/dist/widget/checkmark-DV6OKvnY.js +16 -0
- package/dist/widget/checkmark-bold-CAp1-IQ2.js +13 -0
- package/dist/widget/chevron-bottom-BjzsVzk9.js +13 -0
- package/dist/widget/chevron-left-CQZBDCiR.js +13 -0
- package/dist/widget/chevron-right-Dhg4zeZM.js +13 -0
- package/dist/widget/chevron-top-CDQmfJef.js +13 -0
- package/dist/widget/chrome-store-BNaC_b6w.js +66 -0
- package/dist/widget/circle-BC_GBj91.js +9 -0
- package/dist/widget/clock-BmF8-4a0.js +13 -0
- package/dist/widget/close-Bf61nZ8o.js +13 -0
- package/dist/widget/coinPlaceholder-7cZW2058.js +13 -0
- package/dist/widget/compass-CFC3yhnW.js +13 -0
- package/dist/widget/components/ChainImage.d.ts +8 -0
- package/dist/widget/components/ChainImage.d.ts.map +1 -0
- package/dist/widget/components/ChainImage.js +6 -0
- package/dist/widget/components/ConnectWallet.d.ts +18 -0
- package/dist/widget/components/ConnectWallet.d.ts.map +1 -0
- package/dist/widget/components/ConnectWallet.js +66 -0
- package/dist/widget/components/DebugScreensDropdown.d.ts +9 -0
- package/dist/widget/components/DebugScreensDropdown.d.ts.map +1 -0
- package/dist/widget/components/DebugScreensDropdown.js +40 -0
- package/dist/widget/components/FeeOptions.d.ts +17 -0
- package/dist/widget/components/FeeOptions.d.ts.map +1 -0
- package/dist/widget/components/FeeOptions.js +65 -0
- package/dist/widget/components/Footer.d.ts +9 -0
- package/dist/widget/components/Footer.d.ts.map +1 -0
- package/dist/widget/components/Footer.js +13 -0
- package/dist/widget/components/GreenCheckAnimation.d.ts +2 -0
- package/dist/widget/components/GreenCheckAnimation.d.ts.map +1 -0
- package/dist/widget/components/GreenCheckAnimation.js +74 -0
- package/dist/widget/components/Modal.d.ts +11 -0
- package/dist/widget/components/Modal.d.ts.map +1 -0
- package/dist/widget/components/Modal.js +36 -0
- package/dist/widget/components/Receipt.d.ts +13 -0
- package/dist/widget/components/Receipt.d.ts.map +1 -0
- package/dist/widget/components/Receipt.js +36 -0
- package/dist/widget/components/SendForm.d.ts +44 -0
- package/dist/widget/components/SendForm.d.ts.map +1 -0
- package/dist/widget/components/SendForm.js +177 -0
- package/dist/widget/components/TokenImage.d.ts +10 -0
- package/dist/widget/components/TokenImage.d.ts.map +1 -0
- package/dist/widget/components/TokenImage.js +8 -0
- package/dist/widget/components/TokenList.d.ts +16 -0
- package/dist/widget/components/TokenList.d.ts.map +1 -0
- package/dist/widget/components/TokenList.js +39 -0
- package/dist/widget/components/TransferPending.d.ts +11 -0
- package/dist/widget/components/TransferPending.d.ts.map +1 -0
- package/dist/widget/components/TransferPending.js +77 -0
- package/dist/widget/components/TransferPendingVertical.d.ts +18 -0
- package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -0
- package/dist/widget/components/TransferPendingVertical.js +183 -0
- package/dist/widget/components/WalletConfirmation.d.ts +18 -0
- package/dist/widget/components/WalletConfirmation.d.ts.map +1 -0
- package/dist/widget/components/WalletConfirmation.js +22 -0
- package/dist/widget/config.d.ts +4 -0
- package/dist/widget/config.d.ts.map +1 -0
- package/dist/widget/config.js +3 -0
- package/dist/widget/copy-e0xXvKN0.js +20 -0
- package/dist/widget/cursor-CqM3v0xJ.js +8 -0
- package/dist/widget/cursor-transparent-CUQpdsCG.js +17 -0
- package/dist/widget/desktop-DUDGIRpM.js +14 -0
- package/dist/widget/disconnect-DUFST9QQ.js +13 -0
- package/dist/widget/discord-C1cj365Z.js +22 -0
- package/dist/widget/email-BHhmb_lX.js +703 -0
- package/dist/widget/embedded-wallet-CuuC4eah.js +467 -0
- package/dist/widget/ethereum-CfmBVfeB.js +15 -0
- package/dist/widget/etherscan-BSiynDhW.js +11 -0
- package/dist/widget/exclamation-triangle-DEiFNpHw.js +9 -0
- package/dist/widget/extension-mRmfCDxo.js +13 -0
- package/dist/widget/external-link-B4xMIVnW.js +13 -0
- package/dist/widget/facebook-CBAZStBR.js +31 -0
- package/dist/widget/farcaster-LHDEDf5S.js +17 -0
- package/dist/widget/filters-CBijuvFv.js +13 -0
- package/dist/widget/github-C3ILD420.js +23 -0
- package/dist/widget/google-CSj73POX.js +23 -0
- package/dist/widget/help-circle-2hdG5IdB.js +17 -0
- package/dist/widget/hooks/useAmountUsd.d.ts +13 -0
- package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -0
- package/dist/widget/hooks/useAmountUsd.js +35 -0
- package/dist/widget/hooks/useSendForm.d.ts +125 -0
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.js +450 -0
- package/dist/widget/hooks/useTokenList.d.ts +52 -0
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -0
- package/dist/widget/hooks/useTokenList.js +252 -0
- package/dist/widget/id-ByYSrwsd.js +17 -0
- package/dist/widget/if-defined-DRXJEhv7.js +752 -0
- package/dist/widget/image-C90L4Rf6.js +9 -0
- package/dist/widget/index-B3SlQ9v3.js +46 -0
- package/dist/widget/index-B8LPuLXQ.js +78 -0
- package/dist/widget/index-BDbworWA.js +171 -0
- package/dist/widget/index-BTlDgFSK.js +98 -0
- package/dist/widget/index-BUCcjXbd.js +306 -0
- package/dist/widget/index-BZ34edi2.js +1055 -0
- package/dist/widget/index-BiCU29wK.js +147 -0
- package/dist/widget/index-BlmqIKsY.js +266 -0
- package/dist/widget/index-BlviH5nG.js +55 -0
- package/dist/widget/index-Bwd5X3fS.js +8306 -0
- package/dist/widget/index-C5gmknHK.js +78 -0
- package/dist/widget/index-CD6dBcRj.js +76 -0
- package/dist/widget/index-CDlhy529.js +63 -0
- package/dist/widget/index-CHXa5ke-.js +59 -0
- package/dist/widget/index-CK94R-H7.js +22498 -0
- package/dist/widget/index-CQzo3m3x.js +182 -0
- package/dist/widget/index-CRT8cAwG.js +325 -0
- package/dist/widget/index-CY7Lt2Yu.js +310 -0
- package/dist/widget/index-CiKfAu1E.js +79 -0
- package/dist/widget/index-CkCu6rMi.js +258 -0
- package/dist/widget/index-CngLTu_R.js +517 -0
- package/dist/widget/index-CsMV8-em.js +2577 -0
- package/dist/widget/index-Cu2Wva8v.js +200 -0
- package/dist/widget/index-DKBxLTEF.js +240 -0
- package/dist/widget/index-DQEVT3dx.js +511 -0
- package/dist/widget/index-DU2HcCis.js +200 -0
- package/dist/widget/index-DutZGWNW.js +321 -0
- package/dist/widget/index-O0glArmc.js +182 -0
- package/dist/widget/index-O8FmRjKe.js +63719 -0
- package/dist/widget/index-RtKXrB6I.js +576 -0
- package/dist/widget/index-TIYtS0gE.js +88 -0
- package/dist/widget/index-dQNJvWHs.js +66 -0
- package/dist/widget/index-wmEwdsq7.js +909 -0
- package/dist/widget/index.d.ts +3 -0
- package/dist/widget/index.d.ts.map +1 -0
- package/dist/widget/index.js +2 -0
- package/dist/widget/info-DMPChDjV.js +8 -0
- package/dist/widget/info-circle-DAvS_7nY.js +17 -0
- package/dist/widget/lightbulb-DnZ9mNEs.js +8 -0
- package/dist/widget/lit-html-BRjl1r6K.js +243 -0
- package/dist/widget/mail-DpaVSOP8.js +13 -0
- package/dist/widget/mobile-CRvdyu7I.js +14 -0
- package/dist/widget/more-C5VqW9PR.js +16 -0
- package/dist/widget/network-placeholder-CZ0vApma.js +19 -0
- package/dist/widget/nftPlaceholder-7jjIK2bT.js +13 -0
- package/dist/widget/off-4mHjJLLX.js +9 -0
- package/dist/widget/onramp-Bc0ozVsw.js +929 -0
- package/dist/widget/play-store-Uocul8nC.js +37 -0
- package/dist/widget/plus-DrYF7siO.js +18 -0
- package/dist/widget/prepareSend-BQJmzM5B.js +54987 -0
- package/dist/widget/qr-code-DcnGMUB3.js +11 -0
- package/dist/widget/receive-fvIVd7R_.js +184 -0
- package/dist/widget/recycle-horizontal-DrDwXC4D.js +14 -0
- package/dist/widget/ref-CXNmEjML.js +41 -0
- package/dist/widget/refresh-OK9lIPLS.js +13 -0
- package/dist/widget/reown-logo-C-Qn7mS3.js +17 -0
- package/dist/widget/search-DZqv1oKg.js +13 -0
- package/dist/widget/send-CJlmI-xe.js +1039 -0
- package/dist/widget/send-otoEC8uU.js +20 -0
- package/dist/widget/socials-BJciurWF.js +599 -0
- package/dist/widget/solana-Bv5Hs_0T.js +18 -0
- package/dist/widget/swapHorizontal-BzOPGV37.js +13 -0
- package/dist/widget/swapHorizontalBold-axyHnSmj.js +13 -0
- package/dist/widget/swapHorizontalMedium-C6YOPfPz.js +21 -0
- package/dist/widget/swapHorizontalRoundedBold-yVcLbWNT.js +13 -0
- package/dist/widget/swapVertical-BDjxt9pE.js +13 -0
- package/dist/widget/swaps-DEWNj4kd.js +1637 -0
- package/dist/widget/telegram-BQJD7dlP.js +21 -0
- package/dist/widget/three-dots-DW9jmSMG.js +10 -0
- package/dist/widget/transactions-uCseGQQt.js +38 -0
- package/dist/widget/twitch-XugxDfOE.js +23 -0
- package/dist/widget/twitterIcon-DQVObQUL.js +11 -0
- package/dist/widget/types.d.ts +51 -0
- package/dist/widget/types.d.ts.map +1 -0
- package/dist/widget/types.js +1 -0
- package/dist/widget/verify-DpMYHxLf.js +13 -0
- package/dist/widget/verify-filled-KpEL6ZJ_.js +13 -0
- package/dist/widget/w3m-modal-C8e-6Kba.js +1047 -0
- package/dist/widget/wallet-D8ssEB0o.js +13 -0
- package/dist/widget/wallet-placeholder-HtAy21Wc.js +19 -0
- package/dist/widget/walletconnect-Bp_4XfrY.js +37 -0
- package/dist/widget/warning-circle-FgYS7P7n.js +17 -0
- package/dist/widget/widget/index.js +7 -0
- package/dist/widget/widget.d.ts +47 -0
- package/dist/widget/widget.d.ts.map +1 -0
- package/dist/widget/widget.js +932 -0
- package/dist/widget/x-DlZBoP9k.js +17 -0
- package/dist/widget/x-mark-Ba9pt-_h.js +8 -0
- package/package.json +102 -8
- package/src/abi.ts +38 -0
- package/src/apiClient.ts +32 -0
- package/src/buffer.ts +10 -0
- package/src/cctp.ts +579 -0
- package/src/chainSwitch.ts +55 -0
- package/src/chains.ts +124 -0
- package/src/constants.ts +26 -0
- package/src/encoders.ts +20 -0
- package/src/error.ts +15 -0
- package/src/explorer.ts +37 -0
- package/src/gasless.ts +545 -0
- package/src/index.ts +48 -0
- package/src/indexerClient.ts +36 -0
- package/src/intents.ts +537 -0
- package/src/metaTxnMonitor.ts +163 -0
- package/src/metaTxns.ts +21 -0
- package/src/paymasterSend.ts +503 -0
- package/src/preconditions.ts +52 -0
- package/src/prepareSend.ts +1849 -0
- package/src/prices.ts +186 -0
- package/src/queryParams.ts +80 -0
- package/src/relaySdk.ts +481 -0
- package/src/relayer.ts +255 -0
- package/src/sendUserOp.ts +570 -0
- package/src/sequenceWallet.ts +579 -0
- package/src/theme.ts +2 -0
- package/src/toSimpleSmartAccount.ts +567 -0
- package/src/tokenBalances.ts +760 -0
- package/src/tokens.ts +471 -0
- package/src/trails.ts +1591 -0
- package/src/types.d.ts +11 -0
- package/src/umd.tsx +49 -0
- package/src/utils.ts +16 -0
- package/src/vite-env.d.ts +4 -0
- package/src/widget/assets/MetaMask-icon-fox-with-margins.svg +31 -0
- package/src/widget/assets/MetaMask-icon-fox.svg +26 -0
- package/src/widget/assets/MetaMask-logo-black.svg +3 -0
- package/src/widget/assets/MetaMask-logo-white.svg +16 -0
- package/src/widget/assets/Privy_Brandmark_Black.svg +9 -0
- package/src/widget/assets/Privy_Brandmark_White.svg +9 -0
- package/src/widget/assets/Trails-logo-black.svg +11 -0
- package/src/widget/assets/Trails-logo-white.svg +11 -0
- package/src/widget/components/ChainImage.tsx +28 -0
- package/src/widget/components/ConnectWallet.tsx +206 -0
- package/src/widget/components/DebugScreensDropdown.tsx +88 -0
- package/src/widget/components/FeeOptions.tsx +199 -0
- package/src/widget/components/Footer.tsx +51 -0
- package/src/widget/components/GreenCheckAnimation.tsx +119 -0
- package/src/widget/components/Modal.tsx +97 -0
- package/src/widget/components/Receipt.tsx +237 -0
- package/src/widget/components/SendForm.tsx +695 -0
- package/src/widget/components/TokenImage.tsx +37 -0
- package/src/widget/components/TokenList.tsx +287 -0
- package/src/widget/components/TransferPending.tsx +204 -0
- package/src/widget/components/TransferPendingVertical.tsx +412 -0
- package/src/widget/components/WalletConfirmation.tsx +172 -0
- package/src/widget/config.ts +5 -0
- package/src/widget/hooks/useAmountUsd.ts +59 -0
- package/src/widget/hooks/useSendForm.ts +715 -0
- package/src/widget/hooks/useTokenList.ts +397 -0
- package/src/widget/index.css +2 -0
- package/src/widget/index.tsx +8 -0
- package/src/widget/types/svg.d.ts +8 -0
- package/src/widget/types.ts +59 -0
- package/src/widget/widget.tsx +1438 -0
- package/index.js +0 -1
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
GatewayNativeTokenBalances,
|
|
3
|
+
GatewayTokenBalance,
|
|
4
|
+
GetTokenBalancesSummaryReturn,
|
|
5
|
+
NativeTokenBalance,
|
|
6
|
+
SequenceIndexerGateway,
|
|
7
|
+
TokenBalance,
|
|
8
|
+
} from "@0xsequence/indexer"
|
|
9
|
+
import { ContractVerificationStatus } from "@0xsequence/indexer"
|
|
10
|
+
import type { Page, Price, SequenceAPIClient } from "@0xsequence/trails-api"
|
|
11
|
+
import { QueryClient, useQuery } from "@tanstack/react-query"
|
|
12
|
+
import type { Address } from "ox"
|
|
13
|
+
import { useEffect, useState } from "react"
|
|
14
|
+
import { formatUnits, parseUnits, zeroAddress } from "viem"
|
|
15
|
+
import { useAPIClient } from "./apiClient.js"
|
|
16
|
+
import { useIndexerGatewayClient } from "./indexerClient.js"
|
|
17
|
+
import { getTokenPrices, useTokenPrices } from "./prices.js"
|
|
18
|
+
|
|
19
|
+
export type { NativeTokenBalance, TokenBalance }
|
|
20
|
+
|
|
21
|
+
// Initialize query client for token balances
|
|
22
|
+
const tokenBalancesQueryClient = new QueryClient({
|
|
23
|
+
defaultOptions: {
|
|
24
|
+
queries: {
|
|
25
|
+
staleTime: 60000, // 1 minute
|
|
26
|
+
gcTime: 300000, // 5 minutes
|
|
27
|
+
retry: 2,
|
|
28
|
+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
29
|
+
refetchOnWindowFocus: false,
|
|
30
|
+
refetchOnReconnect: true,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
// Default empty page info for query fallback
|
|
36
|
+
const defaultPage = { page: 1, pageSize: 10, more: false }
|
|
37
|
+
|
|
38
|
+
// Type guard for native token balance
|
|
39
|
+
export function isNativeToken(
|
|
40
|
+
token: TokenBalance | NativeTokenBalance,
|
|
41
|
+
): token is NativeTokenBalance {
|
|
42
|
+
if ("contractAddress" in token) {
|
|
43
|
+
return false
|
|
44
|
+
}
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface TokenBalanceWithPrice extends TokenBalance {
|
|
49
|
+
price?: Price
|
|
50
|
+
balanceUsd?: number
|
|
51
|
+
balanceUsdFormatted?: string
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface NativeTokenBalanceWithPrice extends NativeTokenBalance {
|
|
55
|
+
price?: Price
|
|
56
|
+
balanceUsd?: number
|
|
57
|
+
balanceUsdFormatted?: string
|
|
58
|
+
symbol?: string
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type TokenBalanceExtended =
|
|
62
|
+
| TokenBalanceWithPrice
|
|
63
|
+
| NativeTokenBalanceWithPrice
|
|
64
|
+
|
|
65
|
+
export function sortTokensByPriority(
|
|
66
|
+
a: TokenBalanceExtended,
|
|
67
|
+
b: TokenBalanceExtended,
|
|
68
|
+
): number {
|
|
69
|
+
// First sort by USD balance if available
|
|
70
|
+
const aUsdBalance = a.balanceUsd ?? 0
|
|
71
|
+
const bUsdBalance = b.balanceUsd ?? 0
|
|
72
|
+
if (aUsdBalance !== bUsdBalance) {
|
|
73
|
+
return bUsdBalance - aUsdBalance // Higher USD balance first
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Then sort by native token status
|
|
77
|
+
if (isNativeToken(a) && !isNativeToken(b)) return -1
|
|
78
|
+
if (!isNativeToken(a) && isNativeToken(b)) return 1
|
|
79
|
+
|
|
80
|
+
// Finally sort by token balance
|
|
81
|
+
try {
|
|
82
|
+
const balanceA = BigInt(a.balance)
|
|
83
|
+
const balanceB = BigInt(b.balance)
|
|
84
|
+
if (balanceA > balanceB) return -1
|
|
85
|
+
if (balanceA < balanceB) return 1
|
|
86
|
+
} catch {
|
|
87
|
+
// If balance comparison fails, maintain current order
|
|
88
|
+
return 0
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return 0
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface GetTokenBalancesWithPrice {
|
|
95
|
+
page: Page
|
|
96
|
+
nativeBalances: Array<
|
|
97
|
+
NativeTokenBalance & {
|
|
98
|
+
price?: Price
|
|
99
|
+
symbol?: string
|
|
100
|
+
balanceUsd?: number
|
|
101
|
+
balanceUsdFormatted?: string
|
|
102
|
+
}
|
|
103
|
+
>
|
|
104
|
+
balances: Array<
|
|
105
|
+
TokenBalance & {
|
|
106
|
+
price?: Price
|
|
107
|
+
balanceUsd?: number
|
|
108
|
+
balanceUsdFormatted?: string
|
|
109
|
+
}
|
|
110
|
+
>
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function useTokenBalances(
|
|
114
|
+
address: Address.Address,
|
|
115
|
+
indexerGatewayClient?: SequenceIndexerGateway,
|
|
116
|
+
sequenceApiClient?: SequenceAPIClient,
|
|
117
|
+
): {
|
|
118
|
+
tokenBalancesData: GetTokenBalancesSummaryReturn | undefined
|
|
119
|
+
isLoadingBalances: boolean
|
|
120
|
+
isLoadingPrices: boolean
|
|
121
|
+
isLoadingSortedTokens: boolean
|
|
122
|
+
balanceError: Error | null
|
|
123
|
+
sortedTokens: TokenBalanceExtended[]
|
|
124
|
+
} {
|
|
125
|
+
// Always call hooks unconditionally to fix React rules violation
|
|
126
|
+
const hookIndexerClient = useIndexerGatewayClient()
|
|
127
|
+
const hookApiClient = useAPIClient()
|
|
128
|
+
|
|
129
|
+
// Use passed parameters if available, otherwise use hook results
|
|
130
|
+
const indexerClient = indexerGatewayClient ?? hookIndexerClient
|
|
131
|
+
const apiClient = sequenceApiClient ?? hookApiClient
|
|
132
|
+
|
|
133
|
+
// Fetch token balances with improved query key structure
|
|
134
|
+
const {
|
|
135
|
+
data: tokenBalancesData,
|
|
136
|
+
isLoading: isLoadingBalances,
|
|
137
|
+
error: balanceError,
|
|
138
|
+
} = useQuery<GetTokenBalancesWithPrice>({
|
|
139
|
+
queryKey: ["tokenBalances", "summary", address],
|
|
140
|
+
queryFn: async (): Promise<GetTokenBalancesWithPrice> => {
|
|
141
|
+
if (!address) {
|
|
142
|
+
console.warn("[trails-sdk] No account address or indexer client")
|
|
143
|
+
return {
|
|
144
|
+
balances: [],
|
|
145
|
+
nativeBalances: [],
|
|
146
|
+
page: defaultPage,
|
|
147
|
+
} as GetTokenBalancesWithPrice
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
const summaryFromGateway = await getTokenBalances({
|
|
151
|
+
account: address,
|
|
152
|
+
indexerGatewayClient: indexerClient,
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
page: summaryFromGateway.page,
|
|
157
|
+
balances: (
|
|
158
|
+
summaryFromGateway.balances as unknown as GatewayTokenBalance[]
|
|
159
|
+
).flatMap((b) => b.results),
|
|
160
|
+
nativeBalances: (
|
|
161
|
+
summaryFromGateway.nativeBalances as unknown as GatewayNativeTokenBalances[]
|
|
162
|
+
).flatMap((b) => b.results),
|
|
163
|
+
}
|
|
164
|
+
} catch (error) {
|
|
165
|
+
console.error("[trails-sdk] Failed to fetch token balances:", error)
|
|
166
|
+
throw error
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
enabled: !!address && !!indexerClient,
|
|
170
|
+
staleTime: 60000, // 1 minute
|
|
171
|
+
gcTime: 300000, // 5 minutes cache time
|
|
172
|
+
retry: (failureCount, error) => {
|
|
173
|
+
// Don't retry 404s or network errors after 3 attempts
|
|
174
|
+
if (error && "status" in error && error.status === 404) return false
|
|
175
|
+
if (failureCount < 3) return true
|
|
176
|
+
return false
|
|
177
|
+
},
|
|
178
|
+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), // Exponential backoff
|
|
179
|
+
refetchOnWindowFocus: false, // Prevent refetch on window focus
|
|
180
|
+
refetchOnReconnect: true, // Refetch on reconnect
|
|
181
|
+
refetchInterval: 300000, // Background refetch every 5 minutes
|
|
182
|
+
refetchIntervalInBackground: true,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
const { tokenPrices, isLoadingTokenPrices } = useTokenPrices(
|
|
186
|
+
(tokenBalancesData?.balances ?? [])
|
|
187
|
+
.map((b: any) => {
|
|
188
|
+
return {
|
|
189
|
+
tokenId: b.contractInfo?.symbol,
|
|
190
|
+
contractAddress: b.contractAddress,
|
|
191
|
+
chainId: b.contractInfo?.chainId!,
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
.concat(
|
|
195
|
+
(tokenBalancesData?.nativeBalances ?? []).map((b) => {
|
|
196
|
+
return {
|
|
197
|
+
tokenId: b.symbol,
|
|
198
|
+
contractAddress: zeroAddress,
|
|
199
|
+
chainId: b.chainId,
|
|
200
|
+
}
|
|
201
|
+
}),
|
|
202
|
+
) ?? [],
|
|
203
|
+
apiClient,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
const { data: sortedTokens = [], isLoading: isLoadingSortedTokens } =
|
|
207
|
+
useQuery<TokenBalanceExtended[]>({
|
|
208
|
+
queryKey: [
|
|
209
|
+
"tokenBalances",
|
|
210
|
+
"sorted",
|
|
211
|
+
address,
|
|
212
|
+
tokenBalancesData?.page?.page,
|
|
213
|
+
tokenPrices?.length,
|
|
214
|
+
],
|
|
215
|
+
queryFn: () => {
|
|
216
|
+
if (!tokenBalancesData || !tokenPrices) {
|
|
217
|
+
return []
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const balances = [
|
|
221
|
+
...tokenBalancesData.nativeBalances,
|
|
222
|
+
...tokenBalancesData.balances,
|
|
223
|
+
].filter((token) => {
|
|
224
|
+
try {
|
|
225
|
+
return BigInt(token.balance) > 0n
|
|
226
|
+
} catch {
|
|
227
|
+
return false
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
// First pass: add prices to all tokens
|
|
232
|
+
const tokensWithPrices = balances.map((token) => {
|
|
233
|
+
const isNative = isNativeToken(token)
|
|
234
|
+
const priceData = tokenPrices.find(
|
|
235
|
+
(p: { token: { contractAddress: string; chainId: number } }) =>
|
|
236
|
+
p.token.contractAddress ===
|
|
237
|
+
(isNative ? zeroAddress : token.contractAddress) &&
|
|
238
|
+
p.token.chainId ===
|
|
239
|
+
(isNative ? token.chainId : token.contractInfo?.chainId),
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if (priceData?.price) {
|
|
243
|
+
const tokenWithPrice = { ...token, price: priceData.price }
|
|
244
|
+
tokenWithPrice.balanceUsd = getTokenBalanceUsd(
|
|
245
|
+
token,
|
|
246
|
+
priceData.price,
|
|
247
|
+
)
|
|
248
|
+
tokenWithPrice.balanceUsdFormatted = getTokenBalanceUsdFormatted(
|
|
249
|
+
token,
|
|
250
|
+
priceData.price,
|
|
251
|
+
)
|
|
252
|
+
return tokenWithPrice
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return token
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
return tokensWithPrices.sort(sortTokensByPriority)
|
|
259
|
+
},
|
|
260
|
+
enabled:
|
|
261
|
+
!isLoadingBalances &&
|
|
262
|
+
!isLoadingTokenPrices &&
|
|
263
|
+
!!tokenBalancesData &&
|
|
264
|
+
!!tokenPrices,
|
|
265
|
+
staleTime: 30000, // 30 seconds for sorted tokens
|
|
266
|
+
gcTime: 120000, // 2 minutes cache time
|
|
267
|
+
refetchOnWindowFocus: false,
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
tokenBalancesData,
|
|
272
|
+
isLoadingBalances,
|
|
273
|
+
isLoadingPrices: isLoadingTokenPrices,
|
|
274
|
+
isLoadingSortedTokens:
|
|
275
|
+
isLoadingSortedTokens || isLoadingBalances || isLoadingTokenPrices,
|
|
276
|
+
balanceError,
|
|
277
|
+
sortedTokens,
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Helper to format balance
|
|
282
|
+
export function formatBalance(balance: string, decimals: number = 18) {
|
|
283
|
+
try {
|
|
284
|
+
const formatted = formatUnits(BigInt(balance), decimals)
|
|
285
|
+
return formatValue(formatted)
|
|
286
|
+
} catch (e) {
|
|
287
|
+
console.error("[trails-sdk] Error formatting balance:", e)
|
|
288
|
+
return balance
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function getTokenBalanceUsd(
|
|
293
|
+
token: TokenBalance | NativeTokenBalance,
|
|
294
|
+
tokenPrice: Price,
|
|
295
|
+
): number {
|
|
296
|
+
const isNative = isNativeToken(token)
|
|
297
|
+
const formattedBalance = formatBalance(
|
|
298
|
+
token.balance,
|
|
299
|
+
isNative ? 18 : token.contractInfo?.decimals,
|
|
300
|
+
)
|
|
301
|
+
const priceUsd = Number(tokenPrice.value) ?? 0
|
|
302
|
+
return Number(formattedBalance) * priceUsd
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function formatValue(value: string | number): string {
|
|
306
|
+
try {
|
|
307
|
+
return Number(value).toLocaleString(undefined, {
|
|
308
|
+
maximumFractionDigits: 5,
|
|
309
|
+
minimumFractionDigits: 2,
|
|
310
|
+
})
|
|
311
|
+
} catch (err) {
|
|
312
|
+
console.error("[trails-sdk] Error formatting value:", err)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return value.toString()
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function formatUsdValue(value: number | string = 0): string {
|
|
319
|
+
return Intl.NumberFormat("en-US", {
|
|
320
|
+
style: "currency",
|
|
321
|
+
currency: "USD",
|
|
322
|
+
maximumFractionDigits: 2,
|
|
323
|
+
minimumFractionDigits: 2,
|
|
324
|
+
}).format(Number(value))
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export function getTokenBalanceUsdFormatted(
|
|
328
|
+
token: TokenBalance | NativeTokenBalance,
|
|
329
|
+
tokenPrice: Price,
|
|
330
|
+
): string {
|
|
331
|
+
const balanceUsd = getTokenBalanceUsd(token, tokenPrice)
|
|
332
|
+
return formatUsdValue(balanceUsd)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export function useTokenBalanceUsdFormat(
|
|
336
|
+
token: TokenBalance | NativeTokenBalance,
|
|
337
|
+
tokenPrice: Price,
|
|
338
|
+
): string {
|
|
339
|
+
const [format, setFormat] = useState<string>("")
|
|
340
|
+
useEffect(() => {
|
|
341
|
+
const formattedBalance = getTokenBalanceUsdFormatted(token, tokenPrice)
|
|
342
|
+
setFormat(formattedBalance)
|
|
343
|
+
}, [token, tokenPrice])
|
|
344
|
+
return format
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export type GetTokenBalancesParams = {
|
|
348
|
+
account: string
|
|
349
|
+
indexerGatewayClient: SequenceIndexerGateway
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Separate fetch function for token balances summary
|
|
353
|
+
export async function fetchGetTokenBalancesSummary({
|
|
354
|
+
account,
|
|
355
|
+
indexerGatewayClient,
|
|
356
|
+
}: GetTokenBalancesParams): Promise<GetTokenBalancesSummaryReturn> {
|
|
357
|
+
if (!account || !indexerGatewayClient) {
|
|
358
|
+
throw new Error("Account address and indexer client are required")
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const summaryFromGateway =
|
|
363
|
+
await indexerGatewayClient.getTokenBalancesSummary({
|
|
364
|
+
filter: {
|
|
365
|
+
accountAddresses: [account],
|
|
366
|
+
contractStatus: ContractVerificationStatus.VERIFIED,
|
|
367
|
+
contractTypes: ["ERC20"],
|
|
368
|
+
omitNativeBalances: false,
|
|
369
|
+
},
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
return summaryFromGateway as unknown as GetTokenBalancesSummaryReturn
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error("[trails-sdk] Failed to fetch token balances summary:", error)
|
|
375
|
+
throw error
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export async function getTokenBalances({
|
|
380
|
+
account,
|
|
381
|
+
indexerGatewayClient,
|
|
382
|
+
}: GetTokenBalancesParams): Promise<GetTokenBalancesSummaryReturn> {
|
|
383
|
+
return tokenBalancesQueryClient.fetchQuery({
|
|
384
|
+
queryKey: ["tokenBalances", "summary", account],
|
|
385
|
+
queryFn: () =>
|
|
386
|
+
fetchGetTokenBalancesSummary({ account, indexerGatewayClient }),
|
|
387
|
+
staleTime: 60000, // 1 minute
|
|
388
|
+
gcTime: 300000, // 5 minutes
|
|
389
|
+
})
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Cache invalidation utility function
|
|
393
|
+
export function invalidateTokenBalancesCache(account?: string) {
|
|
394
|
+
if (account) {
|
|
395
|
+
// Invalidate specific account's token balances
|
|
396
|
+
tokenBalancesQueryClient.invalidateQueries({
|
|
397
|
+
queryKey: ["tokenBalances", account],
|
|
398
|
+
})
|
|
399
|
+
} else {
|
|
400
|
+
// Invalidate all token balance queries
|
|
401
|
+
tokenBalancesQueryClient.invalidateQueries({
|
|
402
|
+
queryKey: ["tokenBalances"],
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export type GetTokenBalancesFlatArrayParams = {
|
|
408
|
+
account: string
|
|
409
|
+
indexerGatewayClient: SequenceIndexerGateway
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export type GetTokenBalancesFlatArrayReturn = {
|
|
413
|
+
balances: TokenBalance[]
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export async function getTokenBalancesFlatArray({
|
|
417
|
+
account,
|
|
418
|
+
indexerGatewayClient,
|
|
419
|
+
}: GetTokenBalancesFlatArrayParams): Promise<TokenBalance[]> {
|
|
420
|
+
const summaryFromGateway = await getTokenBalances({
|
|
421
|
+
account,
|
|
422
|
+
indexerGatewayClient,
|
|
423
|
+
})
|
|
424
|
+
const tokenMap = new Map<string, TokenBalance>()
|
|
425
|
+
|
|
426
|
+
for (const balance of summaryFromGateway.balances) {
|
|
427
|
+
;(balance as any).results.forEach((b: any) => {
|
|
428
|
+
tokenMap.set(
|
|
429
|
+
`${b.contractAddress}-${b.contractInfo?.chainId}-${b.contractInfo?.symbol}`,
|
|
430
|
+
{
|
|
431
|
+
...b,
|
|
432
|
+
contractAddress: b.contractAddress ?? zeroAddress,
|
|
433
|
+
tokenId: b.contractInfo?.symbol,
|
|
434
|
+
},
|
|
435
|
+
)
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
for (const balance of summaryFromGateway.nativeBalances) {
|
|
440
|
+
;(balance as any).results.forEach((b: any) => {
|
|
441
|
+
tokenMap.set(`${b.contractAddress}-${b.chainId}-${b.symbol}`, {
|
|
442
|
+
...b,
|
|
443
|
+
contractAddress: b.contractAddress ?? zeroAddress,
|
|
444
|
+
tokenId: b.symbol,
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const tokens = Array.from(tokenMap.values())
|
|
450
|
+
|
|
451
|
+
return tokens
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
export type GetTokenBalancesWithPricesParams = {
|
|
455
|
+
account: string
|
|
456
|
+
indexerGatewayClient: SequenceIndexerGateway
|
|
457
|
+
apiClient: SequenceAPIClient
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
export type GetTokenBalancesWithPriceReturn = {
|
|
461
|
+
balances: TokenBalanceWithPrice[]
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export async function getTokenBalancesWithPrices({
|
|
465
|
+
account,
|
|
466
|
+
indexerGatewayClient,
|
|
467
|
+
apiClient,
|
|
468
|
+
}: GetTokenBalancesWithPricesParams): Promise<GetTokenBalancesWithPriceReturn> {
|
|
469
|
+
const tokens = await getTokenBalancesFlatArray({
|
|
470
|
+
account,
|
|
471
|
+
indexerGatewayClient,
|
|
472
|
+
})
|
|
473
|
+
const tokenPrices = await getTokenPrices(apiClient, tokens)
|
|
474
|
+
const balancesWithPrices = tokens.map((b) => {
|
|
475
|
+
const price = tokenPrices.find((p) => {
|
|
476
|
+
const isSameChain = p.token.chainId === b.chainId
|
|
477
|
+
let isSameToken = p.token.contractAddress === b.contractAddress
|
|
478
|
+
if (!b.contractAddress) {
|
|
479
|
+
isSameToken =
|
|
480
|
+
p.token.contractAddress === zeroAddress || !p.token.contractAddress
|
|
481
|
+
}
|
|
482
|
+
return isSameChain && isSameToken
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
...b,
|
|
487
|
+
price: price?.price,
|
|
488
|
+
balanceUsd: price?.price
|
|
489
|
+
? getTokenBalanceUsd(b, price?.price)
|
|
490
|
+
: undefined,
|
|
491
|
+
balanceUsdFormatted: price?.price
|
|
492
|
+
? getTokenBalanceUsdFormatted(b, price?.price)
|
|
493
|
+
: undefined,
|
|
494
|
+
}
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
return {
|
|
498
|
+
balances: balancesWithPrices,
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
export type UseAccountTokenBalanceParams = {
|
|
503
|
+
account?: string
|
|
504
|
+
token?: string
|
|
505
|
+
chainId?: number
|
|
506
|
+
indexerGatewayClient?: SequenceIndexerGateway
|
|
507
|
+
apiClient?: SequenceAPIClient
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export function useAccountTokenBalance({
|
|
511
|
+
account,
|
|
512
|
+
token,
|
|
513
|
+
chainId,
|
|
514
|
+
indexerGatewayClient,
|
|
515
|
+
apiClient,
|
|
516
|
+
}: UseAccountTokenBalanceParams) {
|
|
517
|
+
const { data: tokenBalance, isLoading: isLoadingTokenBalance } = useQuery({
|
|
518
|
+
queryKey: ["tokenBalances", "balances", account],
|
|
519
|
+
queryFn: async () => {
|
|
520
|
+
if (
|
|
521
|
+
!account ||
|
|
522
|
+
!indexerGatewayClient ||
|
|
523
|
+
!apiClient ||
|
|
524
|
+
!token ||
|
|
525
|
+
!chainId
|
|
526
|
+
) {
|
|
527
|
+
return null
|
|
528
|
+
}
|
|
529
|
+
const { balances } = await getTokenBalancesWithPrices({
|
|
530
|
+
account,
|
|
531
|
+
indexerGatewayClient,
|
|
532
|
+
apiClient,
|
|
533
|
+
})
|
|
534
|
+
const tokenBalance = balances.find(
|
|
535
|
+
(b) =>
|
|
536
|
+
b.chainId === chainId &&
|
|
537
|
+
(b.contractAddress?.toLowerCase() === token.toLowerCase() ||
|
|
538
|
+
(!b.contractAddress && token === zeroAddress)),
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
return tokenBalance
|
|
542
|
+
},
|
|
543
|
+
})
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
tokenBalance,
|
|
547
|
+
isLoadingTokenBalance,
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export type HasSufficientBalanceParams = {
|
|
552
|
+
account: string
|
|
553
|
+
token: string
|
|
554
|
+
amount: string
|
|
555
|
+
chainId: number
|
|
556
|
+
indexerGatewayClient: SequenceIndexerGateway
|
|
557
|
+
apiClient: SequenceAPIClient
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
export async function getHasSufficientBalanceToken({
|
|
561
|
+
account,
|
|
562
|
+
token,
|
|
563
|
+
amount,
|
|
564
|
+
chainId,
|
|
565
|
+
indexerGatewayClient,
|
|
566
|
+
apiClient,
|
|
567
|
+
}: HasSufficientBalanceParams): Promise<boolean> {
|
|
568
|
+
const { balances } = await getTokenBalancesWithPrices({
|
|
569
|
+
account,
|
|
570
|
+
indexerGatewayClient,
|
|
571
|
+
apiClient,
|
|
572
|
+
})
|
|
573
|
+
const tokenBalance = balances.find(
|
|
574
|
+
(b) =>
|
|
575
|
+
b.chainId === chainId &&
|
|
576
|
+
(b.contractAddress?.toLowerCase() === token.toLowerCase() ||
|
|
577
|
+
(!b.contractAddress && token === zeroAddress)),
|
|
578
|
+
)
|
|
579
|
+
if (!tokenBalance) {
|
|
580
|
+
return false
|
|
581
|
+
}
|
|
582
|
+
const decimals = tokenBalance?.contractInfo?.decimals ?? 18
|
|
583
|
+
return tokenBalance?.balance
|
|
584
|
+
? BigInt(tokenBalance.balance) >= parseUnits(amount, decimals)
|
|
585
|
+
: false
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export function useHasSufficientBalanceToken(
|
|
589
|
+
account: string,
|
|
590
|
+
token: string,
|
|
591
|
+
amount: string,
|
|
592
|
+
chainId: number,
|
|
593
|
+
): {
|
|
594
|
+
hasSufficientBalanceToken: boolean
|
|
595
|
+
isLoadingHasSufficientBalanceToken: boolean
|
|
596
|
+
} {
|
|
597
|
+
const indexerGatewayClient = useIndexerGatewayClient()
|
|
598
|
+
const apiClient = useAPIClient()
|
|
599
|
+
|
|
600
|
+
const {
|
|
601
|
+
data: hasSufficientBalanceToken,
|
|
602
|
+
isLoading: isLoadingHasSufficientBalanceToken,
|
|
603
|
+
} = useQuery({
|
|
604
|
+
queryKey: ["tokenBalances", "sufficient", account, token, amount, chainId],
|
|
605
|
+
queryFn: () =>
|
|
606
|
+
account
|
|
607
|
+
? getHasSufficientBalanceToken({
|
|
608
|
+
account: account,
|
|
609
|
+
token: token,
|
|
610
|
+
amount: amount,
|
|
611
|
+
chainId: chainId,
|
|
612
|
+
indexerGatewayClient: indexerGatewayClient,
|
|
613
|
+
apiClient: apiClient,
|
|
614
|
+
})
|
|
615
|
+
: null,
|
|
616
|
+
enabled: !!account && !!token && !!amount && !!chainId,
|
|
617
|
+
staleTime: 45000, // 45 seconds
|
|
618
|
+
gcTime: 180000, // 3 minutes cache time
|
|
619
|
+
retry: (failureCount, error) => {
|
|
620
|
+
if (error && "status" in error && error.status === 404) return false
|
|
621
|
+
if (failureCount < 2) return true
|
|
622
|
+
return false
|
|
623
|
+
},
|
|
624
|
+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
625
|
+
refetchOnWindowFocus: false,
|
|
626
|
+
})
|
|
627
|
+
|
|
628
|
+
return {
|
|
629
|
+
hasSufficientBalanceToken: hasSufficientBalanceToken || false,
|
|
630
|
+
isLoadingHasSufficientBalanceToken,
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export type GetHasSufficientBalanceUsdParams = {
|
|
635
|
+
account: string
|
|
636
|
+
targetAmountUsd: number | string
|
|
637
|
+
indexerGatewayClient: SequenceIndexerGateway
|
|
638
|
+
apiClient: SequenceAPIClient
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
export async function getHasSufficientBalanceUsd({
|
|
642
|
+
account,
|
|
643
|
+
targetAmountUsd,
|
|
644
|
+
indexerGatewayClient,
|
|
645
|
+
apiClient,
|
|
646
|
+
}: GetHasSufficientBalanceUsdParams): Promise<boolean> {
|
|
647
|
+
const totalBalanceUsd = await getAccountTotalBalanceUsd({
|
|
648
|
+
account,
|
|
649
|
+
indexerGatewayClient,
|
|
650
|
+
apiClient,
|
|
651
|
+
})
|
|
652
|
+
return totalBalanceUsd >= Number(targetAmountUsd)
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export function useHasSufficientBalanceUsd(
|
|
656
|
+
account: string,
|
|
657
|
+
targetAmountUsd?: number | string | null,
|
|
658
|
+
): {
|
|
659
|
+
hasSufficientBalanceUsd: boolean
|
|
660
|
+
isLoadingHasSufficientBalanceUsd: boolean
|
|
661
|
+
hasSufficientBalanceUsdError: Error | null
|
|
662
|
+
} {
|
|
663
|
+
const indexerGatewayClient = useIndexerGatewayClient()
|
|
664
|
+
const apiClient = useAPIClient()
|
|
665
|
+
|
|
666
|
+
const {
|
|
667
|
+
data: hasSufficientBalanceUsd,
|
|
668
|
+
isLoading: isLoadingHasSufficientBalanceUsd,
|
|
669
|
+
error: hasSufficientBalanceUsdError,
|
|
670
|
+
} = useQuery({
|
|
671
|
+
queryKey: ["tokenBalances", "sufficientUsd", account, targetAmountUsd],
|
|
672
|
+
queryFn: () =>
|
|
673
|
+
account && targetAmountUsd
|
|
674
|
+
? getHasSufficientBalanceUsd({
|
|
675
|
+
account: account,
|
|
676
|
+
targetAmountUsd: targetAmountUsd,
|
|
677
|
+
indexerGatewayClient: indexerGatewayClient,
|
|
678
|
+
apiClient: apiClient,
|
|
679
|
+
})
|
|
680
|
+
: false,
|
|
681
|
+
enabled: !!account && !!targetAmountUsd,
|
|
682
|
+
staleTime: 45000, // 45 seconds
|
|
683
|
+
gcTime: 180000, // 3 minutes cache time
|
|
684
|
+
retry: (failureCount, error) => {
|
|
685
|
+
if (error && "status" in error && error.status === 404) return false
|
|
686
|
+
if (failureCount < 2) return true
|
|
687
|
+
return false
|
|
688
|
+
},
|
|
689
|
+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
690
|
+
refetchOnWindowFocus: false,
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
return {
|
|
694
|
+
hasSufficientBalanceUsd: hasSufficientBalanceUsd || false,
|
|
695
|
+
isLoadingHasSufficientBalanceUsd:
|
|
696
|
+
isLoadingHasSufficientBalanceUsd || !targetAmountUsd || !account,
|
|
697
|
+
hasSufficientBalanceUsdError,
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
export type GetAccountTotalBalanceUsdParams = {
|
|
702
|
+
account: string
|
|
703
|
+
indexerGatewayClient: SequenceIndexerGateway
|
|
704
|
+
apiClient: SequenceAPIClient
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
export async function getAccountTotalBalanceUsd({
|
|
708
|
+
account,
|
|
709
|
+
indexerGatewayClient,
|
|
710
|
+
apiClient,
|
|
711
|
+
}: GetAccountTotalBalanceUsdParams): Promise<number> {
|
|
712
|
+
const { balances } = await getTokenBalancesWithPrices({
|
|
713
|
+
account,
|
|
714
|
+
indexerGatewayClient,
|
|
715
|
+
apiClient,
|
|
716
|
+
})
|
|
717
|
+
|
|
718
|
+
return balances.reduce((acc, b) => acc + (b.balanceUsd ?? 0), 0)
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
export function useAccountTotalBalanceUsd(account: string): {
|
|
722
|
+
totalBalanceUsd: number
|
|
723
|
+
isLoadingTotalBalanceUsd: boolean
|
|
724
|
+
totalBalanceUsdFormatted: string
|
|
725
|
+
} {
|
|
726
|
+
const indexerGatewayClient = useIndexerGatewayClient()
|
|
727
|
+
const apiClient = useAPIClient()
|
|
728
|
+
|
|
729
|
+
const { data: totalBalanceUsd, isLoading: isLoadingTotalBalanceUsd } =
|
|
730
|
+
useQuery({
|
|
731
|
+
queryKey: ["tokenBalances", "totalUsd", account],
|
|
732
|
+
queryFn: () =>
|
|
733
|
+
account
|
|
734
|
+
? getAccountTotalBalanceUsd({
|
|
735
|
+
account: account,
|
|
736
|
+
indexerGatewayClient: indexerGatewayClient,
|
|
737
|
+
apiClient: apiClient,
|
|
738
|
+
})
|
|
739
|
+
: null,
|
|
740
|
+
enabled: !!account,
|
|
741
|
+
staleTime: 60000, // 1 minute
|
|
742
|
+
gcTime: 300000, // 5 minutes cache time
|
|
743
|
+
retry: (failureCount, error) => {
|
|
744
|
+
if (error && "status" in error && error.status === 404) return false
|
|
745
|
+
if (failureCount < 2) return true
|
|
746
|
+
return false
|
|
747
|
+
},
|
|
748
|
+
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
749
|
+
refetchOnWindowFocus: false,
|
|
750
|
+
refetchOnReconnect: true,
|
|
751
|
+
refetchInterval: 300000, // Background refetch every 5 minutes
|
|
752
|
+
refetchIntervalInBackground: true,
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
return {
|
|
756
|
+
totalBalanceUsd: totalBalanceUsd || 0,
|
|
757
|
+
isLoadingTotalBalanceUsd,
|
|
758
|
+
totalBalanceUsdFormatted: formatUsdValue(totalBalanceUsd || 0),
|
|
759
|
+
}
|
|
760
|
+
}
|