0xtrails 0.12.0 → 0.12.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.
Files changed (205) hide show
  1. package/dist/analytics.d.ts +65 -50
  2. package/dist/analytics.d.ts.map +1 -1
  3. package/dist/{ccip-DtfgR432.js → ccip-62W6LwH2.js} +28 -28
  4. package/dist/chains.d.ts.map +1 -1
  5. package/dist/error.d.ts +2 -0
  6. package/dist/error.d.ts.map +1 -1
  7. package/dist/estimate.d.ts.map +1 -1
  8. package/dist/fees.d.ts.map +1 -1
  9. package/dist/{index-CHiCSmCD.js → index-C0QTNYIA.js} +43750 -41806
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +199 -171
  13. package/dist/localeUtils.d.ts.map +1 -1
  14. package/dist/meld/components/MeldCountriesList.d.ts +0 -2
  15. package/dist/meld/components/MeldCountriesList.d.ts.map +1 -1
  16. package/dist/meld/components/MeldFundMethods.d.ts.map +1 -1
  17. package/dist/meld/components/MeldTokensList.d.ts.map +1 -1
  18. package/dist/meld/utils/meld.d.ts +2 -52
  19. package/dist/meld/utils/meld.d.ts.map +1 -1
  20. package/dist/poolUtils.d.ts.map +1 -1
  21. package/dist/prepareSend.d.ts.map +1 -1
  22. package/dist/prices.d.ts +1 -2
  23. package/dist/prices.d.ts.map +1 -1
  24. package/dist/query/balance.fetchers.d.ts +2 -2
  25. package/dist/query/balance.fetchers.d.ts.map +1 -1
  26. package/dist/query/fiat.fetchers.d.ts +11 -0
  27. package/dist/query/fiat.fetchers.d.ts.map +1 -0
  28. package/dist/query/fiat.hooks.d.ts +18 -0
  29. package/dist/query/fiat.hooks.d.ts.map +1 -0
  30. package/dist/query/fiat.queries.d.ts +24 -0
  31. package/dist/query/fiat.queries.d.ts.map +1 -0
  32. package/dist/query/meld.fetchers.d.ts +19 -0
  33. package/dist/query/meld.fetchers.d.ts.map +1 -0
  34. package/dist/query/meld.hooks.d.ts +4 -0
  35. package/dist/query/meld.hooks.d.ts.map +1 -0
  36. package/dist/query/meld.queries.d.ts +61 -0
  37. package/dist/query/meld.queries.d.ts.map +1 -0
  38. package/dist/recover.d.ts.map +1 -1
  39. package/dist/tokens.d.ts.map +1 -1
  40. package/dist/transactionIntent/deposits/depositOrchestrator.d.ts.map +1 -1
  41. package/dist/transactionIntent/deposits/gaslessDeposit.d.ts.map +1 -1
  42. package/dist/transactionIntent/deposits/standardDeposit.d.ts +7 -1
  43. package/dist/transactionIntent/deposits/standardDeposit.d.ts.map +1 -1
  44. package/dist/transactionIntent/handlers/intentHandler.d.ts +2 -0
  45. package/dist/transactionIntent/handlers/intentHandler.d.ts.map +1 -1
  46. package/dist/transactionIntent/quote/normalizeQuote.d.ts +2 -2
  47. package/dist/transactionIntent/quote/normalizeQuote.d.ts.map +1 -1
  48. package/dist/transactionIntent/quote/quoteHelpers.d.ts +1 -1
  49. package/dist/transactionIntent/quote/quoteHelpers.d.ts.map +1 -1
  50. package/dist/transactionIntent/types.d.ts +2 -0
  51. package/dist/transactionIntent/types.d.ts.map +1 -1
  52. package/dist/transactionIntent/utils/balanceChecker.d.ts +3 -1
  53. package/dist/transactionIntent/utils/balanceChecker.d.ts.map +1 -1
  54. package/dist/transactions.d.ts +2 -9
  55. package/dist/transactions.d.ts.map +1 -1
  56. package/dist/umd/trails.min.js +206 -152
  57. package/dist/utils/fiat.d.ts +8 -0
  58. package/dist/utils/fiat.d.ts.map +1 -0
  59. package/dist/utils/format.d.ts.map +1 -1
  60. package/dist/utils/passthrough.d.ts +5 -2
  61. package/dist/utils/passthrough.d.ts.map +1 -1
  62. package/dist/utils/validation.d.ts +33 -0
  63. package/dist/utils/validation.d.ts.map +1 -1
  64. package/dist/utils.d.ts.map +1 -1
  65. package/dist/walletUtils.d.ts +1 -1
  66. package/dist/walletUtils.d.ts.map +1 -1
  67. package/dist/widget/components/ClassicSwap.d.ts.map +1 -1
  68. package/dist/widget/components/DepositTracker.d.ts.map +1 -1
  69. package/dist/widget/components/Earn.d.ts +2 -0
  70. package/dist/widget/components/Earn.d.ts.map +1 -1
  71. package/dist/widget/components/FeeOption.d.ts.map +1 -1
  72. package/dist/widget/components/Fund.d.ts.map +1 -1
  73. package/dist/widget/components/FundMethods.d.ts.map +1 -1
  74. package/dist/widget/components/HookModalContent.d.ts.map +1 -1
  75. package/dist/widget/components/MeldForm.d.ts.map +1 -1
  76. package/dist/widget/components/MeldHistory.d.ts.map +1 -1
  77. package/dist/widget/components/MeldStepsFlow.d.ts.map +1 -1
  78. package/dist/widget/components/OFTProgressBar.d.ts +2 -0
  79. package/dist/widget/components/OFTProgressBar.d.ts.map +1 -1
  80. package/dist/widget/components/OnRampProviderSelector.d.ts.map +1 -1
  81. package/dist/widget/components/OnrampHistoryRow.d.ts.map +1 -1
  82. package/dist/widget/components/Pay.d.ts.map +1 -1
  83. package/dist/widget/components/PercentageMaxButtons.d.ts.map +1 -1
  84. package/dist/widget/components/PoolDeposit.d.ts +2 -0
  85. package/dist/widget/components/PoolDeposit.d.ts.map +1 -1
  86. package/dist/widget/components/QuoteDetails.d.ts.map +1 -1
  87. package/dist/widget/components/Receipt.d.ts.map +1 -1
  88. package/dist/widget/components/SlippageToleranceSettings.d.ts.map +1 -1
  89. package/dist/widget/components/Swap.d.ts +2 -0
  90. package/dist/widget/components/Swap.d.ts.map +1 -1
  91. package/dist/widget/components/TransferPendingVertical.d.ts.map +1 -1
  92. package/dist/widget/components/Withdraw.d.ts.map +1 -1
  93. package/dist/widget/hooks/useAmountUsd.d.ts.map +1 -1
  94. package/dist/widget/hooks/useCustomTokenSearch.d.ts.map +1 -1
  95. package/dist/widget/hooks/useDisplayCurrencyPreference.d.ts.map +1 -1
  96. package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts +3 -21
  97. package/dist/widget/hooks/useFiatOnRampCurrencies.d.ts.map +1 -1
  98. package/dist/widget/hooks/useMeldTransactionHistory.d.ts.map +1 -1
  99. package/dist/widget/hooks/useOnRampCountryDefaults.d.ts +0 -18
  100. package/dist/widget/hooks/useOnRampCountryDefaults.d.ts.map +1 -1
  101. package/dist/widget/hooks/useOnRampPaymentMethods.d.ts +2 -18
  102. package/dist/widget/hooks/useOnRampPaymentMethods.d.ts.map +1 -1
  103. package/dist/widget/hooks/useOnRampQuote.d.ts.map +1 -1
  104. package/dist/widget/hooks/useQuote.d.ts +5 -1
  105. package/dist/widget/hooks/useQuote.d.ts.map +1 -1
  106. package/dist/widget/hooks/useSendForm.d.ts +3 -1
  107. package/dist/widget/hooks/useSendForm.d.ts.map +1 -1
  108. package/dist/widget/hooks/useTokenList.d.ts.map +1 -1
  109. package/dist/widget/hooks/useTokenWithFreshBalance.d.ts +3 -2
  110. package/dist/widget/hooks/useTokenWithFreshBalance.d.ts.map +1 -1
  111. package/dist/widget/index.js +1 -1
  112. package/dist/widget/types/commonProps.d.ts +2 -0
  113. package/dist/widget/types/commonProps.d.ts.map +1 -1
  114. package/dist/widget/utils/transactionFailure.d.ts +20 -0
  115. package/dist/widget/utils/transactionFailure.d.ts.map +1 -0
  116. package/dist/widget/widget.d.ts +44 -3
  117. package/dist/widget/widget.d.ts.map +1 -1
  118. package/dist/widget/workers/intentExecutionWorker.d.ts.map +1 -1
  119. package/package.json +22 -22
  120. package/src/analytics.ts +115 -79
  121. package/src/chains.ts +0 -1
  122. package/src/error.ts +11 -0
  123. package/src/estimate.ts +12 -7
  124. package/src/fees.ts +0 -1
  125. package/src/index.ts +11 -0
  126. package/src/localeUtils.ts +3 -1
  127. package/src/meld/components/MeldCountriesList.tsx +30 -15
  128. package/src/meld/components/MeldFundMethods.tsx +8 -4
  129. package/src/meld/components/MeldTokensList.tsx +90 -2
  130. package/src/meld/utils/meld.ts +3 -400
  131. package/src/poolUtils.ts +5 -19
  132. package/src/prepareSend.ts +32 -5
  133. package/src/prices.ts +7 -33
  134. package/src/query/balance.fetchers.ts +128 -168
  135. package/src/query/fiat.fetchers.ts +33 -0
  136. package/src/query/fiat.hooks.ts +71 -0
  137. package/src/query/fiat.queries.ts +67 -0
  138. package/src/query/meld.fetchers.ts +97 -0
  139. package/src/query/meld.hooks.ts +18 -0
  140. package/src/query/meld.queries.ts +184 -0
  141. package/src/recover.ts +6 -1
  142. package/src/tokens.ts +31 -6
  143. package/src/transactionIntent/deposits/depositOrchestrator.ts +2 -0
  144. package/src/transactionIntent/deposits/gaslessDeposit.ts +9 -2
  145. package/src/transactionIntent/deposits/standardDeposit.ts +35 -14
  146. package/src/transactionIntent/handlers/intentHandler.ts +134 -138
  147. package/src/transactionIntent/quote/normalizeQuote.ts +31 -22
  148. package/src/transactionIntent/quote/quoteHelpers.ts +24 -7
  149. package/src/transactionIntent/types.ts +2 -0
  150. package/src/transactionIntent/utils/balanceChecker.ts +10 -4
  151. package/src/transactions.ts +22 -13
  152. package/src/umd.tsx +1 -1
  153. package/src/utils/fiat.ts +32 -0
  154. package/src/utils/format.ts +1 -3
  155. package/src/utils/passthrough.ts +19 -3
  156. package/src/utils/validation.ts +88 -0
  157. package/src/utils.ts +2 -1
  158. package/src/walletUtils.ts +2 -2
  159. package/src/widget/components/AccountIntentTransactionHistory.tsx +2 -2
  160. package/src/widget/components/ClassicSwap.tsx +10 -4
  161. package/src/widget/components/DepositTracker.tsx +2 -5
  162. package/src/widget/components/Earn.tsx +6 -0
  163. package/src/widget/components/FeeOption.tsx +15 -8
  164. package/src/widget/components/Fund.tsx +16 -11
  165. package/src/widget/components/FundMethods.tsx +255 -192
  166. package/src/widget/components/HookModalContent.tsx +4 -0
  167. package/src/widget/components/MeldForm.tsx +44 -42
  168. package/src/widget/components/MeldHistory.tsx +4 -3
  169. package/src/widget/components/MeldStepsFlow.tsx +33 -71
  170. package/src/widget/components/OFTProgressBar.tsx +32 -12
  171. package/src/widget/components/OnRampProviderSelector.tsx +2 -1
  172. package/src/widget/components/OnrampHistoryRow.tsx +2 -1
  173. package/src/widget/components/Pay.tsx +8 -2
  174. package/src/widget/components/PercentageMaxButtons.tsx +5 -3
  175. package/src/widget/components/PoolDeposit.tsx +6 -0
  176. package/src/widget/components/PoolWithdraw.tsx +1 -1
  177. package/src/widget/components/QuoteDetails.tsx +5 -4
  178. package/src/widget/components/Receipt.tsx +4 -3
  179. package/src/widget/components/SlippageToleranceSettings.tsx +3 -2
  180. package/src/widget/components/Swap.tsx +2 -0
  181. package/src/widget/components/TransferPendingVertical.tsx +21 -28
  182. package/src/widget/components/UserPreferences.tsx +1 -1
  183. package/src/widget/components/Withdraw.tsx +20 -14
  184. package/src/widget/hooks/useAmountUsd.ts +3 -15
  185. package/src/widget/hooks/useCustomTokenSearch.tsx +2 -6
  186. package/src/widget/hooks/useDisplayCurrencyPreference.tsx +1 -2
  187. package/src/widget/hooks/useFiatOnRampCurrencies.ts +11 -76
  188. package/src/widget/hooks/useMeldTransactionHistory.ts +24 -89
  189. package/src/widget/hooks/useOnRampCountryDefaults.ts +3 -49
  190. package/src/widget/hooks/useOnRampPaymentMethods.ts +21 -100
  191. package/src/widget/hooks/useOnRampQuote.ts +2 -5
  192. package/src/widget/hooks/useQuote.ts +10 -12
  193. package/src/widget/hooks/useSendForm.ts +6 -0
  194. package/src/widget/hooks/useTokenList.ts +3 -6
  195. package/src/widget/hooks/useTokenWithFreshBalance.ts +141 -11
  196. package/src/widget/types/commonProps.ts +2 -0
  197. package/src/widget/utils/transactionFailure.ts +52 -0
  198. package/src/widget/widget.tsx +137 -59
  199. package/src/widget/workers/intentExecutionWorker.ts +3 -1
  200. package/dist/widget/hooks/useExchangeRate.d.ts +0 -31
  201. package/dist/widget/hooks/useExchangeRate.d.ts.map +0 -1
  202. package/dist/widget/hooks/useFiatCurrencyList.d.ts +0 -3
  203. package/dist/widget/hooks/useFiatCurrencyList.d.ts.map +0 -1
  204. package/src/widget/hooks/useExchangeRate.ts +0 -257
  205. package/src/widget/hooks/useFiatCurrencyList.ts +0 -66
