@b3dotfun/sdk 0.1.70-alpha.17 → 0.1.70-alpha.19

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 (162) hide show
  1. package/README.md +3 -3
  2. package/dist/cjs/anyspend/constants/rpc.d.ts +1 -0
  3. package/dist/cjs/anyspend/constants/rpc.js +3 -1
  4. package/dist/cjs/anyspend/react/components/AnySpendDeposit.d.ts +1 -3
  5. package/dist/cjs/anyspend/react/components/AnySpendDeposit.js +14 -12
  6. package/dist/cjs/anyspend/react/components/AnySpendStakeB3.js +32 -13
  7. package/dist/cjs/anyspend/react/components/AnySpendStakeB3ExactIn.js +32 -10
  8. package/dist/cjs/anyspend/react/components/checkout/CryptoPayPanel.js +16 -8
  9. package/dist/cjs/anyspend/react/components/common/TokenBalance.js +4 -5
  10. package/dist/cjs/anyspend/react/hooks/useSigMint.d.ts +1 -1
  11. package/dist/cjs/anyspend/utils/chain.d.ts +45 -0
  12. package/dist/cjs/anyspend/utils/chain.js +51 -2
  13. package/dist/cjs/global-account/react/components/B3Provider/B3Provider.d.ts +1 -2
  14. package/dist/cjs/global-account/react/components/B3Provider/B3Provider.js +2 -2
  15. package/dist/cjs/global-account/react/components/B3Provider/RelayKitProviderWrapper.d.ts +1 -2
  16. package/dist/cjs/global-account/react/components/B3Provider/RelayKitProviderWrapper.js +4 -5
  17. package/dist/cjs/global-account/react/components/ManageAccount/ContentTokens.js +23 -15
  18. package/dist/cjs/global-account/react/components/ManageAccount/NFTContent.js +2 -2
  19. package/dist/cjs/global-account/react/components/ManageAccount/ProfileSection.js +8 -6
  20. package/dist/cjs/global-account/react/components/ManageAccount/TokenContent.js +17 -8
  21. package/dist/cjs/global-account/react/components/Send/Send.js +15 -11
  22. package/dist/cjs/global-account/react/hooks/index.d.ts +2 -2
  23. package/dist/cjs/global-account/react/hooks/index.js +7 -8
  24. package/dist/cjs/global-account/react/hooks/useAuth.d.ts +2 -1
  25. package/dist/cjs/global-account/react/hooks/useAuth.js +1 -0
  26. package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +2 -1
  27. package/dist/cjs/global-account/react/hooks/useAuthentication.js +1 -0
  28. package/dist/cjs/global-account/react/hooks/useFirstEOA.d.ts +4 -7666
  29. package/dist/cjs/global-account/react/hooks/useFirstEOA.js +1 -0
  30. package/dist/cjs/global-account/react/hooks/usePortfolioBalance.d.ts +15 -0
  31. package/dist/cjs/global-account/react/hooks/usePortfolioBalance.js +76 -0
  32. package/dist/cjs/global-account/react/hooks/usePortfolioCollectibles.d.ts +12 -0
  33. package/dist/cjs/global-account/react/hooks/usePortfolioCollectibles.js +178 -0
  34. package/dist/cjs/global-account/react/hooks/useUnifiedChainSwitchAndExecute.d.ts +1 -0
  35. package/dist/cjs/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +18 -11
  36. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +0 -2
  37. package/dist/cjs/global-account/react/utils/createWagmiConfig.d.ts +2 -2
  38. package/dist/cjs/shared/constants/chains/b3Chain.d.ts +24 -0
  39. package/dist/cjs/shared/constants/chains/supported.d.ts +4 -4
  40. package/dist/cjs/shared/constants/index.d.ts +4 -0
  41. package/dist/cjs/shared/utils/zerionPositions.d.ts +96 -0
  42. package/dist/cjs/shared/utils/zerionPositions.js +186 -0
  43. package/dist/cjs/wallet/utils/createWalletConfig.d.ts +2 -2
  44. package/dist/esm/anyspend/constants/rpc.d.ts +1 -0
  45. package/dist/esm/anyspend/constants/rpc.js +2 -0
  46. package/dist/esm/anyspend/react/components/AnySpendDeposit.d.ts +1 -3
  47. package/dist/esm/anyspend/react/components/AnySpendDeposit.js +15 -13
  48. package/dist/esm/anyspend/react/components/AnySpendStakeB3.js +33 -14
  49. package/dist/esm/anyspend/react/components/AnySpendStakeB3ExactIn.js +33 -11
  50. package/dist/esm/anyspend/react/components/checkout/CryptoPayPanel.js +18 -10
  51. package/dist/esm/anyspend/react/components/common/TokenBalance.js +6 -7
  52. package/dist/esm/anyspend/react/hooks/useSigMint.d.ts +1 -1
  53. package/dist/esm/anyspend/utils/chain.d.ts +45 -0
  54. package/dist/esm/anyspend/utils/chain.js +50 -3
  55. package/dist/esm/global-account/react/components/B3Provider/B3Provider.d.ts +1 -2
  56. package/dist/esm/global-account/react/components/B3Provider/B3Provider.js +2 -2
  57. package/dist/esm/global-account/react/components/B3Provider/RelayKitProviderWrapper.d.ts +1 -2
  58. package/dist/esm/global-account/react/components/B3Provider/RelayKitProviderWrapper.js +4 -5
  59. package/dist/esm/global-account/react/components/ManageAccount/ContentTokens.js +24 -16
  60. package/dist/esm/global-account/react/components/ManageAccount/NFTContent.js +3 -3
  61. package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.js +9 -7
  62. package/dist/esm/global-account/react/components/ManageAccount/TokenContent.js +18 -9
  63. package/dist/esm/global-account/react/components/Send/Send.js +16 -12
  64. package/dist/esm/global-account/react/hooks/index.d.ts +2 -2
  65. package/dist/esm/global-account/react/hooks/index.js +2 -2
  66. package/dist/esm/global-account/react/hooks/useAuth.d.ts +2 -1
  67. package/dist/esm/global-account/react/hooks/useAuth.js +1 -0
  68. package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +2 -1
  69. package/dist/esm/global-account/react/hooks/useAuthentication.js +1 -0
  70. package/dist/esm/global-account/react/hooks/useFirstEOA.d.ts +4 -7666
  71. package/dist/esm/global-account/react/hooks/useFirstEOA.js +1 -0
  72. package/dist/esm/global-account/react/hooks/usePortfolioBalance.d.ts +15 -0
  73. package/dist/esm/global-account/react/hooks/usePortfolioBalance.js +72 -0
  74. package/dist/esm/global-account/react/hooks/usePortfolioCollectibles.d.ts +12 -0
  75. package/dist/esm/global-account/react/hooks/usePortfolioCollectibles.js +175 -0
  76. package/dist/esm/global-account/react/hooks/useUnifiedChainSwitchAndExecute.d.ts +1 -0
  77. package/dist/esm/global-account/react/hooks/useUnifiedChainSwitchAndExecute.js +18 -11
  78. package/dist/esm/global-account/react/stores/useModalStore.d.ts +0 -2
  79. package/dist/esm/global-account/react/utils/createWagmiConfig.d.ts +2 -2
  80. package/dist/esm/shared/constants/chains/b3Chain.d.ts +24 -0
  81. package/dist/esm/shared/constants/chains/supported.d.ts +4 -4
  82. package/dist/esm/shared/constants/index.d.ts +4 -0
  83. package/dist/esm/shared/utils/zerionPositions.d.ts +96 -0
  84. package/dist/esm/shared/utils/zerionPositions.js +178 -0
  85. package/dist/esm/wallet/utils/createWalletConfig.d.ts +2 -2
  86. package/dist/types/anyspend/constants/rpc.d.ts +1 -0
  87. package/dist/types/anyspend/react/components/AnySpendDeposit.d.ts +1 -3
  88. package/dist/types/anyspend/react/hooks/useSigMint.d.ts +1 -1
  89. package/dist/types/anyspend/utils/chain.d.ts +45 -0
  90. package/dist/types/global-account/react/components/B3Provider/B3Provider.d.ts +1 -2
  91. package/dist/types/global-account/react/components/B3Provider/RelayKitProviderWrapper.d.ts +1 -2
  92. package/dist/types/global-account/react/hooks/index.d.ts +2 -2
  93. package/dist/types/global-account/react/hooks/useAuth.d.ts +2 -1
  94. package/dist/types/global-account/react/hooks/useAuthentication.d.ts +2 -1
  95. package/dist/types/global-account/react/hooks/useFirstEOA.d.ts +4 -7666
  96. package/dist/types/global-account/react/hooks/usePortfolioBalance.d.ts +15 -0
  97. package/dist/types/global-account/react/hooks/usePortfolioCollectibles.d.ts +12 -0
  98. package/dist/types/global-account/react/hooks/useUnifiedChainSwitchAndExecute.d.ts +1 -0
  99. package/dist/types/global-account/react/stores/useModalStore.d.ts +0 -2
  100. package/dist/types/global-account/react/utils/createWagmiConfig.d.ts +2 -2
  101. package/dist/types/shared/constants/chains/b3Chain.d.ts +24 -0
  102. package/dist/types/shared/constants/chains/supported.d.ts +4 -4
  103. package/dist/types/shared/constants/index.d.ts +4 -0
  104. package/dist/types/shared/utils/zerionPositions.d.ts +96 -0
  105. package/dist/types/wallet/utils/createWalletConfig.d.ts +2 -2
  106. package/node_modules/@b3/chain-registry/chains.json +29 -1
  107. package/node_modules/@b3/chain-registry/package.json +18 -2
  108. package/package.json +2 -2
  109. package/src/anyspend/README.md +2 -2
  110. package/src/anyspend/constants/rpc.ts +2 -0
  111. package/src/anyspend/docs/components.md +6 -6
  112. package/src/anyspend/docs/contributing.md +1 -1
  113. package/src/anyspend/docs/hooks.md +3 -3
  114. package/src/anyspend/docs/installation.md +2 -2
  115. package/src/anyspend/react/components/AnySpendDeposit.tsx +26 -19
  116. package/src/anyspend/react/components/AnySpendStakeB3.tsx +55 -14
  117. package/src/anyspend/react/components/AnySpendStakeB3ExactIn.tsx +55 -11
  118. package/src/anyspend/react/components/__tests__/QRDeposit.test.tsx +3 -4
  119. package/src/anyspend/react/components/checkout/CryptoPayPanel.tsx +25 -12
  120. package/src/anyspend/react/components/common/TokenBalance.tsx +10 -7
  121. package/src/anyspend/utils/__tests__/deprecatedChains.test.ts +44 -0
  122. package/src/anyspend/utils/__tests__/toRegistryChainId.test.ts +17 -0
  123. package/src/anyspend/utils/chain.ts +53 -2
  124. package/src/global-account/react/components/B3Provider/B3Provider.tsx +1 -3
  125. package/src/global-account/react/components/B3Provider/RelayKitProviderWrapper.tsx +4 -11
  126. package/src/global-account/react/components/ManageAccount/ContentTokens.tsx +50 -21
  127. package/src/global-account/react/components/ManageAccount/NFTContent.tsx +5 -3
  128. package/src/global-account/react/components/ManageAccount/ProfileSection.tsx +10 -6
  129. package/src/global-account/react/components/ManageAccount/TokenContent.tsx +22 -9
  130. package/src/global-account/react/components/Send/Send.tsx +39 -15
  131. package/src/global-account/react/hooks/__tests__/useUnifiedChainSwitchAndExecute.test.tsx +156 -0
  132. package/src/global-account/react/hooks/index.ts +2 -2
  133. package/src/global-account/react/hooks/useAuth.ts +3 -2
  134. package/src/global-account/react/hooks/useAuthentication.ts +3 -2
  135. package/src/global-account/react/hooks/useFirstEOA.tsx +9 -2
  136. package/src/global-account/react/hooks/usePortfolioBalance.ts +89 -0
  137. package/src/global-account/react/hooks/usePortfolioCollectibles.ts +212 -0
  138. package/src/global-account/react/hooks/useUnifiedChainSwitchAndExecute.ts +26 -12
  139. package/src/global-account/react/stores/useModalStore.ts +0 -2
  140. package/src/shared/constants/index.ts +8 -0
  141. package/src/shared/utils/__tests__/zerionPositions.test.ts +226 -0
  142. package/src/shared/utils/zerionPositions.ts +244 -0
  143. package/dist/cjs/global-account/react/hooks/useSimBalance.d.ts +0 -59
  144. package/dist/cjs/global-account/react/hooks/useSimBalance.js +0 -98
  145. package/dist/cjs/global-account/react/hooks/useSimCollectibles.d.ts +0 -45
  146. package/dist/cjs/global-account/react/hooks/useSimCollectibles.js +0 -190
  147. package/dist/cjs/global-account/react/utils/simdune.d.ts +0 -7
  148. package/dist/cjs/global-account/react/utils/simdune.js +0 -21
  149. package/dist/esm/global-account/react/hooks/useSimBalance.d.ts +0 -59
  150. package/dist/esm/global-account/react/hooks/useSimBalance.js +0 -93
  151. package/dist/esm/global-account/react/hooks/useSimCollectibles.d.ts +0 -45
  152. package/dist/esm/global-account/react/hooks/useSimCollectibles.js +0 -187
  153. package/dist/esm/global-account/react/utils/simdune.d.ts +0 -7
  154. package/dist/esm/global-account/react/utils/simdune.js +0 -17
  155. package/dist/types/global-account/react/hooks/useSimBalance.d.ts +0 -59
  156. package/dist/types/global-account/react/hooks/useSimCollectibles.d.ts +0 -45
  157. package/dist/types/global-account/react/utils/simdune.d.ts +0 -7
  158. package/node_modules/@b3/chain-registry/src/index.ts +0 -169
  159. package/node_modules/@b3/chain-registry/tsconfig.json +0 -16
  160. package/src/global-account/react/hooks/useSimBalance.ts +0 -164
  161. package/src/global-account/react/hooks/useSimCollectibles.ts +0 -238
  162. package/src/global-account/react/utils/simdune.ts +0 -20
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ /**
3
+ * Canonical browser-side translation of Zerion wallet positions into B3 balance rows.
4
+ *
5
+ * This lives in the published SDK because every browser consumer needs it — the apps in this repo
6
+ * and the sites that install `@b3dotfun/sdk` from npm — and the in-repo Zerion client is a private
7
+ * workspace package that a published package cannot depend on. Backend services keep their own
8
+ * translation: pulling a browser SDK into a Node service to share one function is a worse trade
9
+ * than the second copy.
10
+ *
11
+ * Only the HTTP layer differs between callers. The rules below — which row counts as the chain's
12
+ * native coin, and which rows have no usable identity — must not be re-derived anywhere else.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SOLANA_CHAIN_ID = exports.SOLANA_ZERION_SLUG = exports.SOLANA_NATIVE_TOKEN_ADDRESS = exports.EVM_NATIVE_TOKEN_ADDRESS = void 0;
16
+ exports.zerionSlugToChainId = zerionSlugToChainId;
17
+ exports.chainIdToZerionSlug = chainIdToZerionSlug;
18
+ exports.nativeTokenAddressForChain = nativeTokenAddressForChain;
19
+ exports.balanceMatchesToken = balanceMatchesToken;
20
+ exports.adaptZerionPositions = adaptZerionPositions;
21
+ /** Address B3 uses for a chain's native coin on EVM. Matches NATIVE_TOKEN_ADDRESSES. */
22
+ exports.EVM_NATIVE_TOKEN_ADDRESS = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
23
+ exports.SOLANA_NATIVE_TOKEN_ADDRESS = "So11111111111111111111111111111111111111112";
24
+ exports.SOLANA_ZERION_SLUG = "solana";
25
+ /** What Zerion's `quantity.int` is: a base-10 integer, unbounded in length. */
26
+ const DECIMAL_INTEGER_PATTERN = /^\d+$/;
27
+ /** Solana's chain id as the chain registry defines it — the id every balance row is keyed by. */
28
+ exports.SOLANA_CHAIN_ID = 7565164;
29
+ /**
30
+ * Zerion chain slug → numeric chain id. Slugs are Zerion's own vocabulary and do not always match
31
+ * the chain's common name — Gnosis is `xdai`, Metis is `metis-andromeda` — so an entry invented
32
+ * from the chain name silently drops every position on that chain, because the adapter skips any
33
+ * slug it cannot map. Every entry here is taken from Zerion's `GET /v1/chains/`.
34
+ */
35
+ const ZERION_CHAIN_IDS = {
36
+ ethereum: 1,
37
+ optimism: 10,
38
+ "binance-smart-chain": 56,
39
+ xdai: 100,
40
+ unichain: 130,
41
+ polygon: 137,
42
+ sonic: 146,
43
+ fantom: 250,
44
+ "zksync-era": 324,
45
+ world: 480,
46
+ hyperevm: 999,
47
+ "metis-andromeda": 1088,
48
+ "polygon-zkevm": 1101,
49
+ soneium: 1868,
50
+ ronin: 2020,
51
+ abstract: 2741,
52
+ robinhood: 4663,
53
+ mantle: 5000,
54
+ base: 8453,
55
+ mode: 34443,
56
+ arbitrum: 42161,
57
+ celo: 42220,
58
+ avalanche: 43114,
59
+ linea: 59144,
60
+ berachain: 80094,
61
+ blast: 81457,
62
+ taiko: 167000,
63
+ scroll: 534352,
64
+ zora: 7777777,
65
+ degen: 666666666,
66
+ aurora: 1313161554,
67
+ [exports.SOLANA_ZERION_SLUG]: exports.SOLANA_CHAIN_ID,
68
+ };
69
+ // Maps rather than objects: a slug arrives straight off the wire, and an object lookup resolves an
70
+ // inherited key like `constructor` or `toString` to something truthy instead of reporting the chain
71
+ // as unknown — which would carry a row past the `=== undefined` guard with a function as its id.
72
+ const CHAIN_ID_BY_ZERION_SLUG = new Map(Object.entries(ZERION_CHAIN_IDS));
73
+ const ZERION_SLUG_BY_CHAIN_ID = new Map(Object.entries(ZERION_CHAIN_IDS).map(([slug, id]) => [id, slug]));
74
+ function zerionSlugToChainId(slug) {
75
+ return CHAIN_ID_BY_ZERION_SLUG.get(slug);
76
+ }
77
+ function chainIdToZerionSlug(chainId) {
78
+ return ZERION_SLUG_BY_CHAIN_ID.get(chainId);
79
+ }
80
+ /**
81
+ * Zerion encodes a position's asset in the first hyphen-delimited segment of its resource `id`
82
+ * (`<assetRef>-<chainSlug>-<positionType>-…`). A chain's native coin uses the literal `base`
83
+ * (`base-polygon-asset-asset`); ERC-20s use the contract address
84
+ * (`0x1dd17…-ethereum-asset-asset`).
85
+ *
86
+ * This is the only reliable native signal. A falsy per-chain `implementation.address` looks
87
+ * equivalent and is not: Zerion reports Polygon's native coin at its `0x…1010` system contract and
88
+ * Celo's at a real ERC-20 address, while Polygon simultaneously carries a non-native fungible with
89
+ * the same `POL` symbol. Keying on the address shape misclassifies both.
90
+ */
91
+ function isNativePosition(id, impl) {
92
+ if (id?.startsWith("base-") === true)
93
+ return true;
94
+ // Second signal, for the chains that spell native as an absent address. It requires the
95
+ // implementation to EXIST: a fungible with no entry for this chain tells us nothing about what
96
+ // the row is, and calling that native would hand a value-bearing send an amount we cannot name.
97
+ return impl !== undefined && !impl.address;
98
+ }
99
+ function nativeTokenAddressForChain(slug) {
100
+ return slug === exports.SOLANA_ZERION_SLUG ? exports.SOLANA_NATIVE_TOKEN_ADDRESS : exports.EVM_NATIVE_TOKEN_ADDRESS;
101
+ }
102
+ /**
103
+ * Every spelling of "the chain's own coin" a caller might hold.
104
+ *
105
+ * A token record's address depends on where it came from — a quote provider, a token list, a URL
106
+ * parameter — and each source names the native coin differently. Matching a balance row against any
107
+ * single one of these silently fails for the others.
108
+ */
109
+ const NATIVE_TOKEN_MARKERS = new Set([
110
+ "native",
111
+ "0x0000000000000000000000000000000000000000",
112
+ exports.EVM_NATIVE_TOKEN_ADDRESS,
113
+ exports.SOLANA_NATIVE_TOKEN_ADDRESS.toLowerCase(),
114
+ "11111111111111111111111111111111",
115
+ ]);
116
+ /**
117
+ * Whether `balance` is the token identified by `tokenAddress`.
118
+ *
119
+ * Native coins are matched on the row's `is_native` flag rather than its printed address, because
120
+ * the caller and the adapter rarely spell the native coin the same way.
121
+ */
122
+ function balanceMatchesToken(balance, tokenAddress) {
123
+ const wanted = tokenAddress.toLowerCase();
124
+ if (NATIVE_TOKEN_MARKERS.has(wanted))
125
+ return balance.is_native;
126
+ // Case-insensitive only for hex contract addresses. Solana mints are base58, where case is
127
+ // significant — lowercasing them would let two distinct mints compare equal.
128
+ if (balance.chain === exports.SOLANA_ZERION_SLUG)
129
+ return balance.address === tokenAddress;
130
+ return balance.address.toLowerCase() === wanted;
131
+ }
132
+ /**
133
+ * Adapt raw Zerion positions into balance rows, dropping any row we cannot describe honestly.
134
+ *
135
+ * A row is dropped when its chain slug is absent or unmapped, or when neither the quantity nor the
136
+ * chain implementation carries decimals — an amount rendered against a guessed scale is a wrong
137
+ * number presented as a real one, which is worse for the reader than a missing row.
138
+ *
139
+ * @param slugToChainId overrides slug resolution; return undefined to skip a chain entirely.
140
+ */
141
+ function adaptZerionPositions(positions, slugToChainId = zerionSlugToChainId) {
142
+ const out = [];
143
+ for (const position of positions) {
144
+ const attributes = position.attributes ?? {};
145
+ const flags = attributes.flags;
146
+ if (flags && (flags.displayable === false || flags.is_trash === true))
147
+ continue;
148
+ const slug = position.relationships?.chain?.data?.id;
149
+ if (!slug)
150
+ continue;
151
+ const chainId = slugToChainId(slug);
152
+ if (chainId === undefined)
153
+ continue;
154
+ const fungibleInfo = attributes.fungible_info ?? {};
155
+ const implementation = (fungibleInfo.implementations ?? []).find(entry => entry.chain_id === slug);
156
+ const decimals = attributes.quantity?.decimals ?? implementation?.decimals;
157
+ if (decimals === undefined)
158
+ continue;
159
+ const isNative = isNativePosition(position.id, implementation);
160
+ // A non-native row with no contract address has no on-chain identity — drop it rather than
161
+ // render a holding nothing can name.
162
+ const address = isNative ? nativeTokenAddressForChain(slug) : implementation?.address;
163
+ if (!address)
164
+ continue;
165
+ // A quantity we cannot read is not a zero balance. Must be the base-10 integer `quantity.int`
166
+ // is: `null`, `""`, and any other unusable string all format to NaN, which callers coerce back
167
+ // to 0 and show as "holds none". `"0"` matches, so a real zero balance still comes through.
168
+ const amount = attributes.quantity?.int;
169
+ if (amount === undefined || !DECIMAL_INTEGER_PATTERN.test(amount))
170
+ continue;
171
+ out.push({
172
+ chain: slug,
173
+ chain_id: chainId,
174
+ address,
175
+ amount,
176
+ symbol: fungibleInfo.symbol ?? "",
177
+ decimals,
178
+ is_native: isNative,
179
+ name: fungibleInfo.name,
180
+ price_usd: attributes.price ?? undefined,
181
+ value_usd: attributes.value ?? undefined,
182
+ token_metadata: fungibleInfo.icon?.url ? { logo: fungibleInfo.icon.url } : undefined,
183
+ });
184
+ }
185
+ return out;
186
+ }
@@ -16,6 +16,6 @@ export interface CreateWalletConfigOptions {
16
16
  * Creates a generic wagmi config with sensible defaults.
17
17
  * No ecosystem wallet, no B3-specific concerns.
18
18
  */
19
- export declare function createWalletConfig(options?: CreateWalletConfigOptions): import("wagmi").Config<readonly [Chain, ...Chain[]], {
20
- [k: string]: import("viem").HttpTransport<undefined, false>;
19
+ export declare function createWalletConfig(options?: CreateWalletConfigOptions): import("wagmi").Config<readonly [any, ...any[]], {
20
+ [k: string]: any;
21
21
  }, CreateConnectorFn[]>;
@@ -17,6 +17,7 @@ export declare const B3_PUBLIC_RPC = "https://mainnet-rpc.b3.fun";
17
17
  export declare const ABSTRACT_PUBLIC_RPC = "https://api.mainnet.abs.xyz";
18
18
  export declare const HYPEREVM_PUBLIC_RPC = "https://rpc.hyperliquid.xyz/evm";
19
19
  export declare const PLUME_PUBLIC_RPC = "https://rpc.plume.org";
20
+ export declare const ROBINHOOD_PUBLIC_RPC = "https://rpc.mainnet.chain.robinhood.com";
20
21
  /**
21
22
  * Map of chain IDs to their default public RPC URLs.
22
23
  */
@@ -19,6 +19,7 @@ export const B3_PUBLIC_RPC = "https://mainnet-rpc.b3.fun";
19
19
  export const ABSTRACT_PUBLIC_RPC = "https://api.mainnet.abs.xyz";
20
20
  export const HYPEREVM_PUBLIC_RPC = "https://rpc.hyperliquid.xyz/evm";
21
21
  export const PLUME_PUBLIC_RPC = "https://rpc.plume.org";
22
+ export const ROBINHOOD_PUBLIC_RPC = "https://rpc.mainnet.chain.robinhood.com";
22
23
  /**
23
24
  * Map of chain IDs to their default public RPC URLs.
24
25
  */
@@ -34,4 +35,5 @@ export const PUBLIC_RPC_URLS = {
34
35
  2741: ABSTRACT_PUBLIC_RPC, // Abstract
35
36
  999: HYPEREVM_PUBLIC_RPC, // HyperEVM
36
37
  98866: PLUME_PUBLIC_RPC, // Plume Network
38
+ 4663: ROBINHOOD_PUBLIC_RPC, // Robinhood Chain
37
39
  };
@@ -84,8 +84,6 @@ export interface AnySpendDepositProps {
84
84
  showFiatOption?: boolean;
85
85
  /** Custom list of supported chains. If not provided, uses default chains */
86
86
  supportedChains?: ChainConfig[];
87
- /** Minimum pool size for filtering tokens (default: 1,000,000) */
88
- minPoolSize?: number;
89
87
  /** Custom title for chain selection step */
90
88
  chainSelectionTitle?: string;
91
89
  /** Custom description for chain selection step */
@@ -164,4 +162,4 @@ export interface AnySpendDepositProps {
164
162
  * onSuccess={(amount) => console.log(`Deposited ${amount}`)}
165
163
  * />
166
164
  */
167
- export declare function AnySpendDeposit({ loadOrder, mode, recipientAddress, paymentType: initialPaymentType, sourceTokenAddress, sourceTokenChainId: initialSourceChainId, destinationTokenAddress, destinationTokenChainId, lockDestinationToken, onSuccess, onOpenCustomModal, mainFooter, onTokenSelect, customUsdInputValues, preferEoa, minDestinationAmount, header, orderType, depositContractConfig, showChainSelection, showFiatOption, supportedChains, minPoolSize, topChainsCount, onClose, returnToHomeUrl, customRecipientLabel, returnHomeLabel, isCustomDeposit, classes, allowDirectTransfer, pureTransferOnly, destinationTokenAmount, callbackMetadata, senderAddress, slots, content, theme, }: AnySpendDepositProps): import("react/jsx-runtime").JSX.Element | null;
165
+ export declare function AnySpendDeposit({ loadOrder, mode, recipientAddress, paymentType: initialPaymentType, sourceTokenAddress, sourceTokenChainId: initialSourceChainId, destinationTokenAddress, destinationTokenChainId, lockDestinationToken, onSuccess, onOpenCustomModal, mainFooter, onTokenSelect, customUsdInputValues, preferEoa, minDestinationAmount, header, orderType, depositContractConfig, showChainSelection, showFiatOption, supportedChains, topChainsCount, onClose, returnToHomeUrl, customRecipientLabel, returnHomeLabel, isCustomDeposit, classes, allowDirectTransfer, pureTransferOnly, destinationTokenAmount, callbackMetadata, senderAddress, slots, content, theme, }: AnySpendDepositProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { USDC_BASE } from "../../../anyspend/index.js";
3
- import { Skeleton, useAccountWallet, useSimBalance, useTokenData } from "../../../global-account/react/index.js";
3
+ import { Skeleton, useAccountWallet, usePortfolioBalance, useTokenData } from "../../../global-account/react/index.js";
4
4
  import { cn } from "../../../shared/utils/cn.js";
5
5
  import { formatTokenAmount } from "../../../shared/utils/number.js";
6
6
  import { NetworkArbitrumOne, NetworkBase, NetworkBinanceSmartChain, NetworkEthereum, NetworkOptimism, NetworkPolygonPos, } from "@web3icons/react";
@@ -21,8 +21,6 @@ const DEFAULT_SUPPORTED_CHAINS = [
21
21
  { id: 137, name: "Polygon" },
22
22
  { id: 56, name: "BNB Chain" },
23
23
  ];
24
- // Minimum pool size to filter out low liquidity tokens
25
- const DEFAULT_MIN_POOL_SIZE = 1000000;
26
24
  /**
27
25
  * Self-described fallback metadata for the default funding token(s), keyed by
28
26
  * `${chainId}:${lowercased address}`. Used when `useTokenData` misses (returns
@@ -109,7 +107,7 @@ function ChainIcon({ chainId, className }) {
109
107
  * onSuccess={(amount) => console.log(`Deposited ${amount}`)}
110
108
  * />
111
109
  */
112
- export function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, paymentType: initialPaymentType, sourceTokenAddress, sourceTokenChainId: initialSourceChainId, destinationTokenAddress, destinationTokenChainId, lockDestinationToken = true, onSuccess, onOpenCustomModal, mainFooter, onTokenSelect, customUsdInputValues, preferEoa, minDestinationAmount, header, orderType, depositContractConfig, showChainSelection, showFiatOption = true, supportedChains = DEFAULT_SUPPORTED_CHAINS, minPoolSize = DEFAULT_MIN_POOL_SIZE, topChainsCount = 3, onClose, returnToHomeUrl, customRecipientLabel, returnHomeLabel, isCustomDeposit = false, classes, allowDirectTransfer = false, pureTransferOnly = false, destinationTokenAmount, callbackMetadata, senderAddress, slots, content, theme, }) {
110
+ export function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, paymentType: initialPaymentType, sourceTokenAddress, sourceTokenChainId: initialSourceChainId, destinationTokenAddress, destinationTokenChainId, lockDestinationToken = true, onSuccess, onOpenCustomModal, mainFooter, onTokenSelect, customUsdInputValues, preferEoa, minDestinationAmount, header, orderType, depositContractConfig, showChainSelection, showFiatOption = true, supportedChains = DEFAULT_SUPPORTED_CHAINS, topChainsCount = 3, onClose, returnToHomeUrl, customRecipientLabel, returnHomeLabel, isCustomDeposit = false, classes, allowDirectTransfer = false, pureTransferOnly = false, destinationTokenAmount, callbackMetadata, senderAddress, slots, content, theme, }) {
113
111
  // Extract deposit-specific classes for convenience
114
112
  const depositClasses = classes?.deposit;
115
113
  const { connectedEOAWallet } = useAccountWallet();
@@ -144,13 +142,17 @@ export function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, p
144
142
  }, [destinationTokenAddress, destinationTokenChainId, destinationTokenData]);
145
143
  // Fetch balances for EOA wallet (use senderAddress as fallback for pre-filled balance display)
146
144
  const effectiveBalanceAddress = senderAddress || eoaAddress;
147
- const { data: balanceData, isLoading: isBalanceLoading } = useSimBalance(shouldShowChainSelection ? effectiveBalanceAddress : undefined, supportedChains.map(c => c.id));
148
- // Group balances by chain and calculate total USD value per chain
145
+ const { data: balances, isLoading: isBalanceLoading, isError: isBalanceError, } = usePortfolioBalance(shouldShowChainSelection ? effectiveBalanceAddress : undefined, supportedChains.map(c => c.id));
146
+ // Group balances by chain and calculate total USD value per chain.
147
+ //
148
+ // There is deliberately no liquidity threshold here. The balance provider reports no pool size,
149
+ // so a `pool_size > n` test is false for every row and quietly reduces to "native only",
150
+ // erasing every ERC-20 the user holds from the chain summary. Spam filtering belongs to the
151
+ // token-filter service, which has the data to do it.
149
152
  const chainBalances = useMemo(() => {
150
- if (!balanceData?.balances)
153
+ if (!balances)
151
154
  return {};
152
- const filteredBalances = balanceData.balances.filter(token => token.address === "native" || (token.pool_size && token.pool_size > minPoolSize));
153
- return filteredBalances.reduce((acc, token) => {
155
+ return balances.reduce((acc, token) => {
154
156
  const chainId = token.chain_id;
155
157
  if (!acc[chainId]) {
156
158
  acc[chainId] = {
@@ -164,7 +166,7 @@ export function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, p
164
166
  acc[chainId].tokenCount += 1;
165
167
  return acc;
166
168
  }, {});
167
- }, [balanceData, minPoolSize]);
169
+ }, [balances]);
168
170
  // Sort chains by USD value (highest first)
169
171
  const sortedChains = useMemo(() => {
170
172
  return supportedChains
@@ -219,10 +221,10 @@ export function AnySpendDeposit({ loadOrder, mode = "modal", recipientAddress, p
219
221
  if (step === "select-chain") {
220
222
  return (_jsxs("div", { className: depositClasses?.chainSelection ||
221
223
  cn("anyspend-deposit anyspend-deposit-chain-selection font-inter bg-as-surface-primary relative mx-auto w-full max-w-[460px]", mode === "page" && "border-as-border-secondary overflow-hidden rounded-2xl border shadow-xl"), children: [onClose && (_jsx("button", { onClick: onClose, className: depositClasses?.closeButton ||
222
- "anyspend-deposit-close-button text-as-secondary hover:text-as-primary absolute right-4 top-4 z-10", children: _jsx("svg", { className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })), !isBalanceLoading && totalBalance > 0 && (_jsxs("div", { className: depositClasses?.balanceContainer || "anyspend-deposit-balance border-theme-border-secondary border-b p-5", children: [_jsx("p", { className: depositClasses?.balanceLabel || "anyspend-deposit-balance-label text-as-secondary text-sm", children: "Your Balance" }), _jsxs("p", { className: depositClasses?.balanceValue || "anyspend-deposit-balance-value text-as-primary text-3xl font-semibold", children: [formatDecimal(totalBalance), " ", _jsx("span", { className: "text-sm", children: "USD" })] })] })), _jsxs("div", { className: depositClasses?.optionsContainer || "anyspend-deposit-options flex flex-col gap-2 p-6", children: [isBalanceLoading && (_jsx("div", { className: depositClasses?.chainsSkeleton || "anyspend-deposit-chains-skeleton flex flex-col gap-2", children: [1, 2].map((_, i) => (_jsxs("div", { className: depositClasses?.skeletonItem ||
223
- "border-border-primary flex items-center justify-between rounded-xl border p-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "h-6 w-6 rounded-full" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-32" }), _jsx(Skeleton, { className: "h-3 w-20" })] })] }), _jsx(Skeleton, { className: "h-5 w-5" })] }, i))) })), topChainsWithBalance.length > 0 && (_jsx("div", { className: depositClasses?.chainsContainer || "anyspend-deposit-chains flex flex-col gap-2", children: topChainsWithBalance.map(chain => (_jsxs("button", { onClick: () => handleSelectChain(chain.id), className: depositClasses?.chainButton ||
224
+ "anyspend-deposit-close-button text-as-secondary hover:text-as-primary absolute right-4 top-4 z-10", children: _jsx("svg", { className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })), !isBalanceLoading && !isBalanceError && totalBalance > 0 && (_jsxs("div", { className: depositClasses?.balanceContainer || "anyspend-deposit-balance border-theme-border-secondary border-b p-5", children: [_jsx("p", { className: depositClasses?.balanceLabel || "anyspend-deposit-balance-label text-as-secondary text-sm", children: "Your Balance" }), _jsxs("p", { className: depositClasses?.balanceValue || "anyspend-deposit-balance-value text-as-primary text-3xl font-semibold", children: [formatDecimal(totalBalance), " ", _jsx("span", { className: "text-sm", children: "USD" })] })] })), _jsxs("div", { className: depositClasses?.optionsContainer || "anyspend-deposit-options flex flex-col gap-2 p-6", children: [isBalanceLoading && (_jsx("div", { className: depositClasses?.chainsSkeleton || "anyspend-deposit-chains-skeleton flex flex-col gap-2", children: [1, 2].map((_, i) => (_jsxs("div", { className: depositClasses?.skeletonItem ||
225
+ "border-border-primary flex items-center justify-between rounded-xl border p-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "h-6 w-6 rounded-full" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-32" }), _jsx(Skeleton, { className: "h-3 w-20" })] })] }), _jsx(Skeleton, { className: "h-5 w-5" })] }, i))) })), isBalanceError && !isBalanceLoading && (_jsx("p", { className: "text-as-primary/50 px-1 py-2 text-sm", children: "We couldn't read your balances just now. You can still pick a chain below." })), !isBalanceError && topChainsWithBalance.length > 0 && (_jsx("div", { className: depositClasses?.chainsContainer || "anyspend-deposit-chains flex flex-col gap-2", children: topChainsWithBalance.map(chain => (_jsxs("button", { onClick: () => handleSelectChain(chain.id), className: depositClasses?.chainButton ||
224
226
  "anyspend-deposit-chain-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left shadow-sm transition-all", children: [_jsx("div", { className: depositClasses?.chainContent || "anyspend-deposit-chain-content", children: _jsxs("div", { className: depositClasses?.chainInfo || "anyspend-deposit-chain-info", children: [_jsxs("span", { className: depositClasses?.chainName ||
225
- "anyspend-deposit-chain-name text-as-primary flex items-center gap-1.5 font-medium", children: ["Deposit from ", chain.name, _jsx(ChainIcon, { chainId: chain.id, className: depositClasses?.chainIcon || "h-5 w-5" })] }), _jsxs("p", { className: depositClasses?.chainBalance || "anyspend-deposit-chain-balance text-as-secondary text-xs", children: [formatUsd(chain.balance), " available"] })] }) }), _jsx(ChevronRight, { className: depositClasses?.chainChevron || "anyspend-deposit-chain-chevron text-as-secondary h-5 w-5" })] }, chain.id))) })), _jsxs("div", { className: depositClasses?.generalOptions || "anyspend-deposit-general-options flex flex-col gap-2", children: [_jsxs("button", { onClick: handleSelectCrypto, className: depositClasses?.cryptoButton ||
227
+ "anyspend-deposit-chain-name text-as-primary flex items-center gap-1.5 font-medium", children: ["Deposit from ", chain.name, _jsx(ChainIcon, { chainId: chain.id, className: depositClasses?.chainIcon || "h-5 w-5" })] }), _jsx("p", { className: depositClasses?.chainBalance || "anyspend-deposit-chain-balance text-as-secondary text-xs", children: `${formatUsd(chain.balance)} available` })] }) }), _jsx(ChevronRight, { className: depositClasses?.chainChevron || "anyspend-deposit-chain-chevron text-as-secondary h-5 w-5" })] }, chain.id))) })), _jsxs("div", { className: depositClasses?.generalOptions || "anyspend-deposit-general-options flex flex-col gap-2", children: [_jsxs("button", { onClick: handleSelectCrypto, className: depositClasses?.cryptoButton ||
226
228
  "anyspend-deposit-option-button anyspend-deposit-crypto-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left shadow-sm transition-all", children: [_jsx("div", { className: depositClasses?.optionContent || "anyspend-deposit-option-content", children: _jsxs("div", { className: depositClasses?.optionInfo || "anyspend-deposit-option-info", children: [_jsx("span", { className: depositClasses?.optionTitle || "anyspend-deposit-option-title text-as-primary font-medium", children: "Deposit Crypto" }), _jsx("p", { className: depositClasses?.optionDescription ||
227
229
  "anyspend-deposit-option-description text-as-secondary text-xs", children: "Swap from any token on any chain" })] }) }), _jsx(ChevronRight, { className: depositClasses?.optionChevron || "anyspend-deposit-option-chevron text-as-secondary h-5 w-5" })] }), _jsxs("div", { className: depositClasses?.divider || "anyspend-deposit-divider flex items-center gap-3", children: [_jsx("div", { className: depositClasses?.dividerLine || "bg-as-stroke h-px flex-1" }), _jsx("span", { className: depositClasses?.dividerText || "anyspend-deposit-divider-text text-as-secondary text-sm", children: "More options" }), _jsx("div", { className: depositClasses?.dividerLine || "bg-as-stroke h-px flex-1" })] }), _jsxs("button", { onClick: handleSelectQrDeposit, className: depositClasses?.qrButton ||
228
230
  "anyspend-deposit-option-button anyspend-deposit-qr-button border-border-primary hover:border-as-brand hover:bg-as-surface-secondary flex w-full items-center justify-between rounded-xl border p-4 text-left shadow-sm transition-all", children: [_jsxs("div", { className: depositClasses?.optionContent || "anyspend-deposit-option-content flex items-center gap-3", children: [_jsx(QrCodeIcon, { className: depositClasses?.optionIcon || "anyspend-deposit-option-icon h-10 w-10" }), _jsxs("div", { className: depositClasses?.optionInfo || "anyspend-deposit-option-info", children: [_jsx("span", { className: depositClasses?.optionTitle || "anyspend-deposit-option-title text-as-primary font-medium", children: "Deposit with QR Code" }), _jsx("p", { className: depositClasses?.optionDescription ||
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { ABI_ERC20_STAKING, B3_TOKEN, eqci, getExplorerTxUrl } from "../../../anyspend/index.js";
3
- import { Button, GlareCardRounded, Input, StyleRoot, TextLoop, toast, useHasMounted, useModalStore, useSimBalance, useUnifiedChainSwitchAndExecute, } from "../../../global-account/react/index.js";
3
+ import { Button, GlareCardRounded, Input, StyleRoot, TextLoop, toast, useHasMounted, useModalStore, usePortfolioBalance, useUnifiedChainSwitchAndExecute, } from "../../../global-account/react/index.js";
4
4
  import { PUBLIC_BASE_RPC_URL } from "../../../shared/constants/index.js";
5
5
  import { formatTokenAmount } from "../../../shared/utils/number.js";
6
6
  import invariant from "invariant";
@@ -34,11 +34,25 @@ export function AnySpendStakeB3({ loadOrder, mode = "modal", recipientAddress, s
34
34
  // Wagmi hooks for direct staking
35
35
  const { address } = useAccount();
36
36
  const { switchChainAndExecute, isSwitchingOrExecuting } = useUnifiedChainSwitchAndExecute();
37
- // Fetch B3 token balance
38
- const { data: simBalance, isLoading: isBalanceLoading } = useSimBalance(address, [base.id]);
39
- const b3RawBalanceStr = simBalance?.balances.find(b => eqci(b.address, B3_TOKEN.address))?.amount || "0";
37
+ // Fetch B3 token balance. `isBalanceUnknown` must gate every use of `b3RawBalance`: a failed read
38
+ // yields the same 0 as an empty wallet, and acting on it pushes a holder into the swap flow to
39
+ // buy a token they already have.
40
+ const { data: balances, isLoading: isBalanceLoading, isError: isBalanceError, refetch: refetchBalance, } = usePortfolioBalance(address, [base.id]);
41
+ // Guarded on `address`: with no wallet connected the query never runs, and a query that was
42
+ // never asked is not a query that failed. Without this the screen tells a visitor their
43
+ // balance is unavailable and refuses to continue, when they simply have not connected yet.
44
+ const isBalanceUnknown = Boolean(address) && (isBalanceError || (!isBalanceLoading && balances === undefined));
45
+ const b3RawBalanceStr = balances?.find(b => eqci(b.address, B3_TOKEN.address))?.amount || "0";
40
46
  const b3RawBalance = BigInt(b3RawBalanceStr);
41
47
  const b3Balance = formatTokenAmount(b3RawBalance, B3_TOKEN.decimals);
48
+ /**
49
+ * Whether the wallet demonstrably holds enough B3 to stake `amountRaw` directly.
50
+ *
51
+ * False while the balance is unknown — but callers must not read that as a shortfall. An unknown
52
+ * balance is neither enough nor short, and the two need different UI: a shortfall offers the swap
53
+ * flow, an unknown offers a retry. Check `isBalanceUnknown` first.
54
+ */
55
+ const hasEnoughB3 = (amountRaw) => !isBalanceUnknown && BigInt(amountRaw || "0") <= b3RawBalance;
42
56
  // State for direct staking flow
43
57
  const [isStaking, setIsStaking] = useState(false);
44
58
  const [stakingTxHash, setStakingTxHash] = useState("");
@@ -206,9 +220,11 @@ export function AnySpendStakeB3({ loadOrder, mode = "modal", recipientAddress, s
206
220
  toast.error("Please enter a valid amount to stake");
207
221
  return;
208
222
  }
209
- // Check if user has sufficient B3 balance for direct staking
210
- const hasEnoughBalance = b3RawBalance && BigInt(userStakeAmount) <= b3RawBalance;
211
- if (hasEnoughBalance) {
223
+ if (isBalanceUnknown) {
224
+ toast.error("Can't read your B3 balance right now. Please try again.");
225
+ return;
226
+ }
227
+ if (hasEnoughB3(userStakeAmount)) {
212
228
  // User has enough B3, proceed with direct staking
213
229
  handleDirectStaking();
214
230
  }
@@ -232,16 +248,19 @@ export function AnySpendStakeB3({ loadOrder, mode = "modal", recipientAddress, s
232
248
  opacity: hasMounted ? 1 : 0,
233
249
  y: hasMounted ? 0 : 20,
234
250
  filter: hasMounted ? "blur(0px)" : "blur(10px)",
235
- }, transition: { duration: 0.3, delay: 0.1, ease: "easeInOut" }, children: _jsx("h2", { className: "font-sf-rounded font-neue-montreal-medium mb-1 text-center text-2xl font-semibold", children: (() => {
236
- const hasEnoughBalance = b3RawBalance && BigInt(debouncedUserStakeAmount || "0") <= b3RawBalance;
237
- return hasEnoughBalance || !debouncedAmount ? "Stake B3" : "Swap & Stake B3";
238
- })() }) })] }), _jsxs(motion.div, { initial: false, animate: {
251
+ }, transition: { duration: 0.3, delay: 0.1, ease: "easeInOut" }, children: _jsx("h2", { className: "font-sf-rounded font-neue-montreal-medium mb-1 text-center text-2xl font-semibold", children: isBalanceUnknown || !debouncedAmount || hasEnoughB3(debouncedUserStakeAmount)
252
+ ? "Stake B3"
253
+ : "Swap & Stake B3" }) })] }), _jsxs(motion.div, { initial: false, animate: {
239
254
  opacity: hasMounted ? 1 : 0,
240
255
  y: hasMounted ? 0 : 20,
241
256
  filter: hasMounted ? "blur(0px)" : "blur(10px)",
242
- }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: "bg-b3-react-background w-full p-6", children: [_jsx("div", { className: "mb-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-as-primary/70 text-sm font-medium", children: "I want to stake" }), _jsxs("span", { className: "text-as-primary/50 flex items-center gap-1 text-sm", children: ["Available: ", isBalanceLoading ? _jsx(Loader2, { className: "h-3 w-3 animate-spin" }) : `${b3Balance} B3`] })] }) }), _jsxs("div", { className: "relative", children: [_jsx(Input, { onFocus: onFocusStakeAmountInput, type: "text", placeholder: "0.00", value: displayAmount, onChange: e => validateAndSetAmount(e.target.value), className: `h-14 px-4 text-lg ${!isAmountValid && displayAmount ? "border-as-red" : "border-b3-react-border"}` }), _jsx("div", { className: "font-pack absolute right-4 top-1/2 -translate-y-1/2 text-lg font-medium text-blue-500/70", children: "B3" })] }), !isAmountValid && displayAmount && _jsx("p", { className: "text-as-red mt-2 text-sm", children: validationError }), _jsx("div", { className: "mt-4", children: (() => {
243
- const hasEnoughBalance = b3RawBalance && BigInt(debouncedUserStakeAmount || "0") <= b3RawBalance;
244
- if (!hasEnoughBalance || !debouncedAmount) {
257
+ }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: "bg-b3-react-background w-full p-6", children: [_jsx("div", { className: "mb-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-as-primary/70 text-sm font-medium", children: "I want to stake" }), _jsxs("span", { className: "text-as-primary/50 flex items-center gap-1 text-sm", children: ["Available:", " ", isBalanceLoading ? (_jsx(Loader2, { className: "h-3 w-3 animate-spin" })) : isBalanceUnknown ? ("unavailable") : (`${b3Balance} B3`)] })] }) }), _jsxs("div", { className: "relative", children: [_jsx(Input, { onFocus: onFocusStakeAmountInput, type: "text", placeholder: "0.00", value: displayAmount, onChange: e => validateAndSetAmount(e.target.value), className: `h-14 px-4 text-lg ${!isAmountValid && displayAmount ? "border-as-red" : "border-b3-react-border"}` }), _jsx("div", { className: "font-pack absolute right-4 top-1/2 -translate-y-1/2 text-lg font-medium text-blue-500/70", children: "B3" })] }), !isAmountValid && displayAmount && _jsx("p", { className: "text-as-red mt-2 text-sm", children: validationError }), _jsx("div", { className: "mt-4", children: (() => {
258
+ // An unreadable balance is not a shortfall, so it must not open the swap upsell —
259
+ // that would sell a swap to someone who may already hold the tokens.
260
+ if (isBalanceUnknown) {
261
+ return (_jsxs("div", { className: "bg-as-brand/10 flex flex-col items-center gap-2 rounded-lg p-4 pb-5", children: [_jsx("p", { className: "text-as-primary/70 text-sm font-medium", children: "We couldn't read your B3 balance just now." }), _jsx(Button, { variant: "ghost", onClick: () => refetchBalance(), className: "text-as-primary text-sm", children: "Try again" })] }));
262
+ }
263
+ if (!hasEnoughB3(debouncedUserStakeAmount) || !debouncedAmount) {
245
264
  return (_jsxs("div", { className: "bg-as-brand/10 flex flex-col items-center gap-2 rounded-lg p-4 pb-5", children: [_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx("span", { className: "text-as-primary text-sm font-semibold", children: "Swap & stake from any token" }), _jsxs(TextLoop, { children: [_jsx(EthIcon, { className: "h-8 w-8" }), _jsx(SolIcon, { className: "h-8 w-8" }), _jsx(UsdcIcon, { className: "h-8 w-8" })] }), _jsx(ArrowRight, { className: "text-as-primary h-4 w-4" }), _jsx("img", { src: "https://cdn.b3.fun/b3-coin-3d.png", className: "h-7 w-7", alt: "B3 Token" })] }), _jsx("p", { className: "text-as-primary/50 text-sm font-medium", children: debouncedAmount
246
265
  ? `No problem, we'll help you swap to ${debouncedAmount} B3!`
247
266
  : "Not enough B3? We'll help you swap from other coins." })] }));
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { ABI_ERC20_STAKING, B3_TOKEN, eqci, getExplorerTxUrl } from "../../../anyspend/index.js";
3
3
  import { normalizeAddress } from "../../../anyspend/utils/index.js";
4
- import { Button, GlareCardRounded, Input, StyleRoot, TextLoop, toast, useHasMounted, useModalStore, useSimBalance, useUnifiedChainSwitchAndExecute, } from "../../../global-account/react/index.js";
4
+ import { Button, GlareCardRounded, Input, StyleRoot, TextLoop, toast, useHasMounted, useModalStore, usePortfolioBalance, useUnifiedChainSwitchAndExecute, } from "../../../global-account/react/index.js";
5
5
  import { PUBLIC_BASE_RPC_URL } from "../../../shared/constants/index.js";
6
6
  import { formatTokenAmount } from "../../../shared/utils/number.js";
7
7
  import { ArrowRight, Loader2 } from "lucide-react";
@@ -37,11 +37,25 @@ export function AnySpendStakeB3ExactIn({ loadOrder, mode = "modal", sourceTokenA
37
37
  // Wagmi hooks for direct staking
38
38
  const { address } = useAccount();
39
39
  const { switchChainAndExecute, isSwitchingOrExecuting } = useUnifiedChainSwitchAndExecute();
40
- // Fetch B3 token balance
41
- const { data: simBalance, isLoading: isBalanceLoading } = useSimBalance(address, [base.id]);
42
- const b3RawBalanceStr = simBalance?.balances.find(b => eqci(b.address, B3_TOKEN.address))?.amount || "0";
40
+ // Fetch B3 token balance. `isBalanceUnknown` must gate every use of `b3RawBalance`: a failed read
41
+ // yields the same 0 as an empty wallet, and acting on it pushes a holder into the swap flow to
42
+ // buy a token they already have.
43
+ const { data: balances, isLoading: isBalanceLoading, isError: isBalanceError, refetch: refetchBalance, } = usePortfolioBalance(address, [base.id]);
44
+ // Guarded on `address`: with no wallet connected the query never runs, and a query that was
45
+ // never asked is not a query that failed. Without this the screen tells a visitor their
46
+ // balance is unavailable and refuses to continue, when they simply have not connected yet.
47
+ const isBalanceUnknown = Boolean(address) && (isBalanceError || (!isBalanceLoading && balances === undefined));
48
+ const b3RawBalanceStr = balances?.find(b => eqci(b.address, B3_TOKEN.address))?.amount || "0";
43
49
  const b3RawBalance = BigInt(b3RawBalanceStr);
44
50
  const b3Balance = formatTokenAmount(b3RawBalance, B3_TOKEN.decimals);
51
+ /**
52
+ * Whether the wallet demonstrably holds enough B3 to stake `amountRaw` directly.
53
+ *
54
+ * False while the balance is unknown — but callers must not read that as a shortfall. An unknown
55
+ * balance is neither enough nor short, and the two need different UI: a shortfall offers the swap
56
+ * flow, an unknown offers a retry. Check `isBalanceUnknown` first.
57
+ */
58
+ const hasEnoughB3 = (amountRaw) => !isBalanceUnknown && BigInt(amountRaw || "0") <= b3RawBalance;
45
59
  // State for direct staking flow
46
60
  const [isStaking, setIsStaking] = useState(false);
47
61
  const [stakingTxHash, setStakingTxHash] = useState("");
@@ -212,8 +226,11 @@ export function AnySpendStakeB3ExactIn({ loadOrder, mode = "modal", sourceTokenA
212
226
  return;
213
227
  }
214
228
  // Check if user has sufficient B3 balance for direct staking
215
- const hasEnoughBalance = b3RawBalance && BigInt(userStakeAmount) <= b3RawBalance;
216
- if (hasEnoughBalance) {
229
+ if (isBalanceUnknown) {
230
+ toast.error("Can't read your B3 balance right now. Please try again.");
231
+ return;
232
+ }
233
+ if (hasEnoughB3(userStakeAmount)) {
217
234
  // User has enough B3, proceed with direct staking
218
235
  handleDirectStaking();
219
236
  }
@@ -246,15 +263,20 @@ export function AnySpendStakeB3ExactIn({ loadOrder, mode = "modal", sourceTokenA
246
263
  y: hasMounted ? 0 : 20,
247
264
  filter: hasMounted ? "blur(0px)" : "blur(10px)",
248
265
  }, transition: { duration: 0.3, delay: 0.1, ease: "easeInOut" }, children: _jsx("h2", { className: "font-sf-rounded font-neue-montreal-medium mb-1 text-center text-2xl font-semibold", children: (() => {
249
- const hasEnoughBalance = b3RawBalance && BigInt(debouncedUserStakeAmount || "0") <= b3RawBalance;
250
- return hasEnoughBalance || !debouncedAmount ? "Stake B3" : "Swap & Stake B3";
266
+ return isBalanceUnknown || !debouncedAmount || hasEnoughB3(debouncedUserStakeAmount)
267
+ ? "Stake B3"
268
+ : "Swap & Stake B3";
251
269
  })() }) })] }), _jsxs(motion.div, { initial: false, animate: {
252
270
  opacity: hasMounted ? 1 : 0,
253
271
  y: hasMounted ? 0 : 20,
254
272
  filter: hasMounted ? "blur(0px)" : "blur(10px)",
255
- }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: "bg-b3-react-background w-full p-6", children: [_jsx("div", { className: "mb-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-as-primary/70 text-sm font-medium", children: "I want to stake" }), _jsxs("span", { className: "text-as-primary/50 flex items-center gap-1 text-sm", children: ["Available: ", isBalanceLoading ? _jsx(Loader2, { className: "h-3 w-3 animate-spin" }) : `${b3Balance} B3`] })] }) }), _jsxs("div", { className: "relative", children: [_jsx(Input, { onFocus: onFocusStakeAmountInput, type: "text", placeholder: "0.00", value: displayAmount, onChange: e => validateAndSetAmount(e.target.value), className: `h-14 px-4 text-lg ${!isAmountValid && displayAmount ? "border-as-red" : "border-b3-react-border"}` }), _jsx("div", { className: "font-pack absolute right-4 top-1/2 -translate-y-1/2 text-lg font-medium text-blue-500/70", children: "B3" })] }), !isAmountValid && displayAmount && _jsx("p", { className: "text-as-red mt-2 text-sm", children: validationError }), _jsx("div", { className: "mt-4", children: (() => {
256
- const hasEnoughBalance = b3RawBalance && BigInt(debouncedUserStakeAmount || "0") <= b3RawBalance;
257
- if (!hasEnoughBalance || !debouncedAmount) {
273
+ }, transition: { duration: 0.3, delay: 0.2, ease: "easeInOut" }, className: "bg-b3-react-background w-full p-6", children: [_jsx("div", { className: "mb-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-as-primary/70 text-sm font-medium", children: "I want to stake" }), _jsxs("span", { className: "text-as-primary/50 flex items-center gap-1 text-sm", children: ["Available:", " ", isBalanceLoading ? (_jsx(Loader2, { className: "h-3 w-3 animate-spin" })) : isBalanceUnknown ? ("unavailable") : (`${b3Balance} B3`)] })] }) }), _jsxs("div", { className: "relative", children: [_jsx(Input, { onFocus: onFocusStakeAmountInput, type: "text", placeholder: "0.00", value: displayAmount, onChange: e => validateAndSetAmount(e.target.value), className: `h-14 px-4 text-lg ${!isAmountValid && displayAmount ? "border-as-red" : "border-b3-react-border"}` }), _jsx("div", { className: "font-pack absolute right-4 top-1/2 -translate-y-1/2 text-lg font-medium text-blue-500/70", children: "B3" })] }), !isAmountValid && displayAmount && _jsx("p", { className: "text-as-red mt-2 text-sm", children: validationError }), _jsx("div", { className: "mt-4", children: (() => {
274
+ // An unreadable balance is not a shortfall, so it must not open the swap upsell —
275
+ // that would sell a swap to someone who may already hold the tokens.
276
+ if (isBalanceUnknown) {
277
+ return (_jsxs("div", { className: "bg-as-brand/10 flex flex-col items-center gap-2 rounded-lg p-4 pb-5", children: [_jsx("p", { className: "text-as-primary/70 text-sm font-medium", children: "We couldn't read your B3 balance just now." }), _jsx(Button, { variant: "ghost", onClick: () => refetchBalance(), className: "text-as-primary text-sm", children: "Try again" })] }));
278
+ }
279
+ if (!hasEnoughB3(debouncedUserStakeAmount) || !debouncedAmount) {
258
280
  return (_jsxs("div", { className: "bg-as-brand/10 flex flex-col items-center gap-2 rounded-lg p-4 pb-5", children: [_jsxs("div", { className: "flex items-center justify-center gap-2", children: [_jsx("span", { className: "text-as-primary text-sm font-semibold", children: "Swap & stake from any token" }), _jsxs(TextLoop, { children: [_jsx(EthIcon, { className: "h-8 w-8" }), _jsx(SolIcon, { className: "h-8 w-8" }), _jsx(UsdcIcon, { className: "h-8 w-8" })] }), _jsx(ArrowRight, { className: "text-as-primary h-4 w-4" }), _jsx("img", { src: "https://cdn.b3.fun/b3-coin-3d.png", className: "h-7 w-7", alt: "B3 Token" })] }), _jsx("p", { className: "text-as-primary/50 text-sm font-medium", children: debouncedAmount
259
281
  ? `No problem, we'll help you swap to ${debouncedAmount} B3!`
260
282
  : "Not enough B3? We'll help you swap from other coins." })] }));
@@ -6,9 +6,9 @@ import { useAnyspendOrderAndTransactions } from "../../../../anyspend/react/hook
6
6
  import { useCreateDepositFirstOrder } from "../../../../anyspend/react/hooks/useCreateDepositFirstOrder.js";
7
7
  import { useOnOrderSuccess } from "../../../../anyspend/react/hooks/useOnOrderSuccess.js";
8
8
  import { ALL_CHAINS, RELAY_SOLANA_MAINNET_CHAIN_ID, getAvailableChainIds } from "../../../../anyspend/index.js";
9
- import { getPaymentUrl } from "../../../../anyspend/utils/chain.js";
9
+ import { getPaymentUrl, toRegistryChainId } from "../../../../anyspend/utils/chain.js";
10
10
  import { isNativeToken } from "../../../../anyspend/utils/token.js";
11
- import { useAccountWallet, useIsMobile, useSimTokenBalance, useTokenData, useUnifiedChainSwitchAndExecute, } from "../../../../global-account/react/index.js";
11
+ import { useAccountWallet, useIsMobile, usePortfolioTokenBalance, useTokenData, useUnifiedChainSwitchAndExecute, } from "../../../../global-account/react/index.js";
12
12
  import { ShinyButton, TextShimmer } from "../../../../global-account/react/index.js";
13
13
  import { formatTokenAmount } from "../../../../shared/utils/number.js";
14
14
  import { cn } from "../../../../shared/utils/cn.js";
@@ -68,13 +68,17 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
68
68
  ? "native"
69
69
  : selectedSrcToken.address
70
70
  : undefined;
71
- const { data: balanceData } = useSimTokenBalance(effectiveAddress, tokenAddress, selectedSrcChainId);
71
+ // `selectedSrcChainId` is Relay-numbered; balance rows are keyed by the chain registry's id.
72
+ const { data: balanceRow, isError: isBalanceError } = usePortfolioTokenBalance(effectiveAddress, tokenAddress, toRegistryChainId(selectedSrcChainId));
72
73
  const balance = useMemo(() => {
73
- const b = balanceData?.balances?.[0];
74
- if (!b?.amount)
74
+ if (!balanceRow?.amount)
75
75
  return { raw: BigInt(0), formatted: "0", decimals: 18 };
76
- return { raw: BigInt(b.amount), formatted: formatTokenAmount(BigInt(b.amount), b.decimals), decimals: b.decimals };
77
- }, [balanceData]);
76
+ return {
77
+ raw: BigInt(balanceRow.amount),
78
+ formatted: formatTokenAmount(BigInt(balanceRow.amount), balanceRow.decimals),
79
+ decimals: balanceRow.decimals,
80
+ };
81
+ }, [balanceRow]);
78
82
  const srcAmount = useMemo(() => {
79
83
  if (isSameToken)
80
84
  return totalAmount;
@@ -85,7 +89,9 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
85
89
  return "0";
86
90
  return formatTokenAmount(BigInt(srcAmount || "0"), selectedSrcToken.decimals || 18);
87
91
  }, [srcAmount, selectedSrcToken]);
88
- const hasEnoughBalance = balance.raw >= BigInt(srcAmount || "0");
92
+ // A failed balance read must not masquerade as a shortfall: it would disable Pay and tell a
93
+ // funded user they are short. Unknown blocks auto-pay but leaves the manual path open.
94
+ const hasEnoughBalance = !isBalanceError && balance.raw >= BigInt(srcAmount || "0");
89
95
  /* ------------------------------------------------------------------ */
90
96
  /* Destination token object (shared by both flows) */
91
97
  /* ------------------------------------------------------------------ */
@@ -314,7 +320,7 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
314
320
  }, [shouldAutoPay, connectedAddress, selectedSrcToken, hasEnoughBalance, isLoadingAnyspendQuote, handleWalletPay]);
315
321
  const isLoading = isLoadingAnyspendQuote;
316
322
  const isPending = isCreatingSwapOrder || isSendingDeposit || isWaitingForExecution;
317
- const canPay = connectedAddress && selectedSrcToken && hasEnoughBalance && !isLoading && !isPending;
323
+ const canPay = connectedAddress && selectedSrcToken && (hasEnoughBalance || isBalanceError) && !isLoading && !isPending;
318
324
  const chainName = ALL_CHAINS[selectedSrcChainId]?.name || "the specified chain";
319
325
  const chainLogoUrl = ALL_CHAINS[selectedSrcChainId]?.logoUrl;
320
326
  // Collapse QR on mobile when a wallet connector is available
@@ -333,7 +339,9 @@ export function CryptoPayPanel({ recipientAddress, destinationTokenAddress, dest
333
339
  name: token.name,
334
340
  symbol: token.symbol,
335
341
  });
336
- }, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: _jsxs("button", { className: cn("flex w-full items-center justify-between rounded-xl border border-gray-200 bg-white px-4 py-3 transition-colors hover:border-gray-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600", classes?.tokenSelector), children: [selectedSrcToken ? (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[selectedSrcToken.chainId]?.logoUrl || "", tokenUrl: selectedSrcToken.metadata?.logoURI, className: "h-8 w-8" }), _jsxs("div", { className: "text-left", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: selectedSrcToken.symbol }), _jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: ["Balance: ", balance.formatted] })] })] })) : (_jsx("span", { className: "text-sm text-gray-400", children: "Select token" })), _jsx(ChevronsUpDown, { className: "h-4 w-4 text-gray-400" })] }) })] }), _jsx(motion.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 dark:border-neutral-700 dark:bg-neutral-800/50", classes?.quoteDisplay), children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "You pay" }), _jsx(AnimatePresence, { mode: "wait", children: isLoadingAnyspendQuote ? (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: _jsx(TextShimmer, { duration: 1, className: "text-sm", children: "Fetching quote..." }) }, "quote-loading")) : (_jsxs(motion.span, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: [srcAmountFormatted, " ", selectedSrcToken?.symbol || ""] }, "quote-amount")) })] }) }), _jsx(AnimatePresence, { children: connectedAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && (_jsxs(motion.p, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, className: "text-center text-sm text-red-500", children: ["Insufficient ", selectedSrcToken.symbol, " balance"] }, "balance-warning")) }), !connectedAddress ? (_jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleConnectWallet, className: cn("w-full", classes?.payButton), textClassName: "text-white", children: "Connect Wallet to Pay" })) : (_jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleWalletPay, disabled: !canPay, className: cn("w-full", classes?.payButton), textClassName: cn(!canPay ? "text-as-secondary" : "text-white"), children: isPending ? (_jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), isCreatingSwapOrder
342
+ }, supportedWalletVMs: ["evm", "svm"], token: undefined, trigger: _jsxs("button", { className: cn("flex w-full items-center justify-between rounded-xl border border-gray-200 bg-white px-4 py-3 transition-colors hover:border-gray-300 dark:border-neutral-700 dark:bg-neutral-800 dark:hover:border-neutral-600", classes?.tokenSelector), children: [selectedSrcToken ? (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(ChainTokenIcon, { chainUrl: ALL_CHAINS[selectedSrcToken.chainId]?.logoUrl || "", tokenUrl: selectedSrcToken.metadata?.logoURI, className: "h-8 w-8" }), _jsxs("div", { className: "text-left", children: [_jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: selectedSrcToken.symbol }), _jsxs("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: ["Balance: ", balance.formatted] })] })] })) : (_jsx("span", { className: "text-sm text-gray-400", children: "Select token" })), _jsx(ChevronsUpDown, { className: "h-4 w-4 text-gray-400" })] }) })] }), _jsx(motion.div, { initial: { opacity: 0, y: 6 }, animate: { opacity: 1, y: 0 }, transition: { duration: 0.25, ease: "easeOut" }, className: cn("rounded-xl border border-gray-200 bg-gray-50 px-4 py-3 dark:border-neutral-700 dark:bg-neutral-800/50", classes?.quoteDisplay), children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: "You pay" }), _jsx(AnimatePresence, { mode: "wait", children: isLoadingAnyspendQuote ? (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, children: _jsx(TextShimmer, { duration: 1, className: "text-sm", children: "Fetching quote..." }) }, "quote-loading")) : (_jsxs(motion.span, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.15 }, className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: [srcAmountFormatted, " ", selectedSrcToken?.symbol || ""] }, "quote-amount")) })] }) }), _jsx(AnimatePresence, { children: connectedAddress && selectedSrcToken && !hasEnoughBalance && !isLoading && (_jsx(motion.p, { initial: { opacity: 0, height: 0 }, animate: { opacity: 1, height: "auto" }, exit: { opacity: 0, height: 0 }, transition: { duration: 0.2, ease: "easeOut" }, className: `text-center text-sm ${isBalanceError ? "text-as-primary/50" : "text-red-500"}`, children: isBalanceError
343
+ ? `Couldn't check your ${selectedSrcToken.symbol} balance`
344
+ : `Insufficient ${selectedSrcToken.symbol} balance` }, "balance-warning")) }), !connectedAddress ? (_jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleConnectWallet, className: cn("w-full", classes?.payButton), textClassName: "text-white", children: "Connect Wallet to Pay" })) : (_jsx(ShinyButton, { accentColor: themeColor || "hsl(var(--as-brand))", onClick: handleWalletPay, disabled: !canPay, className: cn("w-full", classes?.payButton), textClassName: cn(!canPay ? "text-as-secondary" : "text-white"), children: isPending ? (_jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), isCreatingSwapOrder
337
345
  ? "Creating order..."
338
346
  : isSendingDeposit
339
347
  ? "Confirm in wallet..."