@openfort/react 1.3.0 → 1.5.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.
Files changed (80) hide show
  1. package/build/components/Common/Modal/styles.js +3 -0
  2. package/build/components/Common/Modal/styles.js.map +1 -1
  3. package/build/components/Common/ScrollArea/index.d.ts +5 -1
  4. package/build/components/Common/ScrollArea/index.js +2 -2
  5. package/build/components/Common/ScrollArea/styles.d.ts +4 -1
  6. package/build/components/Common/ScrollArea/styles.js +19 -4
  7. package/build/components/Common/ScrollArea/styles.js.map +1 -1
  8. package/build/components/ConnectModal/index.js +15 -5
  9. package/build/components/ConnectModal/index.js.map +1 -1
  10. package/build/components/PageContent/index.d.ts +2 -1
  11. package/build/components/PageContent/index.js +2 -2
  12. package/build/components/Pages/BuyProviderSelect/styles.d.ts +2 -1
  13. package/build/components/Pages/Connected/EthereumConnected.js +7 -3
  14. package/build/components/Pages/Connected/EthereumConnected.js.map +1 -1
  15. package/build/components/Pages/Deposit/AssetChainLogo.d.ts +5 -2
  16. package/build/components/Pages/Deposit/AssetChainLogo.js +21 -4
  17. package/build/components/Pages/Deposit/AssetChainLogo.js.map +1 -1
  18. package/build/components/Pages/Deposit/DepositAddressBlock.js +1 -1
  19. package/build/components/Pages/Deposit/RouteSelectors.js +11 -1
  20. package/build/components/Pages/Deposit/RouteSelectors.js.map +1 -1
  21. package/build/components/Pages/Deposit/SameChainDepositStatus.d.ts +7 -0
  22. package/build/components/Pages/Deposit/SameChainDepositStatus.js +34 -0
  23. package/build/components/Pages/Deposit/SameChainDepositStatus.js.map +1 -0
  24. package/build/components/Pages/Deposit/SameChainDepositSuccess.d.ts +11 -0
  25. package/build/components/Pages/Deposit/SameChainDepositSuccess.js +30 -0
  26. package/build/components/Pages/Deposit/SameChainDepositSuccess.js.map +1 -0
  27. package/build/components/Pages/Deposit/TestnetNotice.d.ts +10 -0
  28. package/build/components/Pages/Deposit/TestnetNotice.js +131 -0
  29. package/build/components/Pages/Deposit/TestnetNotice.js.map +1 -0
  30. package/build/components/Pages/Deposit/UnsupportedNetworkNotice.d.ts +12 -0
  31. package/build/components/Pages/Deposit/UnsupportedNetworkNotice.js +111 -0
  32. package/build/components/Pages/Deposit/UnsupportedNetworkNotice.js.map +1 -0
  33. package/build/components/Pages/Deposit/index.js +20 -4
  34. package/build/components/Pages/Deposit/index.js.map +1 -1
  35. package/build/components/Pages/Deposit/paymentOptions.d.ts +6 -0
  36. package/build/components/Pages/Deposit/paymentOptions.js +7 -1
  37. package/build/components/Pages/Deposit/paymentOptions.js.map +1 -1
  38. package/build/components/Pages/Deposit/useDepositRoute.d.ts +2 -0
  39. package/build/components/Pages/Deposit/useDepositRoute.js +26 -2
  40. package/build/components/Pages/Deposit/useDepositRoute.js.map +1 -1
  41. package/build/components/Pages/Deposit/useSameChainArrival.d.ts +19 -0
  42. package/build/components/Pages/Deposit/useSameChainArrival.js +86 -0
  43. package/build/components/Pages/Deposit/useSameChainArrival.js.map +1 -0
  44. package/build/components/Pages/DepositCrypto/index.js +26 -3
  45. package/build/components/Pages/DepositCrypto/index.js.map +1 -1
  46. package/build/components/Pages/DepositWallet/DepositWalletDesktop.js +9 -8
  47. package/build/components/Pages/DepositWallet/DepositWalletDesktop.js.map +1 -1
  48. package/build/components/Pages/DepositWallet/index.js +48 -10
  49. package/build/components/Pages/DepositWallet/index.js.map +1 -1
  50. package/build/components/Pages/SelectToken/styles.d.ts +2 -1
  51. package/build/components/Pages/SendConfirmation/index.js +1 -1
  52. package/build/constants/chainConfigs.js +32 -32
  53. package/build/constants/logos.d.ts +4 -0
  54. package/build/constants/logos.js +25 -1
  55. package/build/constants/logos.js.map +1 -1
  56. package/build/ethereum/hooks/useEthereumWalletAssets.js +39 -3
  57. package/build/ethereum/hooks/useEthereumWalletAssets.js.map +1 -1
  58. package/build/hooks/openfort/useFundingChains.d.ts +6 -0
  59. package/build/hooks/openfort/useFundingChains.js +19 -5
  60. package/build/hooks/openfort/useFundingChains.js.map +1 -1
  61. package/build/hooks/openfort/useUI.d.ts +13 -1
  62. package/build/hooks/openfort/useUI.js +19 -1
  63. package/build/hooks/openfort/useUI.js.map +1 -1
  64. package/build/shared/utils/explorer.js +6 -4
  65. package/build/shared/utils/explorer.js.map +1 -1
  66. package/build/utils/validation.d.ts +9 -0
  67. package/build/utils/validation.js +14 -1
  68. package/build/utils/validation.js.map +1 -1
  69. package/build/version.d.ts +1 -1
  70. package/build/version.js +1 -1
  71. package/build/wagmi/components/ChainSelect/index.js +3 -2
  72. package/build/wagmi/components/ChainSelect/index.js.map +1 -1
  73. package/build/wagmi/components/ChainSelectList/index.js +3 -2
  74. package/build/wagmi/components/ChainSelectList/index.js.map +1 -1
  75. package/build/wagmi/components/SwitchNetworks/index.js +3 -2
  76. package/build/wagmi/components/SwitchNetworks/index.js.map +1 -1
  77. package/build/wagmi/useSwitchChainFiltered.d.ts +199 -0
  78. package/build/wagmi/useSwitchChainFiltered.js +53 -0
  79. package/build/wagmi/useSwitchChainFiltered.js.map +1 -0
  80. package/package.json +1 -1