@@ -5,26 +5,91 @@ import {
5
5
  WIDGET_REFRESH_INTERVAL,
6
6
  } from "../../query/balance.hooks.js"
7
7
  import type { Address } from "viem"
8
+ import { erc20Abi } from "viem"
9
+ import { useBalance, useReadContract } from "wagmi"
8
10
  import { logger } from "../../index.js"
11
+ import { isNativeToken, addressEqual } from "../../utils/address.js"
12
+ import { formatBalanceFields } from "../../utils/format.js"
9
13
 
10
- import { addressEqual } from "../../utils/address.js"
11
14
  /**
12
- * Enriches a token with fresh balance data from the cache/API
13
- * This prevents displaying stale balance data from localStorage or props
15
+ * Reads the on-chain balance for a single token (native or ERC20).
16
+ * This is the source of truth it bypasses the indexer entirely.
17
+ */
18
+ function useOnChainBalance(
19
+ token: Token | null | undefined,
20
+ accountAddress?: string,
21
+ ): { balance: string | undefined; isLoading: boolean } {
22
+ const isNative = isNativeToken(token?.contractAddress)
23
+ const enabled = !!token && !!accountAddress && !!token.chainId
24
+
25
+ // Native token balance (ETH, MATIC, etc.)
26
+ const { data: nativeData, isLoading: nativeLoading } = useBalance({
27
+ address: accountAddress as Address | undefined,
28
+ chainId: token?.chainId,
29
+ query: {
30
+ enabled: enabled && isNative,
31
+ refetchInterval: WIDGET_REFRESH_INTERVAL,
32
+ },
33
+ })
34
+
35
+ // ERC20 token balance
36
+ const { data: erc20Data, isLoading: erc20Loading } = useReadContract({
37
+ address: token?.contractAddress as Address | undefined,
38
+ abi: erc20Abi,
39
+ functionName: "balanceOf",
40
+ args: accountAddress ? [accountAddress as Address] : undefined,
41
+ chainId: token?.chainId,
42
+ query: {
43
+ enabled: enabled && !isNative,
44
+ refetchInterval: WIDGET_REFRESH_INTERVAL,
45
+ },
46
+ })
47
+
48
+ return useMemo(() => {
49
+ if (!enabled) return { balance: undefined, isLoading: false }
50
+
51
+ if (isNative) {
52
+ return {
53
+ balance: nativeData?.value?.toString(),
54
+ isLoading: nativeLoading,
55
+ }
56
+ }
57
+
58
+ return {
59
+ balance: erc20Data?.toString(),
60
+ isLoading: erc20Loading,
61
+ }
62
+ }, [
63
+ enabled,
64
+ isNative,
65
+ nativeData?.value,
66
+ nativeLoading,
67
+ erc20Data,
68
+ erc20Loading,
69
+ ])
70
+ }
71
+
72
+ /**
73
+ * Enriches a token with fresh balance data from the cache/API,
74
+ * with an on-chain balance read as source of truth fallback.
75
+ * This prevents displaying stale balance data from localStorage or props.
14
76
  */
