0xtrails 0.1.13 → 0.2.0
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/dist/aave.d.ts.map +1 -1
- package/dist/analytics.d.ts +11 -2
- package/dist/analytics.d.ts.map +1 -1
- package/dist/apiClient.d.ts +1 -1
- package/dist/apiClient.d.ts.map +1 -1
- package/dist/{proxyCaller.d.ts → balanceInjector.d.ts} +5 -4
- package/dist/balanceInjector.d.ts.map +1 -0
- package/dist/{ccip-D3gTQONK.js → ccip-D6ToCrWc.js} +12 -12
- package/dist/cctp.d.ts.map +1 -1
- package/dist/cctpqueue.d.ts +3 -3
- package/dist/cctpqueue.d.ts.map +1 -1
- package/dist/chains.d.ts.map +1 -1
- package/dist/config.d.ts +17 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/constants.d.ts +5 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/contractUtils.d.ts +2 -0
- package/dist/contractUtils.d.ts.map +1 -1
- package/dist/customChains.d.ts +24 -0
- package/dist/customChains.d.ts.map +1 -0
- package/dist/{index-CnUM7lKf.js → index-BqgeTLL8.js} +34072 -30146
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +411 -400
- package/dist/intentEntrypoint.d.ts +96 -0
- package/dist/intentEntrypoint.d.ts.map +1 -0
- package/dist/intents.d.ts +5 -3
- package/dist/intents.d.ts.map +1 -1
- package/dist/metaTxnMonitor.d.ts.map +1 -1
- package/dist/morpho.d.ts.map +1 -1
- package/dist/pools.d.ts +3 -1
- package/dist/pools.d.ts.map +1 -1
- package/dist/prepareSend.d.ts +8 -2
- package/dist/prepareSend.d.ts.map +1 -1
- package/dist/prices.d.ts +1 -1
- package/dist/prices.d.ts.map +1 -1
- package/dist/relaySdk.d.ts.map +1 -1
- package/dist/relayer.d.ts.map +1 -1
- package/dist/toast.d.ts +9 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/tokenBalances.d.ts +6 -2
- package/dist/tokenBalances.d.ts.map +1 -1
- package/dist/tokens.d.ts.map +1 -1
- package/dist/trails.d.ts +6 -5
- package/dist/trails.d.ts.map +1 -1
- package/dist/trailsClient.d.ts +12 -0
- package/dist/trailsClient.d.ts.map +1 -0
- package/dist/transactions.d.ts +8 -0
- package/dist/transactions.d.ts.map +1 -1
- package/dist/wallets.d.ts.map +1 -1
- package/dist/widget/components/AccountActionsDropdown.d.ts.map +1 -1
- package/dist/widget/components/AccountIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/components/AccountSettings.d.ts +7 -0
- package/dist/widget/components/AccountSettings.d.ts.map +1 -0
- package/dist/widget/components/ChainList.d.ts +0 -1
- package/dist/widget/components/ChainList.d.ts.map +1 -1
- package/dist/widget/components/ClassicSwap.d.ts +46 -0
- package/dist/widget/components/ClassicSwap.d.ts.map +1 -0
- package/dist/widget/components/ConfigDisplay.d.ts.map +1 -1
- package/dist/widget/components/ConnectedWallets.d.ts +9 -0
- package/dist/widget/components/ConnectedWallets.d.ts.map +1 -0
- package/dist/widget/components/DebugMenu.d.ts.map +1 -1
- package/dist/widget/components/DebugScreensList.d.ts.map +1 -1
- package/dist/widget/components/DebugToast.d.ts +3 -0
- package/dist/widget/components/DebugToast.d.ts.map +1 -0
- package/dist/widget/components/Earn.d.ts.map +1 -1
- package/dist/widget/components/EarnPools.d.ts.map +1 -1
- package/dist/widget/components/Fund.d.ts +44 -0
- package/dist/widget/components/Fund.d.ts.map +1 -0
- package/dist/widget/components/Identicon.d.ts +9 -0
- package/dist/widget/components/Identicon.d.ts.map +1 -0
- package/dist/widget/components/Pay.d.ts +46 -0
- package/dist/widget/components/Pay.d.ts.map +1 -0
- package/dist/widget/components/Receive.d.ts.map +1 -1
- package/dist/widget/components/RecentTokens.d.ts.map +1 -1
- package/dist/widget/components/Recipients.d.ts +9 -0
- package/dist/widget/components/Recipients.d.ts.map +1 -0
- package/dist/widget/components/RefundWarning.d.ts +9 -0
- package/dist/widget/components/RefundWarning.d.ts.map +1 -0
- package/dist/widget/components/SimpleSwap.d.ts.map +1 -1
- package/dist/widget/components/Swap.d.ts.map +1 -1
- package/dist/widget/components/SwapSettings.d.ts +1 -5
- package/dist/widget/components/SwapSettings.d.ts.map +1 -1
- package/dist/widget/components/ThemeProvider.d.ts.map +1 -1
- package/dist/widget/components/ThemeSyncer.d.ts +6 -0
- package/dist/widget/components/ThemeSyncer.d.ts.map +1 -0
- package/dist/widget/components/Toast.d.ts +24 -0
- package/dist/widget/components/Toast.d.ts.map +1 -0
- package/dist/widget/components/TokenList.d.ts.map +1 -1
- package/dist/widget/components/TransactionDetails.d.ts.map +1 -1
- package/dist/widget/components/TruncatedAddress.d.ts +2 -0
- package/dist/widget/components/TruncatedAddress.d.ts.map +1 -1
- package/dist/widget/components/UserPreferences.d.ts +7 -0
- package/dist/widget/components/UserPreferences.d.ts.map +1 -0
- package/dist/widget/hooks/useBalanceVisible.d.ts +1 -0
- package/dist/widget/hooks/useBalanceVisible.d.ts.map +1 -1
- package/dist/widget/hooks/useCheckout.d.ts.map +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts +1 -1
- package/dist/widget/hooks/useCurrentScreen.d.ts.map +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts +1 -1
- package/dist/widget/hooks/useDebugScreens.d.ts.map +1 -1
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts +54 -0
- package/dist/widget/hooks/useDefaultTokenSelection.d.ts.map +1 -0
- package/dist/widget/hooks/useIntentTransactionHistory.d.ts.map +1 -1
- package/dist/widget/hooks/usePayMessage.d.ts +34 -0
- package/dist/widget/hooks/usePayMessage.d.ts.map +1 -0
- package/dist/widget/hooks/useRecipients.d.ts +17 -0
- package/dist/widget/hooks/useRecipients.d.ts.map +1 -0
- package/dist/widget/hooks/useSelectedRecipient.d.ts +12 -0
- package/dist/widget/hooks/useSelectedRecipient.d.ts.map +1 -0
- package/dist/widget/hooks/useSendForm.d.ts +2 -0
- package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
- package/dist/widget/hooks/useSwapAmount.d.ts +13 -0
- package/dist/widget/hooks/useSwapAmount.d.ts.map +1 -0
- package/dist/widget/hooks/useSwapSettings.d.ts +16 -0
- package/dist/widget/hooks/useSwapSettings.d.ts.map +1 -0
- package/dist/widget/hooks/useTargetAmount.d.ts +5 -0
- package/dist/widget/hooks/useTargetAmount.d.ts.map +1 -0
- package/dist/widget/hooks/useTheme.d.ts +14 -0
- package/dist/widget/hooks/useTheme.d.ts.map +1 -0
- package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
- package/dist/widget/index.js +2 -2
- package/dist/widget/widget.d.ts +9 -0
- package/dist/widget/widget.d.ts.map +1 -1
- package/package.json +29 -28
- package/src/aave.ts +6 -1
- package/src/analytics.ts +103 -53
- package/src/apiClient.ts +1 -1
- package/src/{proxyCaller.ts → balanceInjector.ts} +22 -17
- package/src/cctp.ts +6 -2
- package/src/cctpqueue.ts +7 -7
- package/src/chains.ts +8 -0
- package/src/config.ts +40 -9
- package/src/constants.ts +11 -8
- package/src/contractUtils.ts +33 -2
- package/src/customChains.ts +24 -0
- package/src/index.ts +11 -1
- package/src/intentEntrypoint.ts +253 -0
- package/src/intents.ts +87 -54
- package/src/metaTxnMonitor.ts +1 -0
- package/src/morpho.ts +13 -2
- package/src/pools.ts +68 -86
- package/src/prepareSend.ts +437 -207
- package/src/prices.ts +51 -7
- package/src/relaySdk.ts +6 -4
- package/src/relayer.ts +2 -0
- package/src/toast.ts +110 -0
- package/src/tokenBalances.ts +112 -20
- package/src/tokens.ts +70 -7
- package/src/trails.ts +80 -77
- package/src/trailsClient.ts +45 -0
- package/src/transactions.ts +27 -35
- package/src/umd.tsx +1 -1
- package/src/wallets.ts +2 -1
- package/src/widget/assets/sequence-logo.svg +15 -0
- package/src/widget/compiled.css +2 -2
- package/src/widget/components/AccountActionsDropdown.tsx +18 -159
- package/src/widget/components/AccountIntentTransactionHistory.tsx +346 -63
- package/src/widget/components/AccountSettings.tsx +96 -0
- package/src/widget/components/ChainFilterDropdown.tsx +1 -1
- package/src/widget/components/ChainList.tsx +10 -20
- package/src/widget/components/ClassicSwap.tsx +923 -0
- package/src/widget/components/ConfigDisplay.tsx +8 -5
- package/src/widget/components/ConnectedWallets.tsx +260 -0
- package/src/widget/components/DebugMenu.tsx +2 -0
- package/src/widget/components/DebugScreensList.tsx +3 -0
- package/src/widget/components/DebugToast.tsx +63 -0
- package/src/widget/components/Earn.tsx +108 -116
- package/src/widget/components/EarnPools.tsx +2 -4
- package/src/widget/components/EarnPoolsFilters.tsx +6 -6
- package/src/widget/components/Fund.tsx +1245 -0
- package/src/widget/components/FundMethods.tsx +1 -1
- package/src/widget/components/FundSendForm.tsx +1 -1
- package/src/widget/components/Identicon.tsx +158 -0
- package/src/widget/components/Pay.tsx +1088 -0
- package/src/widget/components/PaySendForm.tsx +1 -1
- package/src/widget/components/QuoteDetails.tsx +1 -1
- package/src/widget/components/Receipt.tsx +1 -1
- package/src/widget/components/Receive.tsx +4 -2
- package/src/widget/components/RecentTokens.tsx +2 -1
- package/src/widget/components/Recipients.tsx +448 -0
- package/src/widget/components/RefundWarning.tsx +61 -0
- package/src/widget/components/ScreenHeader.tsx +1 -1
- package/src/widget/components/SimpleSwap.tsx +74 -58
- package/src/widget/components/Swap.tsx +35 -853
- package/src/widget/components/SwapSettings.tsx +5 -11
- package/src/widget/components/ThemeProvider.tsx +32 -0
- package/src/widget/components/ThemeSyncer.tsx +47 -0
- package/src/widget/components/Toast.tsx +315 -0
- package/src/widget/components/TokenList.tsx +2 -34
- package/src/widget/components/TokenSelector.tsx +3 -3
- package/src/widget/components/TransactionDetails.tsx +153 -13
- package/src/widget/components/TruncatedAddress.tsx +5 -1
- package/src/widget/components/UserPreferences.tsx +156 -0
- package/src/widget/components/WalletList.tsx +1 -1
- package/src/widget/hooks/useBalanceVisible.tsx +40 -2
- package/src/widget/hooks/useCheckout.ts +13 -0
- package/src/widget/hooks/useCurrentScreen.tsx +3 -0
- package/src/widget/hooks/useDebugScreens.ts +12 -2
- package/src/widget/hooks/useDefaultTokenSelection.tsx +475 -0
- package/src/widget/hooks/useIntentTransactionHistory.ts +212 -0
- package/src/widget/hooks/usePayMessage.tsx +370 -0
- package/src/widget/hooks/useRecipients.ts +168 -0
- package/src/widget/hooks/useSelectedRecipient.tsx +48 -0
- package/src/widget/hooks/useSendForm.ts +179 -26
- package/src/widget/hooks/useSwapAmount.tsx +50 -0
- package/src/widget/hooks/useSwapSettings.tsx +100 -0
- package/src/widget/hooks/useTargetAmount.ts +23 -0
- package/src/widget/hooks/useTheme.tsx +80 -0
- package/src/widget/hooks/useTokenList.ts +20 -11
- package/src/widget/index.css +45 -21
- package/src/widget/widget.tsx +164 -68
- package/dist/address.d.ts +0 -2
- package/dist/address.d.ts.map +0 -1
- package/dist/proxyCaller.d.ts.map +0 -1
- package/src/address.ts +0 -6
package/src/prices.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
SequenceAPIClient,
|
|
3
|
-
Token,
|
|
4
|
-
TokenPrice,
|
|
5
|
-
} from "@0xsequence/trails-api"
|
|
1
|
+
import type { SequenceAPIClient, Token, TokenPrice } from "@0xsequence/api"
|
|
6
2
|
import { QueryClient, useQuery } from "@tanstack/react-query"
|
|
7
3
|
import { zeroAddress } from "viem"
|
|
8
4
|
import { logger } from "./logger.js"
|
|
@@ -193,13 +189,61 @@ export function calcAmountUsdPrice({
|
|
|
193
189
|
amount: number | string
|
|
194
190
|
usdPrice: string | number | null | undefined
|
|
195
191
|
}) {
|
|
196
|
-
|
|
192
|
+
const sanitizedAmount = normalizeNumber(amount)
|
|
193
|
+
const sanitizedPrice = normalizeNumber(usdPrice)
|
|
194
|
+
|
|
195
|
+
// Validate inputs
|
|
196
|
+
if (
|
|
197
|
+
!Number.isFinite(sanitizedAmount) ||
|
|
198
|
+
Number.isNaN(sanitizedAmount) ||
|
|
199
|
+
sanitizedAmount < 0
|
|
200
|
+
) {
|
|
201
|
+
return 0
|
|
202
|
+
}
|
|
203
|
+
if (
|
|
204
|
+
!Number.isFinite(sanitizedPrice) ||
|
|
205
|
+
Number.isNaN(sanitizedPrice) ||
|
|
206
|
+
sanitizedPrice < 0
|
|
207
|
+
) {
|
|
208
|
+
return 0
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const result = sanitizedAmount * sanitizedPrice
|
|
212
|
+
|
|
213
|
+
// Validate result
|
|
214
|
+
if (!Number.isFinite(result) || Number.isNaN(result)) {
|
|
215
|
+
logger.console.error("[trails-sdk] Error calculating amount USD:", {
|
|
216
|
+
sanitizedAmount,
|
|
217
|
+
sanitizedPrice,
|
|
218
|
+
result,
|
|
219
|
+
amount,
|
|
220
|
+
usdPrice,
|
|
221
|
+
})
|
|
222
|
+
return 0
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return result
|
|
197
226
|
}
|
|
198
227
|
|
|
199
228
|
export function normalizeNumber(
|
|
200
229
|
number: number | string | null | undefined,
|
|
201
230
|
): number {
|
|
202
|
-
|
|
231
|
+
if (number === null || number === undefined) {
|
|
232
|
+
return 0
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const normalized = Number(number?.toString().replace(/[^0-9.-]/g, "") || 0)
|
|
236
|
+
|
|
237
|
+
// Return 0 for invalid numbers
|
|
238
|
+
if (!Number.isFinite(normalized) || Number.isNaN(normalized)) {
|
|
239
|
+
logger.console.error("[trails-sdk] Error normalizing number:", {
|
|
240
|
+
number,
|
|
241
|
+
normalized,
|
|
242
|
+
})
|
|
243
|
+
return 0
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return normalized
|
|
203
247
|
}
|
|
204
248
|
|
|
205
249
|
// Format TVL (Total Value Locked) for display
|
package/src/relaySdk.ts
CHANGED
|
@@ -69,6 +69,7 @@ import {
|
|
|
69
69
|
arenaz,
|
|
70
70
|
b3,
|
|
71
71
|
} from "viem/chains"
|
|
72
|
+
import { somnia } from "./customChains.js"
|
|
72
73
|
import { logger } from "./logger.js"
|
|
73
74
|
|
|
74
75
|
export type Chain = any
|
|
@@ -140,6 +141,7 @@ export const relaySupportedChains: Record<number, Chain> = {
|
|
|
140
141
|
[zksync.id]: zksync,
|
|
141
142
|
[zora.id]: zora,
|
|
142
143
|
[katana.id]: katana,
|
|
144
|
+
[somnia.id]: somnia,
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
getRelaySupportedChains().then((relayChains) => {
|
|
@@ -347,7 +349,7 @@ export async function getRelaySupportedTokens(): Promise<RelayToken[]> {
|
|
|
347
349
|
if (!chain.disabled) {
|
|
348
350
|
// Add native currency
|
|
349
351
|
tokens.push({
|
|
350
|
-
id: chain.currency.id
|
|
352
|
+
id: chain.currency.id || `${chain.currency.symbol}-${chain.name}`,
|
|
351
353
|
symbol: chain.currency.symbol,
|
|
352
354
|
name: chain.currency.name,
|
|
353
355
|
contractAddress: chain.currency.address,
|
|
@@ -360,7 +362,7 @@ export async function getRelaySupportedTokens(): Promise<RelayToken[]> {
|
|
|
360
362
|
// Add featured tokens
|
|
361
363
|
chain.featuredTokens.forEach((token) => {
|
|
362
364
|
tokens.push({
|
|
363
|
-
id: token.id
|
|
365
|
+
id: token.id || `${token.symbol}-${chain.name}`,
|
|
364
366
|
symbol: token.symbol,
|
|
365
367
|
name: token.name,
|
|
366
368
|
contractAddress: token.address,
|
|
@@ -374,7 +376,7 @@ export async function getRelaySupportedTokens(): Promise<RelayToken[]> {
|
|
|
374
376
|
// Add ERC20 currencies
|
|
375
377
|
chain.erc20Currencies.forEach((token) => {
|
|
376
378
|
tokens.push({
|
|
377
|
-
id: token.id
|
|
379
|
+
id: token.id || `${token.symbol}-${chain.name}`,
|
|
378
380
|
symbol: token.symbol,
|
|
379
381
|
name: token.name,
|
|
380
382
|
contractAddress: token.address,
|
|
@@ -388,7 +390,7 @@ export async function getRelaySupportedTokens(): Promise<RelayToken[]> {
|
|
|
388
390
|
// Add solver currencies (fallback for chains that might not have featuredTokens/erc20Currencies)
|
|
389
391
|
chain.solverCurrencies.forEach((token) => {
|
|
390
392
|
tokens.push({
|
|
391
|
-
id: token.id
|
|
393
|
+
id: token.id || `${token.symbol}-${chain.name}`,
|
|
392
394
|
symbol: token.symbol,
|
|
393
395
|
name: token.name,
|
|
394
396
|
contractAddress: token.address,
|
package/src/relayer.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
blast,
|
|
25
25
|
optimism,
|
|
26
26
|
} from "viem/chains"
|
|
27
|
+
import { somnia } from "./customChains.js"
|
|
27
28
|
|
|
28
29
|
export interface MetaTxnReceiptLog {
|
|
29
30
|
address: string
|
|
@@ -74,6 +75,7 @@ const relayerChainIdToSlug: Record<number, string> = {
|
|
|
74
75
|
[blast.id]: "blast",
|
|
75
76
|
[gnosis.id]: "gnosis",
|
|
76
77
|
[soneium.id]: "soneium",
|
|
78
|
+
[somnia.id]: "somnia",
|
|
77
79
|
[xai.id]: "xai",
|
|
78
80
|
[bsc.id]: "bsc",
|
|
79
81
|
[katana.id]: "katana",
|
package/src/toast.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { ToastType } from "./widget/components/Toast.js"
|
|
2
|
+
|
|
3
|
+
let globalShowToast:
|
|
4
|
+
| ((
|
|
5
|
+
title: string,
|
|
6
|
+
message: string,
|
|
7
|
+
type?: ToastType,
|
|
8
|
+
duration?: number,
|
|
9
|
+
iconUrl?: string,
|
|
10
|
+
) => void)
|
|
11
|
+
| null = null
|
|
12
|
+
|
|
13
|
+
let globalUpdatePersistentToast:
|
|
14
|
+
| ((
|
|
15
|
+
title: string,
|
|
16
|
+
message: string,
|
|
17
|
+
type?: ToastType,
|
|
18
|
+
iconUrl?: string,
|
|
19
|
+
) => void)
|
|
20
|
+
| null = null
|
|
21
|
+
|
|
22
|
+
let globalRemovePersistentToast: (() => void) | null = null
|
|
23
|
+
|
|
24
|
+
let toastEnabled = false
|
|
25
|
+
|
|
26
|
+
export function setGlobalShowToast(
|
|
27
|
+
showToast: (
|
|
28
|
+
title: string,
|
|
29
|
+
message: string,
|
|
30
|
+
type?: ToastType,
|
|
31
|
+
duration?: number,
|
|
32
|
+
iconUrl?: string,
|
|
33
|
+
) => void,
|
|
34
|
+
) {
|
|
35
|
+
globalShowToast = showToast
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function setGlobalUpdatePersistentToast(
|
|
39
|
+
updatePersistentToast: (
|
|
40
|
+
title: string,
|
|
41
|
+
message: string,
|
|
42
|
+
type?: ToastType,
|
|
43
|
+
iconUrl?: string,
|
|
44
|
+
) => void,
|
|
45
|
+
) {
|
|
46
|
+
globalUpdatePersistentToast = updatePersistentToast
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function setGlobalRemovePersistentToast(
|
|
50
|
+
removePersistentToast: () => void,
|
|
51
|
+
) {
|
|
52
|
+
globalRemovePersistentToast = removePersistentToast
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function setToastEnabled(enabled: boolean) {
|
|
56
|
+
toastEnabled = enabled
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function showToast(
|
|
60
|
+
title: string,
|
|
61
|
+
message: string,
|
|
62
|
+
type?: ToastType,
|
|
63
|
+
duration?: number,
|
|
64
|
+
iconUrl?: string,
|
|
65
|
+
) {
|
|
66
|
+
if (!toastEnabled) {
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (globalShowToast) {
|
|
71
|
+
globalShowToast(title, message, type, duration, iconUrl)
|
|
72
|
+
} else {
|
|
73
|
+
console.warn(
|
|
74
|
+
"[trails-sdk] Toast not available yet. Make sure ToastProvider is mounted.",
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function updatePersistentToast(
|
|
80
|
+
title: string,
|
|
81
|
+
message: string,
|
|
82
|
+
type?: ToastType,
|
|
83
|
+
iconUrl?: string,
|
|
84
|
+
) {
|
|
85
|
+
if (!toastEnabled) {
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (globalUpdatePersistentToast) {
|
|
90
|
+
globalUpdatePersistentToast(title, message, type, iconUrl)
|
|
91
|
+
} else {
|
|
92
|
+
console.warn(
|
|
93
|
+
"[trails-sdk] Toast not available yet. Make sure ToastProvider is mounted.",
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function removePersistentToast() {
|
|
99
|
+
if (!toastEnabled) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (globalRemovePersistentToast) {
|
|
104
|
+
globalRemovePersistentToast()
|
|
105
|
+
} else {
|
|
106
|
+
console.warn(
|
|
107
|
+
"[trails-sdk] Toast not available yet. Make sure ToastProvider is mounted.",
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/tokenBalances.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
TokenBalance,
|
|
8
8
|
} from "@0xsequence/indexer"
|
|
9
9
|
import { ContractVerificationStatus } from "@0xsequence/indexer"
|
|
10
|
-
import type { Page, Price, SequenceAPIClient } from "@0xsequence/
|
|
10
|
+
import type { Page, Price, SequenceAPIClient } from "@0xsequence/api"
|
|
11
11
|
import { QueryClient, useQuery } from "@tanstack/react-query"
|
|
12
12
|
import type { Address } from "ox"
|
|
13
13
|
import { useEffect, useState } from "react"
|
|
@@ -16,6 +16,7 @@ import { useAPIClient } from "./apiClient.js"
|
|
|
16
16
|
import { useIndexerGatewayClient } from "./indexerClient.js"
|
|
17
17
|
import { getTokenPrices, useTokenPrices } from "./prices.js"
|
|
18
18
|
import { logger } from "./logger.js"
|
|
19
|
+
import { getChainInfo } from "./chains.js"
|
|
19
20
|
|
|
20
21
|
export type { NativeTokenBalance, TokenBalance }
|
|
21
22
|
|
|
@@ -30,6 +31,7 @@ const tokenBalancesQueryClient = new QueryClient({
|
|
|
30
31
|
retry: 2,
|
|
31
32
|
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
|
|
32
33
|
refetchOnWindowFocus: false,
|
|
34
|
+
refetchIntervalInBackground: true, // Refetch in background
|
|
33
35
|
refetchOnReconnect: true,
|
|
34
36
|
},
|
|
35
37
|
},
|
|
@@ -42,16 +44,20 @@ const defaultPage = { page: 1, pageSize: 10, more: false }
|
|
|
42
44
|
export function isNativeToken(
|
|
43
45
|
token: TokenBalance | NativeTokenBalance,
|
|
44
46
|
): token is NativeTokenBalance {
|
|
45
|
-
if ("contractAddress" in token) {
|
|
46
|
-
return
|
|
47
|
+
if (!("contractAddress" in token)) {
|
|
48
|
+
return true
|
|
49
|
+
}
|
|
50
|
+
if (token.contractAddress === zeroAddress) {
|
|
51
|
+
return true
|
|
47
52
|
}
|
|
48
|
-
return
|
|
53
|
+
return false
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
export interface TokenBalanceWithPrice extends TokenBalance {
|
|
52
57
|
price?: Price
|
|
53
58
|
balanceUsd?: number
|
|
54
59
|
balanceUsdFormatted?: string
|
|
60
|
+
chainName?: string
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
export interface NativeTokenBalanceWithPrice extends NativeTokenBalance {
|
|
@@ -59,6 +65,7 @@ export interface NativeTokenBalanceWithPrice extends NativeTokenBalance {
|
|
|
59
65
|
balanceUsd?: number
|
|
60
66
|
balanceUsdFormatted?: string
|
|
61
67
|
symbol?: string
|
|
68
|
+
chainName?: string
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
export type TokenBalanceExtended =
|
|
@@ -132,6 +139,7 @@ export interface GetTokenBalancesWithPrice {
|
|
|
132
139
|
symbol?: string
|
|
133
140
|
balanceUsd?: number
|
|
134
141
|
balanceUsdFormatted?: string
|
|
142
|
+
chainName?: string
|
|
135
143
|
}
|
|
136
144
|
>
|
|
137
145
|
balances: Array<
|
|
@@ -139,6 +147,7 @@ export interface GetTokenBalancesWithPrice {
|
|
|
139
147
|
price?: Price
|
|
140
148
|
balanceUsd?: number
|
|
141
149
|
balanceUsdFormatted?: string
|
|
150
|
+
chainName?: string
|
|
142
151
|
}
|
|
143
152
|
>
|
|
144
153
|
}
|
|
@@ -255,8 +264,16 @@ export function useTokenBalances(
|
|
|
255
264
|
}
|
|
256
265
|
|
|
257
266
|
const balances = [
|
|
258
|
-
...tokenBalancesData.nativeBalances
|
|
259
|
-
|
|
267
|
+
...tokenBalancesData.nativeBalances.map((token) => ({
|
|
268
|
+
...token,
|
|
269
|
+
contractAddress: zeroAddress, // Native tokens always use zero address
|
|
270
|
+
chainName: getChainInfo(token.chainId)?.name || "",
|
|
271
|
+
})),
|
|
272
|
+
...tokenBalancesData.balances.map((token) => ({
|
|
273
|
+
...token,
|
|
274
|
+
contractAddress: token.contractAddress,
|
|
275
|
+
chainName: getChainInfo(token.contractInfo?.chainId)?.name || "",
|
|
276
|
+
})),
|
|
260
277
|
].filter((token) => {
|
|
261
278
|
try {
|
|
262
279
|
return BigInt(token.balance) > 0n
|
|
@@ -301,6 +318,7 @@ export function useTokenBalances(
|
|
|
301
318
|
!!tokenPrices,
|
|
302
319
|
staleTime: REFRESH_INTERVAL, // 10 seconds for sorted tokens
|
|
303
320
|
gcTime: REFRESH_INTERVAL, // 10 seconds cache time
|
|
321
|
+
refetchIntervalInBackground: true, // Refetch in background
|
|
304
322
|
refetchOnWindowFocus: true,
|
|
305
323
|
})
|
|
306
324
|
|
|
@@ -318,17 +336,38 @@ export function useTokenBalances(
|
|
|
318
336
|
// Helper to format balance
|
|
319
337
|
export function formatRawAmount(
|
|
320
338
|
balance: string | bigint,
|
|
321
|
-
decimals: number
|
|
339
|
+
decimals: number,
|
|
322
340
|
): string {
|
|
323
341
|
if (!balance) {
|
|
324
342
|
return "0"
|
|
325
343
|
}
|
|
344
|
+
|
|
326
345
|
try {
|
|
327
|
-
|
|
346
|
+
// Validate decimals
|
|
347
|
+
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 18) {
|
|
348
|
+
throw new Error("Invalid decimals")
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (decimals === 0) {
|
|
352
|
+
throw new Error("Invalid decimals, decimals cannot be 0")
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const balanceBigInt = BigInt(balance)
|
|
356
|
+
|
|
357
|
+
// Check for negative balance
|
|
358
|
+
if (balanceBigInt < 0n) {
|
|
359
|
+
throw new Error("Negative balance")
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const formatted = formatUnits(balanceBigInt, decimals)
|
|
328
363
|
return formatAmount(formatted)
|
|
329
|
-
} catch (
|
|
330
|
-
logger.console.error("[trails-sdk] Error formatting balance:",
|
|
331
|
-
|
|
364
|
+
} catch (err) {
|
|
365
|
+
logger.console.error("[trails-sdk] Error formatting balance:", {
|
|
366
|
+
balance,
|
|
367
|
+
decimals,
|
|
368
|
+
err,
|
|
369
|
+
})
|
|
370
|
+
throw err
|
|
332
371
|
}
|
|
333
372
|
}
|
|
334
373
|
|
|
@@ -337,12 +376,45 @@ export function getTokenBalanceUsd(
|
|
|
337
376
|
tokenPrice: Price,
|
|
338
377
|
): number {
|
|
339
378
|
const isNative = isNativeToken(token)
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
379
|
+
const decimals = isNative ? 18 : token.contractInfo?.decimals
|
|
380
|
+
if (!decimals) {
|
|
381
|
+
throw new Error("Decimals not found")
|
|
382
|
+
}
|
|
383
|
+
const formattedBalance = formatRawAmount(token.balance, decimals)
|
|
384
|
+
|
|
344
385
|
const priceUsd = Number(tokenPrice.value) ?? 0
|
|
345
|
-
|
|
386
|
+
|
|
387
|
+
// Validate price
|
|
388
|
+
if (!Number.isFinite(priceUsd) || Number.isNaN(priceUsd) || priceUsd < 0) {
|
|
389
|
+
return 0
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const balanceNum = Number(formattedBalance)
|
|
393
|
+
|
|
394
|
+
// Validate balance
|
|
395
|
+
if (
|
|
396
|
+
!Number.isFinite(balanceNum) ||
|
|
397
|
+
Number.isNaN(balanceNum) ||
|
|
398
|
+
balanceNum < 0
|
|
399
|
+
) {
|
|
400
|
+
return 0
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const result = balanceNum * priceUsd
|
|
404
|
+
|
|
405
|
+
// Validate result
|
|
406
|
+
if (!Number.isFinite(result) || Number.isNaN(result)) {
|
|
407
|
+
logger.console.error("[trails-sdk] Error calculating balance USD:", {
|
|
408
|
+
balanceNum,
|
|
409
|
+
priceUsd,
|
|
410
|
+
result,
|
|
411
|
+
token,
|
|
412
|
+
tokenPrice,
|
|
413
|
+
})
|
|
414
|
+
return 0
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return result
|
|
346
418
|
}
|
|
347
419
|
|
|
348
420
|
export function formatAmount(
|
|
@@ -395,14 +467,30 @@ export function formatUsdAmountDisplay(value: number | string = 0): string {
|
|
|
395
467
|
if (!value) {
|
|
396
468
|
value = 0
|
|
397
469
|
}
|
|
470
|
+
|
|
471
|
+
const numValue = Number(value)
|
|
472
|
+
|
|
473
|
+
// Validate input
|
|
474
|
+
if (!Number.isFinite(numValue) || Number.isNaN(numValue)) {
|
|
475
|
+
return "$0.00"
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Handle negative values explicitly
|
|
479
|
+
const isNegative = numValue < 0
|
|
480
|
+
const absValue = Math.abs(numValue)
|
|
481
|
+
|
|
482
|
+
// Cap at reasonable max to prevent display issues (100 trillion)
|
|
483
|
+
const MAX_DISPLAY_VALUE = 100_000_000_000_000
|
|
484
|
+
const cappedValue = Math.min(absValue, MAX_DISPLAY_VALUE)
|
|
485
|
+
|
|
398
486
|
const displayValue = Intl.NumberFormat("en-US", {
|
|
399
487
|
style: "currency",
|
|
400
488
|
currency: "USD",
|
|
401
489
|
maximumFractionDigits: 2,
|
|
402
490
|
minimumFractionDigits: 2,
|
|
403
|
-
}).format(
|
|
491
|
+
}).format(isNegative ? -cappedValue : cappedValue)
|
|
404
492
|
|
|
405
|
-
if (displayValue === "$0.00" &&
|
|
493
|
+
if (displayValue === "$0.00" && absValue > 0 && absValue < 0.01) {
|
|
406
494
|
return `<$0.01`
|
|
407
495
|
}
|
|
408
496
|
|
|
@@ -658,7 +746,7 @@ export async function getHasSufficientBalanceToken({
|
|
|
658
746
|
indexerGatewayClient,
|
|
659
747
|
apiClient,
|
|
660
748
|
})
|
|
661
|
-
const tokenBalance = balances.find(
|
|
749
|
+
const tokenBalance: TokenBalanceExtended | null | undefined = balances.find(
|
|
662
750
|
(b) =>
|
|
663
751
|
b.chainId === chainId &&
|
|
664
752
|
(b.contractAddress?.toLowerCase() === token.toLowerCase() ||
|
|
@@ -667,7 +755,11 @@ export async function getHasSufficientBalanceToken({
|
|
|
667
755
|
if (!tokenBalance) {
|
|
668
756
|
return false
|
|
669
757
|
}
|
|
670
|
-
const
|
|
758
|
+
const isNative = isNativeToken(tokenBalance)
|
|
759
|
+
const decimals = isNative ? 18 : tokenBalance?.contractInfo?.decimals
|
|
760
|
+
if (!decimals) {
|
|
761
|
+
throw new Error("Decimals not found")
|
|
762
|
+
}
|
|
671
763
|
return tokenBalance?.balance
|
|
672
764
|
? BigInt(tokenBalance.balance) >= parseUnits(amount, decimals)
|
|
673
765
|
: false
|
package/src/tokens.ts
CHANGED
|
@@ -13,10 +13,11 @@ import {
|
|
|
13
13
|
unichain,
|
|
14
14
|
worldchain,
|
|
15
15
|
} from "viem/chains"
|
|
16
|
-
import { getRelaySupportedTokens } from "./relaySdk.js"
|
|
16
|
+
import { getRelaySupportedTokens, type Chain } from "./relaySdk.js"
|
|
17
17
|
import { useReadContracts } from "wagmi"
|
|
18
18
|
import { useMemo } from "react"
|
|
19
19
|
import { logger } from "./logger.js"
|
|
20
|
+
import { somnia } from "./customChains.js"
|
|
20
21
|
|
|
21
22
|
export type SupportedToken = {
|
|
22
23
|
id: string
|
|
@@ -42,6 +43,7 @@ export const commonTokenImages: Record<string, string> = {
|
|
|
42
43
|
LINK: "https://assets.sequence.info/images/tokens/large/1/0x514910771af9ca656af840dff83e8264ecf986ca.webp",
|
|
43
44
|
XTZ: "https://assets.sequence.info/images/tokens/large/42793/0x0000000000000000000000000000000000000000.webp",
|
|
44
45
|
WXTZ: "https://assets.coingecko.com/coins/images/976/standard/Tezos-logo.png?1696502091",
|
|
46
|
+
SOMI: "https://assets.sequence.info/images/tokens/large/5031/0x0000000000000000000000000000000000000000.webp",
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
const cacheVersion = "01"
|
|
@@ -375,14 +377,32 @@ export async function getSupportedTokens(): Promise<SupportedToken[]> {
|
|
|
375
377
|
}
|
|
376
378
|
}
|
|
377
379
|
|
|
378
|
-
|
|
380
|
+
let supportedChains: Chain[] = []
|
|
381
|
+
try {
|
|
382
|
+
supportedChains = await getSupportedChains({ quoteProvider: "relay" })
|
|
383
|
+
} catch (error) {
|
|
384
|
+
logger.console.error("[trails-sdk] Error getting supported chains:", error)
|
|
385
|
+
// Fallback: include all chains that have tokens
|
|
386
|
+
supportedChains = []
|
|
387
|
+
}
|
|
388
|
+
|
|
379
389
|
const allTokens = [...tokens, ...additionalTokens]
|
|
380
390
|
const supportedChainTokens = allTokens.filter((token) => {
|
|
381
391
|
// Always include Etherlink tokens
|
|
382
392
|
if (token.chainId === etherlink.id) {
|
|
383
393
|
return true
|
|
384
394
|
}
|
|
385
|
-
|
|
395
|
+
// If we have supported chains, filter by them
|
|
396
|
+
if (supportedChains.length > 0) {
|
|
397
|
+
return supportedChains.some((chain) => chain.id === token.chainId)
|
|
398
|
+
}
|
|
399
|
+
// Fallback: include tokens from chains that are in both relay tokens and common tokens
|
|
400
|
+
const relayChainIds = tokens.map((t) => t.chainId)
|
|
401
|
+
const commonChainIds = commonTokens.map((t) => t.chainId)
|
|
402
|
+
const supportedChainIds = [
|
|
403
|
+
...new Set([...relayChainIds, ...commonChainIds]),
|
|
404
|
+
]
|
|
405
|
+
return supportedChainIds.includes(token.chainId)
|
|
386
406
|
})
|
|
387
407
|
const uniqueTokens = supportedChainTokens.filter(
|
|
388
408
|
(token, index, self) =>
|
|
@@ -521,12 +541,13 @@ export async function getTokenInfo(
|
|
|
521
541
|
// Check if it's a native token
|
|
522
542
|
const chainInfo = getChainInfo(chainId)
|
|
523
543
|
if (normalizedAddress === zeroAddress.toLowerCase()) {
|
|
544
|
+
const decimals = chainInfo?.nativeCurrency.decimals ?? 18
|
|
524
545
|
const nativeInfo: SupportedToken = {
|
|
525
546
|
id: `${chainInfo?.nativeCurrency.symbol || "ETH"}-${chainInfo?.name || "ethereum"}`,
|
|
526
547
|
symbol: chainInfo?.nativeCurrency.symbol || "ETH",
|
|
527
548
|
name: chainInfo?.nativeCurrency.name || "Ethereum",
|
|
528
549
|
contractAddress: zeroAddress,
|
|
529
|
-
decimals
|
|
550
|
+
decimals,
|
|
530
551
|
chainId,
|
|
531
552
|
chainName: chainInfo?.name || "Ethereum",
|
|
532
553
|
imageUrl: getTokenImageUrl({
|
|
@@ -803,6 +824,10 @@ export function useTokenInfo({
|
|
|
803
824
|
// Always call hooks unconditionally
|
|
804
825
|
const isAddress = address?.startsWith("0x") ?? false
|
|
805
826
|
|
|
827
|
+
// Handle any native token (zero address) - before any blockchain calls
|
|
828
|
+
const isNativeToken =
|
|
829
|
+
address?.toLowerCase() === zeroAddress.toLowerCase() && !!chainId
|
|
830
|
+
|
|
806
831
|
// Check cache first
|
|
807
832
|
const cachedInfo = useMemo(() => {
|
|
808
833
|
if (!isAddress || !chainId) return null
|
|
@@ -816,7 +841,7 @@ export function useTokenInfo({
|
|
|
816
841
|
} as const
|
|
817
842
|
const result = useReadContracts({
|
|
818
843
|
contracts:
|
|
819
|
-
!!isAddress && !!chainId && !cachedInfo
|
|
844
|
+
!!isAddress && !!chainId && !cachedInfo && !isNativeToken
|
|
820
845
|
? [
|
|
821
846
|
{ ...contract, functionName: "name" },
|
|
822
847
|
{ ...contract, functionName: "symbol" },
|
|
@@ -825,11 +850,33 @@ export function useTokenInfo({
|
|
|
825
850
|
: [],
|
|
826
851
|
})
|
|
827
852
|
const error =
|
|
828
|
-
result?.error ?? result?.data?.find((r) => r.error)?.error ?? null
|
|
853
|
+
result?.error ?? result?.data?.find((r: any) => r.error)?.error ?? null
|
|
829
854
|
const [name, symbol, decimals] = result.data ?? []
|
|
830
855
|
const chainInfo = getChainInfo(chainId!)
|
|
831
856
|
|
|
832
857
|
const tokenInfo = useMemo(() => {
|
|
858
|
+
// Handle native token (zero address) for any chain
|
|
859
|
+
if (isNativeToken) {
|
|
860
|
+
const nativeSymbol = chainInfo?.nativeCurrency.symbol || "ETH"
|
|
861
|
+
const nativeName = chainInfo?.nativeCurrency.name || "Ethereum"
|
|
862
|
+
const decimals = chainInfo?.nativeCurrency.decimals ?? 18
|
|
863
|
+
|
|
864
|
+
return {
|
|
865
|
+
id: `${nativeSymbol}-${chainInfo?.name || ""}`,
|
|
866
|
+
name: nativeName,
|
|
867
|
+
symbol: nativeSymbol,
|
|
868
|
+
decimals,
|
|
869
|
+
chainId: chainId!,
|
|
870
|
+
contractAddress: zeroAddress,
|
|
871
|
+
chainName: chainInfo?.name || "",
|
|
872
|
+
imageUrl: getTokenImageUrl({
|
|
873
|
+
chainId,
|
|
874
|
+
contractAddress: zeroAddress,
|
|
875
|
+
symbol: nativeSymbol,
|
|
876
|
+
}),
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
833
880
|
// If we have cached info, use it
|
|
834
881
|
if (cachedInfo) {
|
|
835
882
|
return {
|
|
@@ -869,10 +916,14 @@ export function useTokenInfo({
|
|
|
869
916
|
|
|
870
917
|
return null
|
|
871
918
|
}, [
|
|
872
|
-
cachedInfo,
|
|
873
919
|
address,
|
|
874
920
|
chainId,
|
|
921
|
+
isNativeToken,
|
|
922
|
+
cachedInfo,
|
|
875
923
|
chainInfo?.name,
|
|
924
|
+
chainInfo?.nativeCurrency.symbol,
|
|
925
|
+
chainInfo?.nativeCurrency.name,
|
|
926
|
+
chainInfo?.nativeCurrency.decimals,
|
|
876
927
|
name?.result,
|
|
877
928
|
symbol?.result,
|
|
878
929
|
decimals?.result,
|
|
@@ -1163,6 +1214,18 @@ export const commonTokens: SupportedToken[] = [
|
|
|
1163
1214
|
chainName: etherlink.name,
|
|
1164
1215
|
imageUrl: commonTokenImages.USDT as string,
|
|
1165
1216
|
},
|
|
1217
|
+
|
|
1218
|
+
// Somnia
|
|
1219
|
+
{
|
|
1220
|
+
id: "SOMI-somnia",
|
|
1221
|
+
symbol: "SOMI",
|
|
1222
|
+
name: "Somnia",
|
|
1223
|
+
contractAddress: "0x0000000000000000000000000000000000000000",
|
|
1224
|
+
decimals: 18,
|
|
1225
|
+
chainId: somnia.id,
|
|
1226
|
+
chainName: somnia.name,
|
|
1227
|
+
imageUrl: commonTokenImages.SOMI as string,
|
|
1228
|
+
},
|
|
1166
1229
|
]
|
|
1167
1230
|
|
|
1168
1231
|
export const wethAddresses: Record<string, string> = {
|