@openfort/react 1.5.0 → 1.5.1

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.
@@ -5,6 +5,7 @@ import { useEthereumEmbeddedWallet } from '../../../ethereum/hooks/useEthereumEm
5
5
  import { useEthereumWalletAssets } from '../../../ethereum/hooks/useEthereumWalletAssets.js';
6
6
  import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
7
7
  import { useSolanaEmbeddedWallet } from '../../../solana/hooks/useSolanaEmbeddedWallet.js';
8
+ import { getPublishableKeyEnvironment } from '../../../utils/validation.js';
8
9
  import Button from '../../Common/Button/index.js';
9
10
  import { ModalHeading, ModalBody } from '../../Common/Modal/styles.js';
10
11
  import { routes } from '../../Openfort/types.js';
@@ -22,6 +23,9 @@ import { isSameToken, getAssetSymbol } from '../Send/utils.js';
22
23
  const BuySelectProvider = () => {
23
24
  const { buyForm, setBuyForm, setRoute, triggerResize, publishableKey } = useOpenfort();
24
25
  const { chainType } = useOpenfortCore();
26
+ // Card / Apple Pay settle on mainnet, so a test key can't deliver — block both
27
+ // providers (and Continue) with a clear reason while keeping the screen reachable.
28
+ const testnet = getPublishableKeyEnvironment(publishableKey) === 'test';
25
29
  // Use chain-specific hooks
26
30
  const ethereumWallet = useEthereumEmbeddedWallet();
27
31
  const solanaWallet = useSolanaEmbeddedWallet();
@@ -220,6 +224,10 @@ const BuySelectProvider = () => {
220
224
  providerFeePercentage = fiatAmount ? ((totalFees / fiatAmount) * 100).toFixed(2) : null;
221
225
  }
222
226
  }
227
+ if (testnet) {
228
+ isDisabled = true;
229
+ disabledReason = 'Not on testnet';
230
+ }
223
231
  // Use real quote data if available, otherwise show loading or fallback
224
232
  const netDisplay = isDisabled
225
233
  ? disabledReason
@@ -241,7 +249,7 @@ const BuySelectProvider = () => {
241
249
  const metaText = isDisabled ? '' : `Fee ${isEstimated ? '~' : ''}${feePercentage}%`;
242
250
  const isActive = buyForm.providerId === provider.id;
243
251
  return (jsxs(ProviderButton, { type: "button", onClick: () => !isDisabled && handleSelectProvider(provider.id), "$active": isActive, disabled: isDisabled, children: [jsxs(ProviderInfo, { children: [jsxs(ProviderNameRow, { children: [jsx(ProviderName, { children: provider.name }), highlight && !isDisabled ? jsx(ProviderBadge, { children: highlight }) : null] }), jsx(ProviderMeta, { children: metaText })] }), jsxs(ProviderRight, { children: [jsx(ProviderQuote, { children: fiatDisplay }), jsx(ProviderFiat, { children: netDisplay })] })] }, provider.id));
244
- }) }), jsxs(ContinueButtonWrapper, { children: [jsx(Button, { variant: "secondary", onClick: handleBack, children: "Back" }), jsx(Button, { variant: "primary", onClick: handleContinue, disabled: step2Disabled, children: "Continue" })] })] }));
252
+ }) }), jsxs(ContinueButtonWrapper, { children: [jsx(Button, { variant: "secondary", onClick: handleBack, children: "Back" }), jsx(Button, { variant: "primary", onClick: handleContinue, disabled: step2Disabled || testnet, children: "Continue" })] })] }));
245
253
  };
246
254
 
247
255
  export { BuySelectProvider as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -10,3 +10,12 @@ export declare function UnsupportedNetworkNotice({ targetChain, railChains, }: {
10
10
  targetChain: string;
11
11
  railChains: FundingChain[];
12
12
  }): import("react/jsx-runtime").JSX.Element;
13
+ /**
14
+ * Shown in the deposit flow when funds would settle on the target chain but the active
15
+ * embedded account can't operate there — a smart or delegated account deployed only on
16
+ * other chains. EOAs share one address across EVM chains, so they never hit this.
17
+ * Guides the user to set up an account usable on the target chain.
18
+ */
19
+ export declare function AccountChainNotice({ targetChain }: {
20
+ targetChain: string;
21
+ }): import("react/jsx-runtime").JSX.Element;
@@ -92,6 +92,16 @@ function UnsupportedNetworkNotice({ targetChain, railChains, }) {
92
92
  ? "Solana isn't supported by the funding rail in test mode. Switch to a supported EVM testnet to add funds."
93
93
  : "The funding rail can't deliver to this network. Switch to a supported one to add funds." }), bridge && jsx(SupportedChainSwitcher, { railChains: railChains })] }));