15
77
  export function useTokenWithFreshBalance(
16
78
  token: Token | null | undefined,
17
79
  accountAddress?: string,
18
80
  disabled?: boolean,
19
81
  ): { token: Token | null | undefined; isLoadingBalance: boolean } {
20
- // Fetch fresh balance data for the account
21
- // We are already filtering the token here, we don't need to pass it to the API, it will collide with useTokenBalances because they share the same query key.
82
+ // Fetch fresh balance data for the account (indexer-backed, includes USD prices)
22
83
  const { sortedTokens, isLoadingBalances } = useTokenBalances(
23
84
  accountAddress as Address | null,
24
85
  undefined,
25
86
  { disabled: disabled, refetchInterval: WIDGET_REFRESH_INTERVAL },
26
87
  )
27
88
 
89
+ // On-chain balance as source of truth for the selected token
90
+ const { balance: onChainBalance, isLoading: isLoadingOnChain } =
91
+ useOnChainBalance(disabled ? null : token, accountAddress)
92
+
28
93
  logger.console.log(
29
94
  "[trails-sdk] sortedTokens FROM useTokenWithFreshBalance",
30
95
  sortedTokens,
@@ -35,7 +100,7 @@ export function useTokenWithFreshBalance(
35
100
  if (!token) return token
36
101
  if (!sortedTokens) return token // Return original if no fresh data available
37
102
 
38
- // Find matching token in balance data
103
+ // Find matching token in indexer balance data
39
104
  const freshToken = sortedTokens.find(
40
105
  (t: Token) =>
41
106
  addressEqual(t.contractAddress, token.contractAddress) &&
@@ -43,6 +108,28 @@ export function useTokenWithFreshBalance(
43
108
  )
44
109
 
45
110
  if (freshToken) {
111
+ // If on-chain balance is lower than indexer balance, the indexer is stale —
112
+ // use the on-chain value as source of truth for the balance fields.
113
+ if (onChainBalance !== undefined && freshToken.balance) {
114
+ try {
115
+ const onChain = BigInt(onChainBalance)
116
+ const indexed = BigInt(freshToken.balance)
117
+ if (onChain < indexed) {
118
+ const fields = formatBalanceFields(onChainBalance, token.decimals)
119
+ return {
120
+ ...token,
121
+ ...fields,
122
+ balanceUsd: freshToken.balanceUsd,
123
+ balanceUsdFormatted: freshToken.balanceUsdFormatted,
124
+ isSufficientBalance: freshToken.isSufficientBalance,
125
+ priceUsd: freshToken.priceUsd,
126
+ }
127
+ }
128
+ } catch {
129
+ // Fall through to indexer balance
130
+ }
131
+ }
132
+
46
133
  // Merge token metadata with fresh balance data
47
134
  return {
48
135
  ...token,
@@ -57,11 +144,41 @@ export function useTokenWithFreshBalance(
57
144
  }
58
145
  }
59
146
 
60
- // Return original token if no fresh data found
147
+ // Token not found in indexer data — use on-chain balance as source of truth
148
+ if (onChainBalance !== undefined) {
149
+ const fields = formatBalanceFields(onChainBalance, token.decimals)
150
+ return {
151
+ ...token,
152
+ ...fields,
153
+ balanceUsd: 0,
154
+ balanceUsdFormatted: "$0.00",
155
+ isSufficientBalance: false,
156
+ priceUsd: token.priceUsd,
157
+ }
158
+ }
159
+
160
+ // Token not found and no on-chain data yet — zero out if indexer has loaded
161
+ if (sortedTokens.length > 0 || !isLoadingBalances) {
162
+ return {
163
+ ...token,
164
+ balance: "0",
165
+ balanceDisplay: "0",
166
+ balanceLocaleDisplay: "0",
167
+ balanceFormatted: "0",
168
+ balanceUsd: 0,
169
+ balanceUsdFormatted: "$0.00",
170
+ isSufficientBalance: false,
171
+ }
172
+ }
173
+
174
+ // Return original token while still loading
61
175
  return token
62
- }, [token, sortedTokens])
176
+ }, [token, sortedTokens, isLoadingBalances, onChainBalance])
63
177
 
64
- return { token: tokenWithFreshBalance, isLoadingBalance: isLoadingBalances }
178
+ return {
179
+ token: tokenWithFreshBalance,
180
+ isLoadingBalance: isLoadingBalances || isLoadingOnChain,
181
+ }
65
182
  }