@@ -329,7 +329,7 @@ const SendConfirmation = () => {
329
329
  if (isSuccess) {
330
330
  const successAmount = normalisedAmount || '0';
331
331
  const successSymbol = getAssetSymbol(token);
332
- return (jsxs(PageContent, { children: [jsx(Loader, { isSuccess: true, header: "Transfer Sent", description: `${successAmount} ${successSymbol} sent successfully` }), jsxs(ButtonRow, { children: [jsx(Button, { variant: "primary", onClick: handleOpenBlockExplorer, children: "View on Explorer" }), jsx(Button, { variant: "secondary", onClick: handleFinish, children: "Back to profile" })] })] }));
332
+ return (jsxs(PageContent, { children: [jsx(Loader, { isSuccess: true, header: "Transfer Sent", description: `${successAmount} ${successSymbol} sent successfully` }), jsxs(ButtonRow, { children: [blockExplorerUrl && (jsx(Button, { variant: "primary", onClick: handleOpenBlockExplorer, children: "View on Explorer" })), jsx(Button, { variant: "secondary", onClick: handleFinish, children: "Back to profile" })] })] }));
333
333
  }
334
334
  return (jsxs(PageContent, { children: [jsx(ModalHeading, { children: "Confirm transfer" }), jsx(ModalBody, { children: "Review the transaction details before sending." }), jsxs(SummaryList, { children: [jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Sending" }), jsxs(AmountValue, { children: [normalisedAmount || '0', " ", getAssetSymbol(token)] })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "From" }), jsx(AddressValue, { children: address ? (jsx(CopyText, { size: "1rem", value: address, children: truncateEthAddress(address) })) : ('--') })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "To" }), jsx(AddressValue, { children: recipientAddress ? (jsx(CopyText, { size: "1rem", value: recipientAddress, children: truncateEthAddress(recipientAddress) })) : ('--') })] }), jsxs("div", { children: [jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Estimated fees" }), jsxs(FeesValue, { "$completed": isSponsored, children: [jsx(EstimatedFees, { account: address, to: token.type === 'erc20' ? token.address : recipientAddress, value: token.type === 'native' && parsedAmount ? parsedAmount : undefined, data: transferData, chainId: chainId, nativeSymbol: nativeSymbol, enabled: Boolean(address && recipientAddress && parsedAmount && parsedAmount > BigInt(0)), hideInfoIcon: isSponsored }), jsx(CheckIconWrapper, { children: jsx(TickIcon, {}) })] })] }), isSponsored && (jsx("div", { style: {
335
335
  textAlign: 'end',
@@ -27,28 +27,28 @@ const chainConfigs = [
27
27
  {
28
28
  id: 37111,
29
29
  name: 'Lens Chain Testnet',
30
- logo: jsx(ChainIcons.LensChain, { testnet: true }),
30
+ logo: jsx(ChainIcons.LensChain, {}),
31
31
  },
32
32
  {
33
33
  id: 3,
34
34
  name: 'Rinkeby',
35
- logo: jsx(ChainIcons.Ethereum, { testnet: true }),
35
+ logo: jsx(ChainIcons.Ethereum, {}),
36
36
  rpcUrls: {},
37
37
  },
38
38
  {
39
39
  id: 4,
40
40
  name: 'Ropsten',
41
- logo: jsx(ChainIcons.Ethereum, { testnet: true }),
41
+ logo: jsx(ChainIcons.Ethereum, {}),
42
42
  },
43
43
  {
44
44
  id: 5,
45
45
  name: 'Görli',
46
- logo: jsx(ChainIcons.Ethereum, { testnet: true }),
46
+ logo: jsx(ChainIcons.Ethereum, {}),
47
47
  },
48
48
  {
49
49
  id: 42,
50
50
  name: 'Kovan',
51
- logo: jsx(ChainIcons.Ethereum, { testnet: true }),
51
+ logo: jsx(ChainIcons.Ethereum, {}),
52
52
  },
53
53
  {
54
54
  id: 10,
@@ -58,17 +58,17 @@ const chainConfigs = [
58
58
  {
59
59
  id: 69, // nice
60
60
  name: 'Optimism Kovan',
61
- logo: jsx(ChainIcons.Optimism, { testnet: true }),
61
+ logo: jsx(ChainIcons.Optimism, {}),
62
62
  },
63
63
  {
64
64
  id: 420, // nice
65
65
  name: 'Optimism Goerli',
66
- logo: jsx(ChainIcons.Optimism, { testnet: true }),
66
+ logo: jsx(ChainIcons.Optimism, {}),
67
67
  },
68
68
  {
69
69
  id: 11155420,
70
70
  name: 'Optimism Sepolia',
71
- logo: jsx(ChainIcons.Optimism, { testnet: true }),
71
+ logo: jsx(ChainIcons.Optimism, {}),
72
72
  },
73
73
  {
74
74
  id: 137,
@@ -78,27 +78,27 @@ const chainConfigs = [
78
78
  {
79
79
  id: 80001,
80
80
  name: 'Polygon Mumbai',
81
- logo: jsx(ChainIcons.Polygon, { testnet: true }),
81
+ logo: jsx(ChainIcons.Polygon, {}),
82
82
  },
83
83
  {
84
84
  id: 80002,
85
85
  name: 'Polygon Amoy',
86
- logo: jsx(ChainIcons.Polygon, { testnet: true }),
86
+ logo: jsx(ChainIcons.Polygon, {}),
87
87
  },
88
88
  {
89
89
  id: 13337,
90
90
  name: 'Beam Testnet',
91
- logo: jsx(ChainIcons.Avalanche, { testnet: true }),
91
+ logo: jsx(ChainIcons.Avalanche, {}),
92
92
  },
93
93
  {
94
94
  id: 31337,
95
95
  name: 'Hardhat',
96
- logo: jsx(ChainIcons.Ethereum, { testnet: true }),
96
+ logo: jsx(ChainIcons.Ethereum, {}),
97
97
  },
98
98
  {
99
99
  id: 1337,
100
100
  name: 'Localhost',
101
- logo: jsx(ChainIcons.Ethereum, { testnet: true }),
101
+ logo: jsx(ChainIcons.Ethereum, {}),
102
102
  },
103
103
  {
104
104
  id: 42161,
@@ -118,12 +118,12 @@ const chainConfigs = [
118
118
  {
119
119
  id: 421611,
120
120
  name: 'Arbitrum Rinkeby',
121
- logo: jsx(ChainIcons.Arbitrum, { testnet: true }),
121
+ logo: jsx(ChainIcons.Arbitrum, {}),
122
122
  },
123
123
  {
124
124
  id: 421613,
125
125
  name: 'Arbitrum Goerli',
126
- logo: jsx(ChainIcons.Arbitrum, { testnet: true }),
126
+ logo: jsx(ChainIcons.Arbitrum, {}),
127
127
  rpcUrls: {
128
128
  alchemy: {
129
129
  http: ['https://arb-goerli.g.alchemy.com/v2'],
@@ -143,7 +143,7 @@ const chainConfigs = [
143
143
  {
144
144
  id: 41,
145
145
  name: 'Telos Testnet',
146
- logo: jsx(ChainIcons.Telos, { testnet: true }),
146
+ logo: jsx(ChainIcons.Telos, {}),
147
147
  },
148
148
  {
149
149
  id: 1313161554,
@@ -153,7 +153,7 @@ const chainConfigs = [
153
153
  {
154
154
  id: 1313161555,
155
155
  name: 'Aurora Testnet',
156
- logo: jsx(ChainIcons.Aurora, { testnet: true }),
156
+ logo: jsx(ChainIcons.Aurora, {}),
157
157
  },
158
158
  {
159
159
  id: 43114,
@@ -163,12 +163,12 @@ const chainConfigs = [
163
163
  {
164
164
  id: 43113,
165
165
  name: 'Avalanche Fuji',
166
- logo: jsx(ChainIcons.Avalanche, { testnet: true }),
166
+ logo: jsx(ChainIcons.Avalanche, {}),
167
167
  },
168
168
  {
169
169
  id: 31337,
170
170
  name: 'Foundry',
171
- logo: jsx(ChainIcons.Foundry, { testnet: true }),
171
+ logo: jsx(ChainIcons.Foundry, {}),
172
172
  },
173
173
  {
174
174
  id: 100,
@@ -183,7 +183,7 @@ const chainConfigs = [
183
183
  {
184
184
  id: 9000,
185
185
  name: 'Evmos Testnet',
186
- logo: jsx(ChainIcons.Evmos, { testnet: true }),
186
+ logo: jsx(ChainIcons.Evmos, {}),
187
187
  },
188
188
  {
189
189
  id: 56,
@@ -193,7 +193,7 @@ const chainConfigs = [
193
193
  {
194
194
  id: 97,
195
195
  name: 'Binance Smart Chain Testnet',
196
- logo: jsx(ChainIcons.BinanceSmartChain, { testnet: true }),
196
+ logo: jsx(ChainIcons.BinanceSmartChain, {}),
197
197
  },
198
198
  {
199
199
  id: 11155111,
@@ -208,7 +208,7 @@ const chainConfigs = [
208
208
  {
209
209
  id: 842,
210
210
  name: 'Taraxa Testnet',
211
- logo: jsx(ChainIcons.Taraxa, { testnet: true }),
211
+ logo: jsx(ChainIcons.Taraxa, {}),
212
212
  },
213
213
  {
214
214
  id: 324,
@@ -218,7 +218,7 @@ const chainConfigs = [
218
218
  {
219
219
  id: 280,
220
220
  name: 'zkSync Testnet',
221
- logo: jsx(ChainIcons.zkSync, { testnet: true }),
221
+ logo: jsx(ChainIcons.zkSync, {}),
222
222
  },
223
223
  {
224
224
  id: 42220,
@@ -228,7 +228,7 @@ const chainConfigs = [
228
228
  {
229
229
  id: 44787,
230
230
  name: 'Celo Alfajores',
231
- logo: jsx(ChainIcons.Celo, { testnet: true }),
231
+ logo: jsx(ChainIcons.Celo, {}),
232
232
  },
233
233
  {
234
234
  id: 7700,
@@ -253,12 +253,12 @@ const chainConfigs = [
253
253
  {
254
254
  id: 3141,
255
255
  name: 'Filecoin Hyperspace',
256
- logo: jsx(ChainIcons.Filecoin, { testnet: true }),
256
+ logo: jsx(ChainIcons.Filecoin, {}),
257
257
  },
258
258
  {
259
259
  id: 314159,
260
260
  name: 'Filecoin Calibration',
261
- logo: jsx(ChainIcons.Filecoin, { testnet: true }),
261
+ logo: jsx(ChainIcons.Filecoin, {}),
262
262
  },
263
263
  {
264
264
  id: 1088,
@@ -268,7 +268,7 @@ const chainConfigs = [
268
268
  {
269
269
  id: 599,
270
270
  name: 'Metis Goerli',
271
- logo: jsx(ChainIcons.Metis, { testnet: true }),
271
+ logo: jsx(ChainIcons.Metis, {}),
272
272
  },
273
273
  {
274
274
  id: 4689,
@@ -278,7 +278,7 @@ const chainConfigs = [
278
278
  {
279
279
  id: 4690,
280
280
  name: 'IoTeX Testnet',
281
- logo: jsx(ChainIcons.IoTeX, { testnet: true }),
281
+ logo: jsx(ChainIcons.IoTeX, {}),
282
282
  },
283
283
  {
284
284
  id: 8453,
@@ -288,12 +288,12 @@ const chainConfigs = [
288
288
  {
289
289
  id: 84531,
290
290
  name: 'Base Goerli',
291
- logo: jsx(ChainIcons.Base, { testnet: true }),
291
+ logo: jsx(ChainIcons.Base, {}),
292
292
  },
293
293
  {
294
294
  id: 84532,
295
295
  name: 'Base Sepolia',
296
- logo: jsx(ChainIcons.Base, { testnet: true }),
296
+ logo: jsx(ChainIcons.Base, {}),
297
297
  },
298
298
  {
299
299
  id: 7777777,
@@ -303,12 +303,12 @@ const chainConfigs = [
303
303
  {
304
304
  id: 999999999,
305
305
  name: 'Zora Sepolia',
306
- logo: jsx(ChainIcons.Zora, { testnet: true }),
306
+ logo: jsx(ChainIcons.Zora, {}),
307
307
  },
308
308
  {
309
309
  id: 999,
310
310
  name: 'Zora Goerli Testnet',
311
- logo: jsx(ChainIcons.Zora, { testnet: true }),
311
+ logo: jsx(ChainIcons.Zora, {}),
312
312
  },
313
313
  ];
314
314
 
@@ -1,2 +1,6 @@
1
1
  export declare const TOKEN_LOGO: Record<string, string>;
2
2
  export declare function symbolToColor(symbol: string): string;
3
+ /** Network logo for a chain: the curated brand first (clean + testnet-aware), then the backend value. */
4
+ export declare function chainLogoUrl(chainId?: number, apiLogo?: string | null): string | null;
5
+ /** Token logo for a symbol: the backend value first, then the curated map (covers native ETH/SOL/… which the rail returns null for). */
6
+ export declare function currencyLogoUrl(symbol?: string, apiLogo?: string | null): string | null;
@@ -54,6 +54,30 @@ function symbolToColor(symbol) {
54
54
  const h = ((hash % 360) + 360) % 360;
55
55
  return `hsl(${h}, 55%, 50%)`;
56
56
  }
57
+ // EVM chain id -> network logo URL. Testnet ids reuse their mainnet brand. Used
58
+ // instead of the funding rail's icon CDN, which serves a generic prism for
59
+ // testnets and a blank square for some mainnets.
60
+ const CHAIN_LOGO = {
61
+ 1: `${TW}/ethereum/info/logo.png`,
62
+ 11155111: `${TW}/ethereum/info/logo.png`, // Sepolia
63
+ 8453: `${TW}/base/info/logo.png`,
64
+ 84532: `${TW}/base/info/logo.png`, // Base Sepolia
65
+ 10: `${TW}/optimism/info/logo.png`,
66
+ 11155420: `${TW}/optimism/info/logo.png`, // OP Sepolia
67
+ 42161: `${TW}/arbitrum/info/logo.png`,
68
+ 421614: `${TW}/arbitrum/info/logo.png`, // Arbitrum Sepolia
69
+ 137: `${TW}/polygon/info/logo.png`,
70
+ 80002: `${TW}/polygon/info/logo.png`, // Polygon Amoy
71
+ 56: `${TW}/smartchain/info/logo.png`,
72
+ };
73
+ /** Network logo for a chain: the curated brand first (clean + testnet-aware), then the backend value. */
74
+ function chainLogoUrl(chainId, apiLogo) {
75
+ return (chainId !== undefined && CHAIN_LOGO[chainId]) || apiLogo || null;
76
+ }
77
+ /** Token logo for a symbol: the backend value first, then the curated map (covers native ETH/SOL/… which the rail returns null for). */
78
+ function currencyLogoUrl(symbol, apiLogo) {
79
+ return apiLogo || (symbol && TOKEN_LOGO[symbol.toUpperCase()]) || null;
80
+ }
57
81
 
58
- export { TOKEN_LOGO, symbolToColor };
82
+ export { TOKEN_LOGO, chainLogoUrl, currencyLogoUrl, symbolToColor };
59
83
  //# sourceMappingURL=logos.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logos.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"logos.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -195,7 +195,7 @@ const useEthereumWalletAssets = ({ assets: hookCustomAssets, multiChain = false,
195
195
  ? ['wallet-assets', 'multi', address, customAssetsMultiChain]
196
196
  : [...openfortKeys.walletAssets(chainId, customAssetsToFetch, address)],
197
197
  queryFn: async () => {
198
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
198
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5;
199
199
  if (multiChain) {
200
200
  if (!address) {
201
201
  throw new OpenfortError('No wallet address available', OpenfortReactErrorType.UNEXPECTED_ERROR);
@@ -292,6 +292,26 @@ const useEthereumWalletAssets = ({ assets: hookCustomAssets, multiChain = false,
292
292
  }
293
293
  }
294
294
  }
295
+ // The ERC-7811 proxy indexes mainnet; testnet natives (e.g. Base Sepolia ETH)
296
+ // come back missing or stale. For configured testnet chains, read the native
297
+ // balance straight from RPC — the same source Rabby uses — and upsert it.
298
+ const testnetChains = chains.filter((c) => c.testnet === true);
299
+ const rpcNatives = await Promise.all(testnetChains.map(async (c) => {
300
+ var _a, _b, _c;
301
+ const rpcUrl = (_c = (_b = (_a = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _a === void 0 ? void 0 : _a.rpcUrls) === null || _b === void 0 ? void 0 : _b[c.id]) !== null && _c !== void 0 ? _c : getDefaultEthereumRpcUrl(c.id);
302
+ const read = await readEvmAssetsViaRpc({ address: address, chain: c, rpcUrl, tokens: [] });
303
+ const native = read.find((a) => a.type === 'native');
304
+ return native ? { ...native, chainId: c.id } : null;
305
+ }));
306
+ for (const native of rpcNatives) {
307
+ if (!native)
308
+ continue;
309
+ const idx = allAssets.findIndex((a) => a.type === 'native' && a.chainId === native.chainId);
310
+ if (idx >= 0)
311
+ allAssets[idx] = native;
312
+ else
313
+ allAssets.push(native);
314
+ }
295
315
  allAssets.sort((a, b) => getUsdValue(b) - getUsdValue(a));
296
316
  return allAssets;
297
317
  }
@@ -389,9 +409,25 @@ const useEthereumWalletAssets = ({ assets: hookCustomAssets, multiChain = false,
389
409
  mergedAssets.push(asset);
390
410
  }
391
411
  });
412
+ // The ERC-7811 proxy indexes mainnet; a testnet native (e.g. Base Sepolia
413
+ // ETH) comes back missing or stale, so the single-chain path would report
414
+ // "no assets" while the multi-chain inventory shows the balance. Read the
415
+ // native straight from RPC and upsert it to keep the two consistent.
416
+ if (chain.testnet === true) {
417
+ const rpcUrl = (_z = (_y = (_x = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _x === void 0 ? void 0 : _x.rpcUrls) === null || _y === void 0 ? void 0 : _y[chainId]) !== null && _z !== void 0 ? _z : getDefaultEthereumRpcUrl(chainId);
418
+ const read = await readEvmAssetsViaRpc({ address: address, chain, rpcUrl, tokens: [] });
419
+ const native = read.find((a) => a.type === 'native');
420
+ if (native) {
421
+ const idx = mergedAssets.findIndex((a) => a.type === 'native');
422
+ if (idx >= 0)
423
+ mergedAssets[idx] = native;
424
+ else
425
+ mergedAssets.unshift(native);
426
+ }
427
+ }
392
428
  if (mergedAssets.length === 0 && customAssetsToFetch.length > 0) {
393
429
  // Proxy succeeded but returned nothing while we expect tokens — read direct.
394
- const rpcUrl = (_z = (_y = (_x = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _x === void 0 ? void 0 : _x.rpcUrls) === null || _y === void 0 ? void 0 : _y[chainId]) !== null && _z !== void 0 ? _z : getDefaultEthereumRpcUrl(chainId);
430
+ const rpcUrl = (_2 = (_1 = (_0 = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _0 === void 0 ? void 0 : _0.rpcUrls) === null || _1 === void 0 ? void 0 : _1[chainId]) !== null && _2 !== void 0 ? _2 : getDefaultEthereumRpcUrl(chainId);
395
431
  const fb = await readEvmAssetsViaRpc({
396
432
  address: address,
397
433
  chain,
@@ -405,7 +441,7 @@ const useEthereumWalletAssets = ({ assets: hookCustomAssets, multiChain = false,
405
441
  }
406
442
  catch {
407
443
  // ERC-7811 asset proxy failed — fall back to direct chain-RPC balance reads.
408
- const rpcUrl = (_2 = (_1 = (_0 = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _0 === void 0 ? void 0 : _0.rpcUrls) === null || _1 === void 0 ? void 0 : _1[chainId]) !== null && _2 !== void 0 ? _2 : getDefaultEthereumRpcUrl(chainId);
444
+ const rpcUrl = (_5 = (_4 = (_3 = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _3 === void 0 ? void 0 : _3.rpcUrls) === null || _4 === void 0 ? void 0 : _4[chainId]) !== null && _5 !== void 0 ? _5 : getDefaultEthereumRpcUrl(chainId);
409
445
  return (await readEvmAssetsViaRpc({
410
446
  address: address,
411
447
  chain,
@@ -1 +1 @@
1
- {"version":3,"file":"useEthereumWalletAssets.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useEthereumWalletAssets.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -20,6 +20,12 @@ export type FundingChain = {
20
20
  };
21
21
  type UseFundingChains = {
22
22
  chains: FundingChain[];
23
+ /**
24
+ * The rail's full deliverable chain list (uncurated). Used to check whether a
25
+ * funding TARGET chain is supported, independent of the source allowlist that
26
+ * narrows {@link chains}.
27
+ */
28
+ railChains: FundingChain[];
23
29
  loading: boolean;
24
30
  error: Error | null;
25
31
  };
@@ -1,6 +1,7 @@
1
1
  import { SDKConfiguration } from '@openfort/openfort-js';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { useOpenfort } from '../../components/Openfort/useOpenfort.js';
4
+ import { getPublishableKeyEnvironment } from '../../utils/validation.js';
4
5
 
5
6
  /**
6
7
  * Sensible default source chains — the common funding origins. Override with
@@ -32,16 +33,24 @@ const DEFAULT_SOURCE_CURRENCIES = ['native', 'USDC', 'USDT'];
32
33
  */
33
34
  function useFundingChains() {
34
35
  var _a, _b, _c, _d, _e;
35
- const { uiConfig } = useOpenfort();
36
+ const { uiConfig, publishableKey } = useOpenfort();
36
37
  // Defaults to the SDK backend (api.openfort.io); override for a custom funding service.
37
38
  const baseUrl = uiConfig.fundingBaseUrl || ((_a = SDKConfiguration.getInstance()) === null || _a === void 0 ? void 0 : _a.backendUrl) || 'https://api.openfort.io';
38
39
  const sourceChains = (_c = (_b = uiConfig.funding) === null || _b === void 0 ? void 0 : _b.sourceChains) !== null && _c !== void 0 ? _c : DEFAULT_SOURCE_CHAINS;
39
40
  const sourceCurrencies = (_e = (_d = uiConfig.funding) === null || _d === void 0 ? void 0 : _d.sourceCurrencies) !== null && _e !== void 0 ? _e : DEFAULT_SOURCE_CURRENCIES;
40
- const [state, setState] = useState({ chains: [], loading: true, error: null });
41
+ // Match the rail host to the key environment: test keys (`pk_test_…`) list the
42
+ // testnet rail, everything else the mainnet rail. The backend picks the same
43
+ // host from the request livemode for the authenticated session endpoints.
44
+ const livemode = getPublishableKeyEnvironment(publishableKey) !== 'test';
45
+ const [state, setState] = useState({
46
+ chains: [],
47
+ loading: true,
48
+ error: null,
49
+ });
41
50
  useEffect(() => {
42
51
  let cancelled = false;
43
52
  setState((s) => ({ ...s, loading: true }));
44
- fetch(`${baseUrl}/v2/funding/chains`)
53
+ fetch(`${baseUrl}/v2/funding/chains?livemode=${livemode}`)
45
54
  .then((r) => {
46
55
  if (!r.ok)
47
56
  throw new Error(`Failed to load chains (${r.status})`);
@@ -59,9 +68,14 @@ function useFundingChains() {
59
68
  return () => {
60
69
  cancelled = true;
61
70
  };
62
- }, [baseUrl]);
71
+ }, [baseUrl, livemode]);
63
72
  // Narrow the provider dictionary to the selected subset (cheap, O(chains)).
64
- return { ...state, chains: curateChains(state.chains, sourceChains, sourceCurrencies) };
73
+ return {
74
+ chains: curateChains(state.chains, sourceChains, sourceCurrencies),
75
+ railChains: state.chains,
76
+ loading: state.loading,
77
+ error: state.error,
78
+ };
65
79
  }
66
80
  /**
67
81
  * Narrow the fetched chains to a selection. `sourceChains` is a CAIP-2 allowlist
@@ -1 +1 @@
1
- {"version":3,"file":"useFundingChains.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useFundingChains.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -17,8 +17,14 @@
17
17
  *
18
18
  * ui.open(); // Opens modal with default route
19
19
  * ui.close(); // Closes modal
20
- * ui.openProfile(); // Opens user profile screen
20
+ * ui.openProfile(); // Opens the connected wallet overview
21
+ * ui.openSend(); // Opens the Send flow
22
+ * ui.openFunding(); // Opens the Deposit (funding) hub
21
23
  * ```
24
+ *
25
+ * The `open*` navigation helpers target connected-only screens. When called while
26
+ * the user is not connected they fall back to the login screen, so a caller can
27
+ * fire them directly and let the modal route the user through auth first.
22
28
  */
23
29
  export declare function useUI(): {
24
30
  isOpen: boolean;
@@ -29,4 +35,10 @@ export declare function useUI(): {
29
35
  openSwitchNetworks: () => void;
30
36
  openProviders: () => void;
31
37
  openWallets: () => void;
38
+ openSend: () => void;
39
+ openReceive: () => void;
40
+ openFunding: () => void;
41
+ openBuy: () => void;
42
+ openExportKey: () => void;
43
+ openSettings: () => void;
32
44
  };
@@ -20,6 +20,12 @@ const safeRoutes = {
20
20
  { route: routes.CONNECTORS, connectType: 'linkIfUserConnectIfNoUser' },
21
21
  routes.ETH_SWITCH_NETWORK,
22
22
  routes.PROVIDERS,
23
+ routes.PROFILE,
24
+ routes.SEND,
25
+ routes.RECEIVE,
26
+ routes.DEPOSIT,
27
+ routes.BUY,
28
+ routes.EXPORT_KEY,
23
29
  ],
24
30
  };
25
31
  /** Route can be selected by string (route name) or by object with `route` property */
@@ -51,8 +57,14 @@ function isAccountId(id) {
51
57
  *
52
58
  * ui.open(); // Opens modal with default route
53
59
  * ui.close(); // Closes modal
54
- * ui.openProfile(); // Opens user profile screen
60
+ * ui.openProfile(); // Opens the connected wallet overview
61
+ * ui.openSend(); // Opens the Send flow
62
+ * ui.openFunding(); // Opens the Deposit (funding) hub
55
63
  * ```
64
+ *
65
+ * The `open*` navigation helpers target connected-only screens. When called while
66
+ * the user is not connected they fall back to the login screen, so a caller can
67
+ * fire them directly and let the modal route the user through auth first.
56
68
  */
57
69
  function useUI() {
58
70
  var _a;
@@ -111,6 +123,12 @@ function useUI() {
111
123
  openSwitchNetworks: () => gotoAndOpen(routes.ETH_SWITCH_NETWORK),
112
124
  openProviders: () => gotoAndOpen(routes.PROVIDERS),
113
125
  openWallets: () => gotoAndOpen({ route: routes.CONNECTORS, connectType: 'linkIfUserConnectIfNoUser' }),
126
+ openSend: () => gotoAndOpen(routes.SEND),
127
+ openReceive: () => gotoAndOpen(routes.RECEIVE),
128
+ openFunding: () => gotoAndOpen(routes.DEPOSIT),
129
+ openBuy: () => gotoAndOpen(routes.BUY),
130
+ openExportKey: () => gotoAndOpen(routes.EXPORT_KEY),
131
+ openSettings: () => gotoAndOpen(routes.PROFILE),
114
132
  };
115
133
  }
116
134
 
@@ -1 +1 @@
1
- {"version":3,"file":"useUI.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useUI.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -29,15 +29,17 @@ function appendPath(base, options, queryParams) {
29
29
  const explorerRegistry = {
30
30
  [ChainTypeEnum.EVM]: (options) => {
31
31
  var _a;
32
+ // Never fall back to an unrelated chain's explorer — a valid hash on the wrong
33
+ // explorer reads as "transaction not found". Return '' so callers hide the link.
32
34
  if (!options.chainId) {
33
- logger.warn('No chain ID provided. Configure explorerUrls in OpenfortProvider for better reliability and rate limits.');
34
- return polygonAmoy.blockExplorers.default.url;
35
+ logger.warn('No chain ID provided; cannot build an explorer URL for this transaction.');
36
+ return '';
35
37
  }
36
38
  const chain = EVM_CHAINS_BY_ID[options.chainId];
37
39
  const explorerUrl = (_a = chain === null || chain === void 0 ? void 0 : chain.blockExplorers) === null || _a === void 0 ? void 0 : _a.default.url;
38
40
  if (!explorerUrl) {
39
- logger.warn(`No explorer URL found for chain ${options.chainId}. Configure explorerUrls in OpenfortProvider for better reliability and rate limits.`);
40
- return polygonAmoy.blockExplorers.default.url;
41
+ logger.warn(`No explorer URL known for chain ${options.chainId}. Configure explorerUrls in OpenfortProvider to enable the link.`);
42
+ return '';
41
43
  }
42
44
  return appendPath(explorerUrl, options);
43
45
  },
@@ -1 +1 @@
1
- {"version":3,"file":"explorer.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"explorer.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1,10 @@
1
1
  export declare const isValidEmail: (email: string) => boolean;
2
+ type PublishableKeyEnvironment = 'test' | 'live';
3
+ /**
4
+ * Derive the environment from an Openfort publishable key.
5
+ *
6
+ * Keys are formatted `pk_test_<uuid>` or `pk_live_<uuid>`. Returns `null` when
7
+ * the prefix is unrecognized so callers can fall back to default behavior.
8
+ */
9
+ export declare const getPublishableKeyEnvironment: (publishableKey: string | undefined | null) => PublishableKeyEnvironment | null;
10
+ export {};
@@ -2,6 +2,19 @@ const isValidEmail = (email) => {
2
2
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3
3
  return emailRegex.test(email);
4
4
  };
5
+ /**
6
+ * Derive the environment from an Openfort publishable key.
7
+ *
8
+ * Keys are formatted `pk_test_<uuid>` or `pk_live_<uuid>`. Returns `null` when
9
+ * the prefix is unrecognized so callers can fall back to default behavior.
10
+ */
11
+ const getPublishableKeyEnvironment = (publishableKey) => {
12
+ if (publishableKey === null || publishableKey === void 0 ? void 0 : publishableKey.startsWith('pk_test_'))
13
+ return 'test';
14
+ if (publishableKey === null || publishableKey === void 0 ? void 0 : publishableKey.startsWith('pk_live_'))
15
+ return 'live';
16
+ return null;
17
+ };
5
18
 
6
- export { isValidEmail };
19
+ export { getPublishableKeyEnvironment, isValidEmail };
7
20
  //# sourceMappingURL=validation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
1
+ {"version":3,"file":"validation.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- export declare const OPENFORT_VERSION = "1.3.0";
1
+ export declare const OPENFORT_VERSION = "1.5.0";
package/build/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const OPENFORT_VERSION = '1.3.0';
1
+ const OPENFORT_VERSION = '1.5.0';
2
2
 
3
3
  export { OPENFORT_VERSION };
4
4
  //# sourceMappingURL=version.js.map
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { motion } from 'framer-motion';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { css } from 'styled-components';
5
- import { useChainId, useSwitchChain } from 'wagmi';
5
+ import { useChainId } from 'wagmi';
6
6
  import Chain from '../../../components/Common/Chain/index.js';
7
7
  import Tooltip from '../../../components/Common/Tooltip/index.js';
8
8
  import { routes } from '../../../components/Openfort/types.js';
@@ -11,6 +11,7 @@ import defaultTheme from '../../../constants/defaultTheme.js';
11
11
  import useLocales from '../../../hooks/useLocales.js';
12
12
  import styled from '../../../styles/styled/index.js';
13
13
  import { isMobile, flattenChildren } from '../../../utils/index.js';
14
+ import { useSwitchChainFiltered } from '../../useSwitchChainFiltered.js';
14
15
  import ChainSelectDropdown from '../ChainSelectDropdown/index.js';
15
16
 
16
17
  const Container = styled(motion.div) ``;
@@ -112,7 +113,7 @@ const ChainSelector = () => {
112
113
  const { open, triggerResize, setRoute } = useOpenfort();
113
114
  const [isOpen, setIsOpen] = useState(false);
114
115
  const chainId = useChainId();
115
- const { chains } = useSwitchChain();
116
+ const { chains } = useSwitchChainFiltered();
116
117
  const chain = chains.find((c) => c.id === chainId);
117
118
  const locales = useLocales({
118
119
  CHAIN: (_a = chain === null || chain === void 0 ? void 0 : chain.name) !== null && _a !== void 0 ? _a : 'UNKNOWN',
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}