94
94
  }
95
+ /**
96
+ * Shown in the deposit flow when funds would settle on the target chain but the active
97
+ * embedded account can't operate there — a smart or delegated account deployed only on
98
+ * other chains. EOAs share one address across EVM chains, so they never hit this.
99
+ * Guides the user to set up an account usable on the target chain.
100
+ */
101
+ function AccountChainNotice({ targetChain }) {
102
+ const name = chainName(targetChain);
103
+ return (jsxs(Card, { children: [jsxs(Title, { children: ["Your account isn't available on ", name] }), jsxs(Body, { children: ["Deposits settle on ", name, ", but your active account isn't set up there. Create an EOA, or a smart or delegated account on ", name, ", then deposit again."] })] }));
104
+ }
95
105
  /**
96
106
  * EVM-only: the rail-supported chains the wallet can switch to. Rendered only when
97
107
  * an Ethereum bridge is present (wagmi), so the wagmi hook below never runs in a
@@ -107,5 +117,5 @@ function SupportedChainSwitcher({ railChains }) {
107
117
  }) }) })] }));
108
118
  }
109
119
 
110
- export { UnsupportedNetworkNotice };
120
+ export { AccountChainNotice, UnsupportedNetworkNotice };
111
121
  //# sourceMappingURL=UnsupportedNetworkNotice.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UnsupportedNetworkNotice.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"UnsupportedNetworkNotice.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -7,7 +7,6 @@ import { useFunding } from '../../../hooks/openfort/useFunding.js';
7
7
  import { useFundingChains } from '../../../hooks/openfort/useFundingChains.js';
8
8
  import useIsMobile from '../../../hooks/useIsMobile.js';
9
9
  import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
10
- import { getPublishableKeyEnvironment } from '../../../utils/validation.js';
11
10
  import { ModalHeading } from '../../Common/Modal/styles.js';
12
11
  import PoweredByFooter from '../../Common/PoweredByFooter/index.js';
13
12
  import { FundingMethod, routes } from '../../Openfort/types.js';
@@ -52,7 +51,7 @@ const hideBrokenLogo = (e) => {
52
51
  */