66
183
 
67
184
  /**
@@ -107,10 +224,23 @@ export function useTokensWithFreshBalance(
107
224
  }
108
225
  }
109
226
 
110
- // Return original token if no fresh data found
227
+ // Token not found balance is likely zero (filtered out by useTokenBalances)
228
+ if (sortedTokens.length > 0 || !isLoadingBalances) {
229
+ return {
230
+ ...token,
231
+ balance: "0",
232
+ balanceDisplay: "0",
233
+ balanceLocaleDisplay: "0",
234
+ balanceFormatted: "0",
235
+ balanceUsd: 0,
236
+ balanceUsdFormatted: "$0.00",
237
+ isSufficientBalance: false,
238
+ }
239
+ }
240
+
111
241
  return token
112
242
  })
113
- }, [tokens, sortedTokens])
243
+ }, [tokens, sortedTokens, isLoadingBalances])
114
244
 
115
245
  return { tokens: tokensWithFreshBalance, isLoadingBalance: isLoadingBalances }
116
246
  }
@@ -45,6 +45,8 @@ export interface BaseProps {
45
45
  toCalldata?: string
46
46
  swapProvider?: string
47
47
  bridgeProvider?: string
48
+ swapProviderFallback?: boolean
49
+ bridgeProviderFallback?: boolean
48
50
  fundMethod?: FundMethod
49
51
  checkoutOnHandlers?: CheckoutOnHandlers
50
52
  selectedToken: Token | null
@@ -0,0 +1,52 @@
1
+ import type { TransactionState } from "../../transactions.js"
2
+
3
+ export const hasFailedTransaction = (tx: TransactionState): boolean => {
4
+ const hasFailedState = tx.state === "failed"
5
+ const hasFailureEvent = tx.decodedGuestModuleEvents?.some(
6
+ (event) => event.type === "CallFailed",
7
+ )
8
+ return hasFailedState || Boolean(hasFailureEvent)
9
+ }
10
+
11
+ export const hasAbortedTransaction = (tx: TransactionState): boolean => {
12
+ const hasAbortedState = tx.state === "aborted"
13
+ const hasAbortedEvent = tx.decodedGuestModuleEvents?.some(
14
+ (event) => event.type === "CallAborted",
15
+ )
16
+ return hasAbortedState || Boolean(hasAbortedEvent)
17
+ }
18
+
19
+ /**
20
+ * Returns true when a transaction should be treated as failed in widget UI.
21
+ *
22
+ * A transaction is considered failed if:
23
+ * - its lifecycle state is `failed` or `aborted`, or
24
+ * - decoded guest module events include `CallFailed` or `CallAborted`.
25
+ */
26
+ export function hasFailedOrAbortedTransaction(
27
+ tx?: TransactionState | null,
28
+ ): boolean {
29
+ if (!tx) return false
30
+
31
+ const hasFailedState = tx.state === "failed" || tx.state === "aborted"
32
+ const hasFailureEvent = tx.decodedGuestModuleEvents?.some(
33
+ (event) => event.type === "CallFailed" || event.type === "CallAborted",
34
+ )
35
+
36
+ return hasFailedState || Boolean(hasFailureEvent)
37
+ }
38
+
39
+ /**
40
+ * Returns true when any transaction in the list is failed or aborted.
41
+ *
42
+ * This is used for screen-level decisions (for example, showing failure CTAs
43
+ * or rendering the receipt in failure state) so pending/receipt logic stays
44
+ * consistent across components.
45
+ */
46
+ export function hasAnyFailedOrAbortedTransactions(
47
+ transactionStates?: TransactionState[] | null,
48
+ ): boolean {
49
+ return Boolean(
50
+ transactionStates?.some((tx) => hasFailedOrAbortedTransaction(tx)),
51
+ )
52
+ }