53
52
  const Deposit = () => {
54
53
  var _a;
55
- const { setRoute, setBuyForm, uiConfig, publishableKey, triggerResize } = useOpenfort();
54
+ const { setRoute, setBuyForm, uiConfig, triggerResize } = useOpenfort();
56
55
  const { chainType } = useOpenfortCore();
57
56
  const isMobile = useIsMobile();
58
57
  const { isAvailable } = useFunding();
@@ -70,7 +69,6 @@ const Deposit = () => {
70
69
  const options = getPaymentOptions({
71
70
  isMobile,
72
71
  fundingAvailable: isAvailable,
73
- testnet: getPublishableKeyEnvironment(publishableKey) === 'test',
74
72
  methods: (_a = uiConfig.funding) === null || _a === void 0 ? void 0 : _a.methods,
75
73
  });
76
74
  // Distinct source-currency logos (USDC, USDT, ETH, …) for the "from address" row.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -28,12 +28,6 @@ type PaymentOptionsContext = {
28
28
  isMobile: boolean;
29
29
  /** When the funding backend is unavailable, crypto/CEX rows are disabled. */
30
30
  fundingAvailable: boolean;
31
- /**
32
- * Test-key (`pk_test_…`) project. The fiat (card/Apple Pay) and exchange rails
33
- * settle real money on mainnet only — Stripe/Coinbase can't deliver to a testnet
34
- * wallet — so they're disabled here. The crypto rails (Relay testnet) stay on.
35
- */
36
- testnet?: boolean;
37
31
  /**
38
32
  * Integrator-selected methods, in display order. When set, only these show
39
33
  * (still subject to device/availability gating). Omit to show all.
@@ -52,11 +52,9 @@ function getPaymentOptions(ctx) {
52
52
  : visible;
53
53
  return ordered.map((method) => {
54
54
  const kind = method.target.kind;
55
- // Fiat (card/Apple Pay) and exchange rails move real money and only settle on
56
- // mainnet, so a test-key project can't use them disable with a clear reason.
57
- if (ctx.testnet && (kind === 'buy' || kind === 'cex')) {
58
- return { ...method, disabled: true, disabledReason: 'Not available on testnet' };
59
- }
55
+ // Fiat (card/Apple Pay) and exchange rails stay visible on testnet so the demo
56
+ // shows the full feature set; the final pay action is blocked downstream
57
+ // (BuyProcessing / DepositCex) with a testnet notice, since they settle on mainnet.
60
58
  const fundingRail = kind === 'crypto' || kind === 'wallet' || kind === 'cex';
61
59
  if (fundingRail && !ctx.fundingAvailable) {
62
60
  return { ...method, disabled: true, disabledReason: 'Coming soon' };
@@ -1 +1 @@
1
- {"version":3,"file":"paymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"paymentOptions.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -26,6 +26,7 @@ export declare function useDepositRoute(kind: DepositRouteKind): {
26
26
  receiverAddress: string | null;
27
27
  sameChain: boolean;
28
28
  targetUnsupported: boolean;
29
+ accountUnusableOnTarget: boolean;
29
30
  railChains: FundingChain[];
30
31
  status: import("../../../hooks/openfort/useFunding").SessionStatus | "idle";
31
32
  loading: boolean;
@@ -1,3 +1,4 @@
1
+ import { AccountTypeEnum } from '@openfort/openfort-js';
1
2
  import { useState, useRef, useEffect } from 'react';
2
3
  import { useEthereumEmbeddedWallet } from '../../../ethereum/hooks/useEthereumEmbeddedWallet.js';
3
4
  import { useFunding } from '../../../hooks/openfort/useFunding.js';
@@ -14,6 +15,22 @@ function paymentMethodFor(chain, currency) {
14
15
  // funding API has no separate 'cex' type, so every source resolves to evm/solana.
15
16
  return { type: isSolana(chain) ? 'solana' : 'evm', source };
16
17
  }
18
+ /**
19
+ * Whether the embedded account can transact on `chainId`. EOAs share one address
20
+ * across EVM chains, so they're always usable; smart/delegated accounts are per-chain
21
+ * deployments, usable only on a chain they're deployed on. Returns true when the type
22
+ * or deployments are unknown — the deposit flow only blocks when it's certain.
23
+ */
24
+ function accountUsableOnChain(wallet, chainId) {
25
+ if (!wallet || wallet.accountType == null)
26
+ return true;
27
+ if (wallet.accountType === AccountTypeEnum.EOA)
28
+ return true;
29
+ const chainScoped = wallet.accounts.filter((a) => a.chainId != null);
30
+ if (chainScoped.length === 0)
31
+ return true;
32
+ return chainScoped.some((a) => a.chainId === chainId);
33
+ }
17
34
  /**
18
35
  * Shared state for a deposit route: the source chain/currency selection (sourced
19
36
  * live from Relay via {@link useFundingChains}) plus the resolved deposit
@@ -21,7 +38,7 @@ function paymentMethodFor(chain, currency) {
21
38
  * address" page build on this — they differ only in the lead buttons.
22
39
  */
23
40
  function useDepositRoute(kind) {
24
- var _a, _b, _c, _d, _e, _f, _g, _h;
41
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
25
42
  const ethWallet = useEthereumEmbeddedWallet();
26
43
  const solWallet = useSolanaEmbeddedWallet();
27
44
  const { session, status, error, loading, isAvailable, fund, payLink, reset } = useFunding();
@@ -38,30 +55,38 @@ function useDepositRoute(kind) {
38
55
  // Coinbase Onramp, which only delivers to a fixed set of EVM chains.
39
56
  const chains = kind === 'cex' ? allChains.filter((c) => isCexDeliverable(c.id)) : allChains;
40
57
  // Where funds land: the active embedded wallet (the Relay deposit recipient).
41
- const address = wallet.status === 'connected' ? wallet.address : undefined;
58
+ // Use the address as soon as the wallet exposes one even mid-(re)connect or
59
+ // pending recovery — since receiving a deposit needs only the address, not a live
60
+ // signer. Gating on 'connected' left the page blank during those states.
61
+ const address = (_a = wallet.address) !== null && _a !== void 0 ? _a : undefined;
42
62
  const [chainId, setChainId] = useState('');
43
63
  const [currencySymbol, setCurrencySymbol] = useState('');
44
64
  const lastKey = useRef('');
45
65
  // Derive the active selection, falling back to the first available chain/currency
46
66
  // so the picker is valid before the user touches it and as chains load in.
47
- const activeChain = (_a = chains.find((c) => c.id === chainId)) !== null && _a !== void 0 ? _a : chains[0];
48
- const currencies = (_b = activeChain === null || activeChain === void 0 ? void 0 : activeChain.currencies) !== null && _b !== void 0 ? _b : [];
49
- const activeCurrency = (_c = currencies.find((c) => c.symbol === currencySymbol)) !== null && _c !== void 0 ? _c : currencies[0];
50
- const chain = (_d = activeChain === null || activeChain === void 0 ? void 0 : activeChain.id) !== null && _d !== void 0 ? _d : target.chain;
67
+ const activeChain = (_b = chains.find((c) => c.id === chainId)) !== null && _b !== void 0 ? _b : chains[0];
68
+ const currencies = (_c = activeChain === null || activeChain === void 0 ? void 0 : activeChain.currencies) !== null && _c !== void 0 ? _c : [];
69
+ const activeCurrency = (_d = currencies.find((c) => c.symbol === currencySymbol)) !== null && _d !== void 0 ? _d : currencies[0];
70
+ const chain = (_e = activeChain === null || activeChain === void 0 ? void 0 : activeChain.id) !== null && _e !== void 0 ? _e : target.chain;
51
71
  const sameChain = chain === target.chain;
52
72
  // The rail only delivers to chains in its list; if the active funding TARGET (the
53
73
  // embedded wallet's chain — e.g. Polygon Amoy or a Solana testnet) isn't one of
54
74
  // them, there's no route. Don't call Relay (it would 400 with a cryptic "invalid
55
75
  // currency"); the page prompts a switch to a supported chain instead.
56
76
  const targetUnsupported = !chainsLoading && railChains.length > 0 && !railChains.some((c) => c.id === target.chain);
77
+ // Funds settle on the TARGET chain. A smart/delegated account not deployed there
78
+ // can't use them, so block and guide the user to set up a usable account. EVM only —
79
+ // the recipient family already matches the target, and EOAs are always usable.
80
+ const targetChainId = isSolana(target.chain) ? null : Number(target.chain.split(':')[1]);
81
+ const accountUnusableOnTarget = targetChainId != null && !accountUsableOnChain(ethWallet.activeWallet, targetChainId);
57
82
  const receiverAddress = sameChain
58
83
  ? (address !== null && address !== void 0 ? address : null)
59
- : ((_f = (_e = session === null || session === void 0 ? void 0 : session.paymentMethod) === null || _e === void 0 ? void 0 : _e.receiverAddress) !== null && _f !== void 0 ? _f : null);
60
- const pm = (_g = session === null || session === void 0 ? void 0 : session.paymentMethod) !== null && _g !== void 0 ? _g : null;
84
+ : ((_g = (_f = session === null || session === void 0 ? void 0 : session.paymentMethod) === null || _f === void 0 ? void 0 : _f.receiverAddress) !== null && _g !== void 0 ? _g : null);
85
+ const pm = (_h = session === null || session === void 0 ? void 0 : session.paymentMethod) !== null && _h !== void 0 ? _h : null;
61
86
  useEffect(() => {
62
87
  if (!address || !isAvailable || !activeChain || !activeCurrency)
63
88
  return;
64
- if (targetUnsupported) {
89
+ if (targetUnsupported || accountUnusableOnTarget) {
65
90
  lastKey.current = '';
66
91
  reset();
67
92
  return;
@@ -89,6 +114,7 @@ function useDepositRoute(kind) {
89
114
  isAvailable,
90
115
  sameChain,
91
116
  targetUnsupported,
117
+ accountUnusableOnTarget,
92
118
  fund,
93
119
  reset,
94
120
  target.chain,
@@ -106,7 +132,7 @@ function useDepositRoute(kind) {
106
132
  chainsLoading,
107
133
  chain,
108
134
  setChain: setChainId,
109
- currency: (_h = activeCurrency === null || activeCurrency === void 0 ? void 0 : activeCurrency.symbol) !== null && _h !== void 0 ? _h : '',
135
+ currency: (_j = activeCurrency === null || activeCurrency === void 0 ? void 0 : activeCurrency.symbol) !== null && _j !== void 0 ? _j : '',
110
136
  setCurrency: setCurrencySymbol,
111
137
  currencies,
112
138
  activeChain,
@@ -117,6 +143,7 @@ function useDepositRoute(kind) {
117
143
  receiverAddress,
118
144
  sameChain,
119
145
  targetUnsupported,
146
+ accountUnusableOnTarget,
120
147
  railChains,
121
148
  status,
122
149
  loading,
@@ -1 +1 @@
1
- {"version":3,"file":"useDepositRoute.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useDepositRoute.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,7 +8,9 @@ import { useFundingChains } from '../../../hooks/openfort/useFundingChains.js';
8
8
  import { invalidateBalance } from '../../../hooks/useBalance.js';
9
9
  import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
10
10
  import { logger } from '../../../utils/logger.js';
11
+ import { getPublishableKeyEnvironment } from '../../../utils/validation.js';
11
12
  import { ModalHeading, ModalBody } from '../../Common/Modal/styles.js';
13
+ import Tooltip from '../../Common/Tooltip/index.js';
12
14
  import { routes } from '../../Openfort/types.js';
13
15
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
14
16
  import { PageContent } from '../../PageContent/index.js';
@@ -56,7 +58,10 @@ const hideBrokenLogo = (e) => {
56
58
  */
57
59
  const DepositCex = () => {
58
60
  var _a, _b, _c, _d, _e, _f;
59
- const { triggerResize } = useOpenfort();
61
+ const { triggerResize, publishableKey } = useOpenfort();
62
+ // Coinbase onramp settles real funds on mainnet, so a test key can't deliver here.
63
+ // Keep the button live for the demo but block the hand-off with a testnet notice.
64
+ const testnet = getPublishableKeyEnvironment(publishableKey) === 'test';
60
65
  const target = useFundingTarget();
61
66
  // CEX (Coinbase pay-link + session) is served by the Openfort API, not the
62
67
  // standalone funding service — resolve this rail's base URL from the API backend.
@@ -100,7 +105,9 @@ const DepositCex = () => {
100
105
  const createSessionRef = useRef(createSession);
101
106
  createSessionRef.current = createSession;
102
107
  useEffect(() => {
103
- if (!isAvailable || !address || !chainSupported)
108
+ // No session on testnet — Coinbase can't settle to a testnet wallet and the
109
+ // button below is blocked anyway; skip the mint so we don't fire a doomed call.
110
+ if (!isAvailable || !address || !chainSupported || testnet)
104
111
  return;
105
112
  const key = `${target.chain}|${target.currency}|${address}`;
106
113
  if (sessionKey.current === key)
@@ -125,7 +132,7 @@ const DepositCex = () => {
125
132
  // without this the guard above would block the retry after this cancel.
126
133
  sessionKey.current = '';
127
134
  };
128
- }, [isAvailable, address, chainSupported, target.chain, target.currency]);
135
+ }, [isAvailable, address, chainSupported, testnet, target.chain, target.currency]);
129
136
  const fiatAmount = useMemo(() => {
130
137
  const normalized = sanitizeForParsing(sanitizeAmountInput(amount));
131
138
  if (!normalized)
@@ -206,14 +213,25 @@ const DepositCex = () => {
206
213
  // success / refunded / expired screen (shared with the crypto rail).
207
214
  if (isDepositFlowActive(status))
208
215
  return jsx(DepositProgress, { status: status });
209
- return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from Exchange" }), jsxs(Section, { children: [jsx(SectionLabel, { children: "Amount" }), jsxs(AmountCard, { children: [jsx(CurrencySymbol, { children: "$" }), jsx(AmountInput, { value: amount, onChange: handleAmountChange, onBlur: handleAmountBlur, placeholder: "0.00", inputMode: "decimal", autoComplete: "off" })] }), jsx(PresetList, { children: PRESETS.map((preset) => (jsxs(PresetButton, { type: "button", "$active": pressedPreset === preset, onClick: () => handlePreset(preset), children: ["$", preset] }, preset))) }), amountTooLow ? (jsxs("span", { style: errorHelper, children: ["Enter at least $", MIN_AMOUNT, ".00 \u2014 the Coinbase minimum."] })) : (jsxs("span", { style: helperText, children: ["Minimum $", MIN_AMOUNT, ".00"] })), chainSupported && (jsxs("span", { style: destinationRow, children: [destAssetLogo && jsx("img", { src: destAssetLogo, alt: "", style: destinationLogo, onError: hideBrokenLogo }), destChainLogo && jsx("img", { src: destChainLogo, alt: "", style: destinationLogo, onError: hideBrokenLogo })] }))] }), !isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), isAvailable && !chainSupported && jsxs(ModalBody, { children: ["Coinbase can't deliver to ", destChainName, " yet."] }), error && jsx(ModalBody, { style: { color: '#dc2626' }, children: error.message }), jsx(StepDivider, { children: "Then open an exchange" }), jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 12 }, children: EXCHANGES.map((ex) => ex.comingSoon ? (jsxs("button", { type: "button", disabled: true, style: {
216
+ return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from Exchange" }), jsxs(Section, { children: [jsx(SectionLabel, { children: "Amount" }), jsxs(AmountCard, { children: [jsx(CurrencySymbol, { children: "$" }), jsx(AmountInput, { value: amount, onChange: handleAmountChange, onBlur: handleAmountBlur, placeholder: "0.00", inputMode: "decimal", autoComplete: "off" })] }), jsx(PresetList, { children: PRESETS.map((preset) => (jsxs(PresetButton, { type: "button", "$active": pressedPreset === preset, onClick: () => handlePreset(preset), children: ["$", preset] }, preset))) }), amountTooLow ? (jsxs("span", { style: errorHelper, children: ["Enter at least $", MIN_AMOUNT, ".00 \u2014 the Coinbase minimum."] })) : (jsxs("span", { style: helperText, children: ["Minimum $", MIN_AMOUNT, ".00"] })), chainSupported && (jsxs("span", { style: destinationRow, children: [destAssetLogo && jsx("img", { src: destAssetLogo, alt: "", style: destinationLogo, onError: hideBrokenLogo }), destChainLogo && jsx("img", { src: destChainLogo, alt: "", style: destinationLogo, onError: hideBrokenLogo })] }))] }), !isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), !testnet && isAvailable && !chainSupported && (jsxs(ModalBody, { children: ["Coinbase can't deliver to ", destChainName, " yet."] })), !testnet && error && jsx(ModalBody, { style: { color: '#dc2626' }, children: error.message }), jsx(StepDivider, { children: "Then open an exchange" }), jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 12 }, children: EXCHANGES.map((ex) => ex.comingSoon ? (jsxs("button", { type: "button", disabled: true, style: {
210
217
  ...walletListBtn,
211
218
  display: 'flex',
212
219
  alignItems: 'center',
213
220
  gap: 8,
214
221
  opacity: 0.55,
215
222
  cursor: 'not-allowed',
216
- }, children: [jsx(ButtonLogo, { children: EXCHANGE_LOGO[ex.id] }), jsx("span", { children: titleCase(ex.id) }), jsx("span", { style: { marginLeft: 'auto', fontSize: 11, fontWeight: 600 }, children: "Coming soon" })] }, ex.id)) : (jsxs("button", { type: "button", disabled: !payReady, style: {
223
+ }, children: [jsx(ButtonLogo, { children: EXCHANGE_LOGO[ex.id] }), jsx("span", { children: titleCase(ex.id) }), jsx("span", { style: { marginLeft: 'auto', fontSize: 11, fontWeight: 600 }, children: "Coming soon" })] }, ex.id)) : testnet ? (
224
+ // Blocked on testnet (Coinbase settles on mainnet). Use aria-disabled, not
225
+ // `disabled`, so the hover still fires the tooltip that explains why.
226
+ jsx(Tooltip, { message: "Coinbase settles on mainnet \u2014 not available on testnet.", children: jsxs("button", { type: "button", "aria-disabled": "true", style: {
227
+ ...walletListBtn,
228
+ display: 'flex',
229
+ alignItems: 'center',
230
+ justifyContent: 'center',
231
+ gap: 8,
232
+ opacity: 0.55,
233
+ cursor: 'not-allowed',
234
+ }, children: [jsx(ButtonLogo, { children: EXCHANGE_LOGO[ex.id] }), `Open ${titleCase(ex.id)} ↗`] }) }, ex.id)) : (jsxs("button", { type: "button", disabled: !payReady, style: {
217
235
  ...walletListBtn,
218
236
  display: 'flex',
219
237
  alignItems: 'center',
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -12,7 +12,7 @@ import { SameChainDepositStatus } from '../Deposit/SameChainDepositStatus.js';
12
12
  import { SameChainDepositSuccess } from '../Deposit/SameChainDepositSuccess.js';
13
13
  import { isSolana } from '../Deposit/sources.js';
14
14
  import { TestnetNotice } from '../Deposit/TestnetNotice.js';
15
- import { UnsupportedNetworkNotice } from '../Deposit/UnsupportedNetworkNotice.js';
15
+ import { UnsupportedNetworkNotice, AccountChainNotice } from '../Deposit/UnsupportedNetworkNotice.js';
16
16
  import { useDepositRoute } from '../Deposit/useDepositRoute.js';
17
17
  import { useSameChainArrival } from '../Deposit/useSameChainArrival.js';
18
18
  import { caipToChainId } from '../DepositWallet/walletDeeplinks.js';
@@ -45,7 +45,7 @@ const DepositCrypto = () => {
45
45
  return jsx(DepositProgress, { status: route.status });
46
46
  if (sameChainEnabled && arrived)
47
47
  return jsx(SameChainDepositSuccess, { address: sameChainAddress, chainId: sameChainId });
48
- return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from address" }), jsx(TestnetNotice, {}), route.targetUnsupported ? (jsx(UnsupportedNetworkNotice, { targetChain: route.target.chain, railChains: route.railChains })) : (jsxs(Fragment, { children: [jsx(RouteSelectors, { chains: route.chains, chain: route.chain, currency: route.currency, chainLabel: "Supported chain", onChainChange: route.setChain, onCurrencyChange: route.setCurrency }), !route.isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), jsx(DepositAddressBlock, { assetLogo: currencyLogoUrl((_b = route.activeCurrency) === null || _b === void 0 ? void 0 : _b.symbol, (_c = route.activeCurrency) === null || _c === void 0 ? void 0 : _c.logo), chainLogo: chainLogoUrl(caipToChainId((_d = route.activeChain) === null || _d === void 0 ? void 0 : _d.id), (_e = route.activeChain) === null || _e === void 0 ? void 0 : _e.logo), receiverAddress: route.receiverAddress, pm: route.pm, sourceCurrency: route.activeCurrency
48
+ return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from address" }), jsx(TestnetNotice, {}), route.targetUnsupported ? (jsx(UnsupportedNetworkNotice, { targetChain: route.target.chain, railChains: route.railChains })) : route.accountUnusableOnTarget ? (jsx(AccountChainNotice, { targetChain: route.target.chain })) : (jsxs(Fragment, { children: [jsx(RouteSelectors, { chains: route.chains, chain: route.chain, currency: route.currency, chainLabel: "Supported chain", onChainChange: route.setChain, onCurrencyChange: route.setCurrency }), !route.isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), jsx(DepositAddressBlock, { assetLogo: currencyLogoUrl((_b = route.activeCurrency) === null || _b === void 0 ? void 0 : _b.symbol, (_c = route.activeCurrency) === null || _c === void 0 ? void 0 : _c.logo), chainLogo: chainLogoUrl(caipToChainId((_d = route.activeChain) === null || _d === void 0 ? void 0 : _d.id), (_e = route.activeChain) === null || _e === void 0 ? void 0 : _e.logo), receiverAddress: route.receiverAddress, pm: route.pm, sourceCurrency: route.activeCurrency
49
49
  ? { symbol: route.activeCurrency.symbol, decimals: route.activeCurrency.decimals }
50
50
  : null, sameChain: route.sameChain, loading: route.loading, status: route.status }), sameChainEnabled && jsx(SameChainDepositStatus, {}), route.error && jsx(ModalBody, { style: { color: '#dc2626', marginTop: 12 }, children: route.error.message })] }))] }));
51
51
  };
@@ -19,7 +19,7 @@ import { RouteSelectors } from '../Deposit/RouteSelectors.js';
19
19
  import { isSolana } from '../Deposit/sources.js';
20
20
  import { StepDivider, Skeleton, ButtonLogo } from '../Deposit/styles.js';
21
21
  import { TestnetNotice } from '../Deposit/TestnetNotice.js';
22
- import { UnsupportedNetworkNotice } from '../Deposit/UnsupportedNetworkNotice.js';
22
+ import { UnsupportedNetworkNotice, AccountChainNotice } from '../Deposit/UnsupportedNetworkNotice.js';
23
23
  import { useDepositRoute } from '../Deposit/useDepositRoute.js';
24
24
  import { sanitizeAmountInput } from '../Send/utils.js';
25
25
  import { DepositWalletDesktop } from './DepositWalletDesktop.js';
@@ -130,7 +130,7 @@ const DepositWallet = () => {
130
130
  }, [route.receiverAddress, route.loading, route.status, deeplinks.length, triggerResize]);
131
131
  if (isDepositFlowActive(route.status))
132
132
  return jsx(DepositProgress, { status: route.status });
133
- return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from wallet" }), jsx(TestnetNotice, {}), route.targetUnsupported && (jsx(UnsupportedNetworkNotice, { targetChain: route.target.chain, railChains: route.railChains })), !route.targetUnsupported && (jsxs(Layout, { children: [jsxs(TopFixed, { children: [jsx(RouteSelectors, { chains: route.chains, chain: route.chain, currency: route.currency, chainLabel: "Supported chain", onChainChange: route.setChain, onCurrencyChange: route.setCurrency }), !route.isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), jsxs(Section, { children: [jsx(SectionLabel, { children: "Amount" }), jsxs(AmountCard, { children: [jsx(CurrencySymbol, { children: (_m = (_l = route.activeCurrency) === null || _l === void 0 ? void 0 : _l.symbol) !== null && _m !== void 0 ? _m : '' }), jsx(AmountInput, { value: amount, onChange: handleAmountChange, placeholder: "0.00", inputMode: "decimal", autoComplete: "off" })] }), jsx(PresetList, { children: PRESETS.map((preset) => (jsx(PresetButton, { type: "button", "$active": pressedPreset === preset, onClick: () => handlePreset(preset), children: preset }, preset))) })] }), jsx(StepDivider, { children: "Then select the wallet you want to use" })] }), jsx(ProvidersRegion, { children: isMobile || isSolanaSrc || !bridge ? (jsxs(Fragment, { children: [!depositPageUrl && (jsx(ModalBody, { style: { marginTop: 12 }, children: "Use a deposit address below to fund from your wallet." })), route.loading && !route.pm && (jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [jsx(Skeleton, { "$h": "44px", "$r": "10px" }), jsx(Skeleton, { "$h": "44px", "$r": "10px" }), jsx(Skeleton, { "$h": "44px", "$r": "10px" })] })), deeplinks.length > 0 && (jsx(ScrollArea, { fill: true, children: jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: deeplinks.map((d) => (jsxs("a", { href: amountValid ? d.url : undefined, "aria-disabled": !amountValid, target: "_blank", rel: "noreferrer", style: {
133
+ return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from wallet" }), jsx(TestnetNotice, {}), route.targetUnsupported && (jsx(UnsupportedNetworkNotice, { targetChain: route.target.chain, railChains: route.railChains })), !route.targetUnsupported && route.accountUnusableOnTarget && (jsx(AccountChainNotice, { targetChain: route.target.chain })), !route.targetUnsupported && !route.accountUnusableOnTarget && (jsxs(Layout, { children: [jsxs(TopFixed, { children: [jsx(RouteSelectors, { chains: route.chains, chain: route.chain, currency: route.currency, chainLabel: "Supported chain", onChainChange: route.setChain, onCurrencyChange: route.setCurrency }), !route.isAvailable && jsx(ModalBody, { children: "Funding isn't available right now." }), jsxs(Section, { children: [jsx(SectionLabel, { children: "Amount" }), jsxs(AmountCard, { children: [jsx(CurrencySymbol, { children: (_m = (_l = route.activeCurrency) === null || _l === void 0 ? void 0 : _l.symbol) !== null && _m !== void 0 ? _m : '' }), jsx(AmountInput, { value: amount, onChange: handleAmountChange, placeholder: "0.00", inputMode: "decimal", autoComplete: "off" })] }), jsx(PresetList, { children: PRESETS.map((preset) => (jsx(PresetButton, { type: "button", "$active": pressedPreset === preset, onClick: () => handlePreset(preset), children: preset }, preset))) })] }), jsx(StepDivider, { children: "Then select the wallet you want to use" })] }), jsx(ProvidersRegion, { children: isMobile || isSolanaSrc || !bridge ? (jsxs(Fragment, { children: [!depositPageUrl && (jsx(ModalBody, { style: { marginTop: 12 }, children: "Use a deposit address below to fund from your wallet." })), route.loading && !route.pm && (jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: [jsx(Skeleton, { "$h": "44px", "$r": "10px" }), jsx(Skeleton, { "$h": "44px", "$r": "10px" }), jsx(Skeleton, { "$h": "44px", "$r": "10px" })] })), deeplinks.length > 0 && (jsx(ScrollArea, { fill: true, children: jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 8 }, children: deeplinks.map((d) => (jsxs("a", { href: amountValid ? d.url : undefined, "aria-disabled": !amountValid, target: "_blank", rel: "noreferrer", style: {
134
134
  ...walletListBtn,
135
135
  display: 'flex',
136
136
  alignItems: 'center',
@@ -1 +1 @@
1
- export declare const OPENFORT_VERSION = "1.5.0";
1
+ export declare const OPENFORT_VERSION = "1.5.1";
package/build/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const OPENFORT_VERSION = '1.5.0';
1
+ const OPENFORT_VERSION = '1.5.1';
2
2
 
3
3
  export { OPENFORT_VERSION };
4
4
  //# sourceMappingURL=version.js.map
@@ -134,7 +134,7 @@ const ChainSelector = () => {
134
134
  else {
135
135
  setIsOpen(!isOpen);
136
136
  }
137
- }, children: [disabled ? (jsx(Tooltip, { message: locales.chainNetwork, xOffset: -6, delay: 0.01, children: jsx(Chain, { id: chain === null || chain === void 0 ? void 0 : chain.id }) })) : (jsx(Chain, { id: chain === null || chain === void 0 ? void 0 : chain.id })), !disabled && jsx(ChevronDown, { style: { top: 1, left: -3 } })] })) }) }));
137
+ }, children: [jsx(Tooltip, { message: locales.chainNetwork, xOffset: -6, delay: 0.01, children: jsx(Chain, { id: chain === null || chain === void 0 ? void 0 : chain.id }) }), !disabled && jsx(ChevronDown, { style: { top: 1, left: -3 } })] })) }) }));
138
138
  };
139
139
 
140
140
  export { ChainSelector as default };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfort/react",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "author": "Openfort (https://www.openfort.io)",
5
5
  "license": "BSD-2-Clause license",
6
6
  "description": "The easiest way to integrate Openfort to your project.",