@openfort/react 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/Common/Chain/styles.d.ts +10 -0
- package/build/components/Common/Chain/styles.js +103 -1
- package/build/components/Common/Chain/styles.js.map +1 -1
- package/build/components/Common/Modal/index.js +2 -0
- package/build/components/Common/Modal/index.js.map +1 -1
- package/build/components/Common/SolanaChain/index.d.ts +5 -4
- package/build/components/Common/SolanaChain/index.js +9 -17
- package/build/components/Common/SolanaChain/index.js.map +1 -1
- package/build/components/ConnectModal/index.js +2 -0
- package/build/components/ConnectModal/index.js.map +1 -1
- package/build/components/Openfort/OpenfortProvider.js +4 -0
- package/build/components/Openfort/OpenfortProvider.js.map +1 -1
- package/build/components/Openfort/context.d.ts +4 -1
- package/build/components/Openfort/types.d.ts +22 -0
- package/build/components/Openfort/types.js +1 -0
- package/build/components/Openfort/types.js.map +1 -1
- package/build/components/Pages/Buy/styles.js +10 -10
- package/build/components/Pages/BuySelectProvider/index.js +9 -1
- package/build/components/Pages/BuySelectProvider/index.js.map +1 -1
- package/build/components/Pages/Connected/EthereumConnected.js +7 -1
- package/build/components/Pages/Connected/EthereumConnected.js.map +1 -1
- package/build/components/Pages/Deposit/TestnetNotice.js +15 -3
- package/build/components/Pages/Deposit/TestnetNotice.js.map +1 -1
- package/build/components/Pages/Deposit/UnsupportedNetworkNotice.d.ts +9 -0
- package/build/components/Pages/Deposit/UnsupportedNetworkNotice.js +11 -1
- package/build/components/Pages/Deposit/UnsupportedNetworkNotice.js.map +1 -1
- package/build/components/Pages/Deposit/index.js +2 -5
- package/build/components/Pages/Deposit/index.js.map +1 -1
- package/build/components/Pages/Deposit/paymentOptions.d.ts +0 -6
- package/build/components/Pages/Deposit/paymentOptions.js +3 -5
- package/build/components/Pages/Deposit/paymentOptions.js.map +1 -1
- package/build/components/Pages/Deposit/sources.d.ts +3 -0
- package/build/components/Pages/Deposit/sources.js +4 -1
- package/build/components/Pages/Deposit/sources.js.map +1 -1
- package/build/components/Pages/Deposit/useDepositRoute.d.ts +1 -0
- package/build/components/Pages/Deposit/useDepositRoute.js +37 -10
- package/build/components/Pages/Deposit/useDepositRoute.js.map +1 -1
- package/build/components/Pages/Deposit/useFundingTarget.d.ts +6 -4
- package/build/components/Pages/Deposit/useFundingTarget.js +18 -7
- package/build/components/Pages/Deposit/useFundingTarget.js.map +1 -1
- package/build/components/Pages/DepositCex/index.js +24 -5
- package/build/components/Pages/DepositCex/index.js.map +1 -1
- package/build/components/Pages/DepositCrypto/index.js +2 -2
- package/build/components/Pages/DepositWallet/index.js +2 -2
- package/build/components/Pages/Send/EthereumSend.js +10 -4
- package/build/components/Pages/Send/EthereumSend.js.map +1 -1
- package/build/components/Pages/Send/SolanaSend.js +9 -4
- package/build/components/Pages/Send/SolanaSend.js.map +1 -1
- package/build/components/Pages/Send/styles.d.ts +0 -1
- package/build/components/Pages/Send/styles.js +2 -13
- package/build/components/Pages/Send/styles.js.map +1 -1
- package/build/components/Pages/SendConfirmation/ConfirmationSummary.d.ts +27 -0
- package/build/components/Pages/SendConfirmation/ConfirmationSummary.js +15 -0
- package/build/components/Pages/SendConfirmation/ConfirmationSummary.js.map +1 -0
- package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js +3 -3
- package/build/components/Pages/SendConfirmation/index.js +14 -30
- package/build/components/Pages/SendConfirmation/index.js.map +1 -1
- package/build/components/Pages/SendConfirmation/styles.d.ts +7 -1
- package/build/components/Pages/SendConfirmation/styles.js +49 -17
- package/build/components/Pages/SendConfirmation/styles.js.map +1 -1
- package/build/components/Pages/SignMessage/index.d.ts +2 -0
- package/build/components/Pages/SignMessage/index.js +81 -0
- package/build/components/Pages/SignMessage/index.js.map +1 -0
- package/build/components/Pages/SignMessage/styles.d.ts +14 -0
- package/build/components/Pages/SignMessage/styles.js +82 -0
- package/build/components/Pages/SignMessage/styles.js.map +1 -0
- package/build/hooks/openfort/useSignMessage.d.ts +27 -0
- package/build/hooks/openfort/useSignMessage.js +52 -0
- package/build/hooks/openfort/useSignMessage.js.map +1 -0
- package/build/hooks/openfort/useUI.d.ts +6 -1
- package/build/hooks/openfort/useUI.js +12 -2
- package/build/hooks/openfort/useUI.js.map +1 -1
- package/build/index.d.ts +2 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/localizations/locales/en-US.js +1 -1
- package/build/utils/rpc.d.ts +6 -0
- package/build/utils/rpc.js +12 -1
- package/build/utils/rpc.js.map +1 -1
- package/build/version.d.ts +1 -1
- package/build/version.js +1 -1
- package/build/wagmi/components/ChainSelect/index.js +2 -94
- package/build/wagmi/components/ChainSelect/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
56
|
-
//
|
|
57
|
-
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -13,5 +13,8 @@ export declare const DEST_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
|
13
13
|
*/
|
|
14
14
|
export declare const DEST_CHAIN_SOL = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
15
15
|
export declare const DEST_USDC_SOL = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
16
|
+
/** EVM native-asset sentinel (zero address) — the default destination currency on
|
|
17
|
+
* chains where we don't ship a stablecoin address (e.g. testnets). */
|
|
18
|
+
export declare const NATIVE_TOKEN_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
16
19
|
/** True for a CAIP-2 Solana chain id. */
|
|
17
20
|
export declare function isSolana(chain: string): boolean;
|
|
@@ -13,10 +13,13 @@ const DEST_USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
|
|
|
13
13
|
*/
|
|
14
14
|
const DEST_CHAIN_SOL = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';
|
|
15
15
|
const DEST_USDC_SOL = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
16
|
+
/** EVM native-asset sentinel (zero address) — the default destination currency on
|
|
17
|
+
* chains where we don't ship a stablecoin address (e.g. testnets). */
|
|
18
|
+
const NATIVE_TOKEN_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
16
19
|
/** True for a CAIP-2 Solana chain id. */
|
|
17
20
|
function isSolana(chain) {
|
|
18
21
|
return chain.startsWith('solana:');
|
|
19
22
|
}
|
|
20
23
|
|
|
21
|
-
export { DEST_CHAIN, DEST_CHAIN_SOL, DEST_USDC, DEST_USDC_SOL, isSolana };
|
|
24
|
+
export { DEST_CHAIN, DEST_CHAIN_SOL, DEST_USDC, DEST_USDC_SOL, NATIVE_TOKEN_ADDRESS, isSolana };
|
|
22
25
|
//# sourceMappingURL=sources.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sources.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sources.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
|
-
|
|
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 = (
|
|
48
|
-
const currencies = (
|
|
49
|
-
const activeCurrency = (
|
|
50
|
-
const 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
|
-
: ((
|
|
60
|
-
const pm = (
|
|
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: (
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The destination route Deposit-hub funding settles into. Integrators override the
|
|
3
|
-
* chain and currency via `uiConfig.funding.{targetChain,targetCurrency}
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* chain and currency via `uiConfig.funding.{targetChain,targetCurrency}`. With no
|
|
4
|
+
* override the destination follows the wallet's ACTIVE chain — so a testnet wallet
|
|
5
|
+
* funds on its own network instead of being told "funding isn't available on Base".
|
|
6
|
+
* Currency defaults to USDC where we ship its address (Base, Solana mainnet) and to
|
|
7
|
+
* the native asset otherwise, so the destination currency is always valid on-chain.
|
|
8
|
+
* The deposit recipient is always the active embedded wallet — callers resolve it.
|
|
7
9
|
*/
|
|
8
10
|
export declare function useFundingTarget(): {
|
|
9
11
|
chain: string;
|
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import { ChainTypeEnum } from '@openfort/openfort-js';
|
|
2
|
+
import { useEthereumEmbeddedWallet } from '../../../ethereum/hooks/useEthereumEmbeddedWallet.js';
|
|
2
3
|
import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
|
|
3
4
|
import { useOpenfort } from '../../Openfort/useOpenfort.js';
|
|
4
|
-
import { DEST_CHAIN_SOL, DEST_CHAIN, DEST_USDC_SOL,
|
|
5
|
+
import { DEST_CHAIN_SOL, DEST_CHAIN, DEST_USDC, DEST_USDC_SOL, NATIVE_TOKEN_ADDRESS } from './sources.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* The destination route Deposit-hub funding settles into. Integrators override the
|
|
8
|
-
* chain and currency via `uiConfig.funding.{targetChain,targetCurrency}
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* chain and currency via `uiConfig.funding.{targetChain,targetCurrency}`. With no
|
|
10
|
+
* override the destination follows the wallet's ACTIVE chain — so a testnet wallet
|
|
11
|
+
* funds on its own network instead of being told "funding isn't available on Base".
|
|
12
|
+
* Currency defaults to USDC where we ship its address (Base, Solana mainnet) and to
|
|
13
|
+
* the native asset otherwise, so the destination currency is always valid on-chain.
|
|
14
|
+
* The deposit recipient is always the active embedded wallet — callers resolve it.
|
|
12
15
|
*/
|
|
13
16
|
function useFundingTarget() {
|
|
14
17
|
var _a, _b, _c, _d;
|
|
15
18
|
const { uiConfig } = useOpenfort();
|
|
16
19
|
const { chainType } = useOpenfortCore();
|
|
20
|
+
const ethereumWallet = useEthereumEmbeddedWallet();
|
|
17
21
|
const isSolana = chainType === ChainTypeEnum.SVM;
|
|
22
|
+
const activeChain = isSolana
|
|
23
|
+
? DEST_CHAIN_SOL
|
|
24
|
+
: ethereumWallet.status === 'connected'
|
|
25
|
+
? `eip155:${ethereumWallet.chainId}`
|
|
26
|
+
: DEST_CHAIN;
|
|
27
|
+
const chain = (_b = (_a = uiConfig.funding) === null || _a === void 0 ? void 0 : _a.targetChain) !== null && _b !== void 0 ? _b : activeChain;
|
|
28
|
+
const defaultCurrency = chain === DEST_CHAIN ? DEST_USDC : chain === DEST_CHAIN_SOL ? DEST_USDC_SOL : NATIVE_TOKEN_ADDRESS;
|
|
18
29
|
return {
|
|
19
|
-
chain
|
|
20
|
-
currency: (_d = (_c = uiConfig.funding) === null || _c === void 0 ? void 0 : _c.targetCurrency) !== null && _d !== void 0 ? _d :
|
|
30
|
+
chain,
|
|
31
|
+
currency: (_d = (_c = uiConfig.funding) === null || _c === void 0 ? void 0 : _c.targetCurrency) !== null && _d !== void 0 ? _d : defaultCurrency,
|
|
21
32
|
};
|
|
22
33
|
}
|
|
23
34
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFundingTarget.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useFundingTarget.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';
|
|
@@ -19,6 +21,7 @@ import { DepositStatus } from '../Deposit/DepositStatus.js';
|
|
|
19
21
|
import { walletListBtn } from '../Deposit/formStyles.js';
|
|
20
22
|
import { isSolana, DEST_USDC } from '../Deposit/sources.js';
|
|
21
23
|
import { StepDivider, ButtonLogo } from '../Deposit/styles.js';
|
|
24
|
+
import { TestnetNotice } from '../Deposit/TestnetNotice.js';
|
|
22
25
|
import { useFundingTarget } from '../Deposit/useFundingTarget.js';
|
|
23
26
|
import { sanitizeForParsing, sanitizeAmountInput } from '../Send/utils.js';
|
|
24
27
|
|
|
@@ -56,7 +59,10 @@ const hideBrokenLogo = (e) => {
|
|
|
56
59
|
*/
|
|
57
60
|
const DepositCex = () => {
|
|
58
61
|
var _a, _b, _c, _d, _e, _f;
|
|
59
|
-
const { triggerResize } = useOpenfort();
|
|
62
|
+
const { triggerResize, publishableKey } = useOpenfort();
|
|
63
|
+
// Coinbase onramp settles real funds on mainnet, so a test key can't deliver here.
|
|
64
|
+
// Keep the button live for the demo but block the hand-off with a testnet notice.
|
|
65
|
+
const testnet = getPublishableKeyEnvironment(publishableKey) === 'test';
|
|
60
66
|
const target = useFundingTarget();
|
|
61
67
|
// CEX (Coinbase pay-link + session) is served by the Openfort API, not the
|
|
62
68
|
// standalone funding service — resolve this rail's base URL from the API backend.
|
|
@@ -100,7 +106,9 @@ const DepositCex = () => {
|
|
|
100
106
|
const createSessionRef = useRef(createSession);
|
|
101
107
|
createSessionRef.current = createSession;
|
|
102
108
|
useEffect(() => {
|
|
103
|
-
|
|
109
|
+
// No session on testnet — Coinbase can't settle to a testnet wallet and the
|
|
110
|
+
// button below is blocked anyway; skip the mint so we don't fire a doomed call.
|
|
111
|
+
if (!isAvailable || !address || !chainSupported || testnet)
|
|
104
112
|
return;
|
|
105
113
|
const key = `${target.chain}|${target.currency}|${address}`;
|
|
106
114
|
if (sessionKey.current === key)
|
|
@@ -125,7 +133,7 @@ const DepositCex = () => {
|
|
|
125
133
|
// without this the guard above would block the retry after this cancel.
|
|
126
134
|
sessionKey.current = '';
|
|
127
135
|
};
|
|
128
|
-
}, [isAvailable, address, chainSupported, target.chain, target.currency]);
|
|
136
|
+
}, [isAvailable, address, chainSupported, testnet, target.chain, target.currency]);
|
|
129
137
|
const fiatAmount = useMemo(() => {
|
|
130
138
|
const normalized = sanitizeForParsing(sanitizeAmountInput(amount));
|
|
131
139
|
if (!normalized)
|
|
@@ -206,14 +214,25 @@ const DepositCex = () => {
|
|
|
206
214
|
// success / refunded / expired screen (shared with the crypto rail).
|
|
207
215
|
if (isDepositFlowActive(status))
|
|
208
216
|
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: {
|
|
217
|
+
return (jsxs(PageContent, { onBack: routes.DEPOSIT, children: [jsx(ModalHeading, { children: "Transfer from Exchange" }), jsx(TestnetNotice, {}), 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
218
|
...walletListBtn,
|
|
211
219
|
display: 'flex',
|
|
212
220
|
alignItems: 'center',
|
|
213
221
|
gap: 8,
|
|
214
222
|
opacity: 0.55,
|
|
215
223
|
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)) :
|
|
224
|
+
}, 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 ? (
|
|
225
|
+
// Blocked on testnet (Coinbase settles on mainnet). Use aria-disabled, not
|
|
226
|
+
// `disabled`, so the hover still fires the tooltip that explains why.
|
|
227
|
+
jsx(Tooltip, { message: "Coinbase settles on mainnet \u2014 not available on testnet.", children: jsxs("button", { type: "button", "aria-disabled": "true", style: {
|
|
228
|
+
...walletListBtn,
|
|
229
|
+
display: 'flex',
|
|
230
|
+
alignItems: 'center',
|
|
231
|
+
justifyContent: 'center',
|
|
232
|
+
gap: 8,
|
|
233
|
+
opacity: 0.55,
|
|
234
|
+
cursor: 'not-allowed',
|
|
235
|
+
}, children: [jsx(ButtonLogo, { children: EXCHANGE_LOGO[ex.id] }), `Open ${titleCase(ex.id)} ↗`] }) }, ex.id)) : (jsxs("button", { type: "button", disabled: !payReady, style: {
|
|
217
236
|
...walletListBtn,
|
|
218
237
|
display: 'flex',
|
|
219
238
|
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,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useMemo } from 'react';
|
|
2
|
+
import { useEffect, useMemo } from 'react';
|
|
3
3
|
import { parseUnits, isAddress, formatUnits } from 'viem';
|
|
4
4
|
import { useEthereumWalletAssets } from '../../../ethereum/hooks/useEthereumWalletAssets.js';
|
|
5
5
|
import Button from '../../Common/Button/index.js';
|
|
@@ -9,12 +9,18 @@ import { ModalHeading } from '../../Common/Modal/styles.js';
|
|
|
9
9
|
import { routes } from '../../Openfort/types.js';
|
|
10
10
|
import { useOpenfort } from '../../Openfort/useOpenfort.js';
|
|
11
11
|
import { PageContent } from '../../PageContent/index.js';
|
|
12
|
-
import {
|
|
12
|
+
import { AmountCard, AmountInput } from '../Buy/styles.js';
|
|
13
|
+
import { Form, Field, FieldLabel, TokenSelectorButton, TokenSelectorContent, TokenSelectorValue, TokenSelectorRight, MaxButton, HelperText, ErrorText } from './styles.js';
|
|
13
14
|
import { isSameToken, sanitizeForParsing, formatBalance, sanitizeAmountInput } from './utils.js';
|
|
14
15
|
|
|
15
16
|
const EthereumSend = () => {
|
|
16
17
|
var _a, _b, _c, _d;
|
|
17
|
-
const { sendForm, setSendForm, setRoute } = useOpenfort();
|
|
18
|
+
const { sendForm, setSendForm, setRoute, triggerResize } = useOpenfort();
|
|
19
|
+
// Size the modal to the form on mount. Without this the screen isn't anchored
|
|
20
|
+
// and scrolls within the modal — every other Page triggers a resize on mount.
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
triggerResize();
|
|
23
|
+
}, [triggerResize]);
|
|
18
24
|
const { data: assets } = useEthereumWalletAssets();
|
|
19
25
|
const matchedToken = useMemo(() => assets === null || assets === void 0 ? void 0 : assets.find((asset) => isSameToken(asset, sendForm.asset)), [assets, sendForm.asset]);
|
|
20
26
|
const selectedTokenOption = matchedToken !== null && matchedToken !== void 0 ? matchedToken : assets === null || assets === void 0 ? void 0 : assets[0];
|
|
@@ -81,7 +87,7 @@ const EthereumSend = () => {
|
|
|
81
87
|
};
|
|
82
88
|
const availableLabel = formatBalance(selectedBalanceValue, selectedDecimalsValue);
|
|
83
89
|
const maxDisabled = !selectedBalanceValue;
|
|
84
|
-
return (jsxs(PageContent, { onBack: routes.CONNECTED, children: [jsx(ModalHeading, { children: "Send assets" }), jsxs(Form, { onSubmit: handleSubmit, children: [jsxs(Field, { children: [jsx(FieldLabel, { children: "Asset" }), jsxs(TokenSelectorButton, { type: "button", onClick: handleOpenTokenSelector, children: [jsx(TokenSelectorContent, { children: jsx(TokenSelectorValue, { "$primary": true, children: selectedSymbol || 'Select token' }) }), jsxs(TokenSelectorRight, { children: [jsx(TokenSelectorValue, { children: availableLabel === '--' ? '--' : `${availableLabel} ${selectedSymbol !== null && selectedSymbol !== void 0 ? selectedSymbol : ''}` }), jsx(Arrow, { width: "13", height: "12", viewBox: "0 0 13 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx(ArrowChevron, { stroke: "currentColor", d: "M7.51431 1.5L11.757 5.74264M7.5 10.4858L11.7426 6.24314", strokeWidth: "2", strokeLinecap: "round" }) })] })] })] }), jsxs(Field, { children: [jsx(FieldLabel, { children: "Amount" }), jsxs(
|
|
90
|
+
return (jsxs(PageContent, { onBack: routes.CONNECTED, children: [jsx(ModalHeading, { children: "Send assets" }), jsxs(Form, { onSubmit: handleSubmit, children: [jsxs(Field, { children: [jsx(FieldLabel, { children: "Asset" }), jsxs(TokenSelectorButton, { type: "button", onClick: handleOpenTokenSelector, children: [jsx(TokenSelectorContent, { children: jsx(TokenSelectorValue, { "$primary": true, children: selectedSymbol || 'Select token' }) }), jsxs(TokenSelectorRight, { children: [jsx(TokenSelectorValue, { children: availableLabel === '--' ? '--' : `${availableLabel} ${selectedSymbol !== null && selectedSymbol !== void 0 ? selectedSymbol : ''}` }), jsx(Arrow, { width: "13", height: "12", viewBox: "0 0 13 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx(ArrowChevron, { stroke: "currentColor", d: "M7.51431 1.5L11.757 5.74264M7.5 10.4858L11.7426 6.24314", strokeWidth: "2", strokeLinecap: "round" }) })] })] })] }), jsxs(Field, { children: [jsx(FieldLabel, { children: "Amount" }), jsxs(AmountCard, { style: { marginTop: 12 }, children: [jsx(AmountInput, { placeholder: "0.00", value: sendForm.amount, onChange: handleAmountChange, inputMode: "decimal", autoComplete: "off" }), jsx(MaxButton, { type: "button", onClick: handleMax, disabled: maxDisabled, children: "Max" })] }), jsxs(HelperText, { children: ["Available: ", availableLabel, " ", selectedSymbol] }), sendForm.amount && parsedAmount === null && jsx(ErrorText, { children: "Enter a valid amount." }), insufficientBalance && jsx(ErrorText, { children: "Insufficient balance for this transfer." })] }), jsxs(Field, { children: [jsx(FieldLabel, { children: "Recipient address" }), jsx(Input, { placeholder: "0x...", value: sendForm.recipient, onChange: handleRecipientChange, autoComplete: "off" }), sendForm.recipient && !recipientValid && jsx(ErrorText, { children: "Enter a valid wallet address." })] }), jsx(Button, { variant: "primary", disabled: !canProceed, children: "Review transfer" })] })] }));
|
|
85
91
|
};
|
|
86
92
|
|
|
87
93
|
export { EthereumSend };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EthereumSend.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EthereumSend.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { useMemo } from 'react';
|
|
2
|
+
import { useEffect, useMemo } from 'react';
|
|
3
3
|
import { parseUnits, formatUnits } from 'viem';
|
|
4
4
|
import { useSolanaWalletAssets } from '../../../solana/hooks/useSolanaWalletAssets.js';
|
|
5
5
|
import Button from '../../Common/Button/index.js';
|
|
@@ -9,7 +9,8 @@ import { ModalHeading } from '../../Common/Modal/styles.js';
|
|
|
9
9
|
import { routes } from '../../Openfort/types.js';
|
|
10
10
|
import { useOpenfort } from '../../Openfort/useOpenfort.js';
|
|
11
11
|
import { PageContent } from '../../PageContent/index.js';
|
|
12
|
-
import {
|
|
12
|
+
import { AmountCard, AmountInput } from '../Buy/styles.js';
|
|
13
|
+
import { Form, Field, FieldLabel, TokenSelectorButton, TokenSelectorContent, TokenSelectorValue, TokenSelectorRight, MaxButton, HelperText, ErrorText } from './styles.js';
|
|
13
14
|
import { sanitizeForParsing, formatBalance, sanitizeAmountInput } from './utils.js';
|
|
14
15
|
|
|
15
16
|
const SOL_DECIMALS = 9;
|
|
@@ -26,8 +27,12 @@ function solAsset(balance) {
|
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
const SolanaSend = () => {
|
|
29
|
-
const { sendForm, setSendForm, setRoute } = useOpenfort();
|
|
30
|
+
const { sendForm, setSendForm, setRoute, triggerResize } = useOpenfort();
|
|
30
31
|
const { data: assets } = useSolanaWalletAssets();
|
|
32
|
+
// Size the modal to the form on mount so it's anchored and doesn't scroll.
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
triggerResize();
|
|
35
|
+
}, [triggerResize]);
|
|
31
36
|
const asset = sendForm.asset;
|
|
32
37
|
const selected = asset.type === 'spl'
|
|
33
38
|
? { isSpl: true, mint: asset.address, decimals: asset.metadata.decimals, symbol: asset.metadata.symbol }
|
|
@@ -82,7 +87,7 @@ const SolanaSend = () => {
|
|
|
82
87
|
setSendForm((prev) => ({ ...prev, amount: formatUnits(balanceBase, selected.decimals) }));
|
|
83
88
|
};
|
|
84
89
|
const handleOpenTokenSelector = () => setRoute(routes.SOL_SEND_TOKEN_SELECT);
|
|
85
|
-
return (jsxs(PageContent, { onBack: routes.SOL_CONNECTED, children: [jsx(ModalHeading, { children: "Send assets" }), jsxs(Form, { onSubmit: handleSubmit, children: [jsxs(Field, { children: [jsx(FieldLabel, { children: "Asset" }), jsxs(TokenSelectorButton, { type: "button", onClick: handleOpenTokenSelector, children: [jsx(TokenSelectorContent, { children: jsx(TokenSelectorValue, { "$primary": true, children: selected.symbol }) }), jsxs(TokenSelectorRight, { children: [jsx(TokenSelectorValue, { children: availableLabel === '--' ? '--' : `${availableLabel} ${selected.symbol}` }), jsx(Arrow, { width: "13", height: "12", viewBox: "0 0 13 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx(ArrowChevron, { stroke: "currentColor", d: "M7.51431 1.5L11.757 5.74264M7.5 10.4858L11.7426 6.24314", strokeWidth: "2", strokeLinecap: "round" }) })] })] })] }), jsxs(Field, { children: [jsx(FieldLabel, { children: "Amount" }), jsxs(
|
|
90
|
+
return (jsxs(PageContent, { onBack: routes.SOL_CONNECTED, children: [jsx(ModalHeading, { children: "Send assets" }), jsxs(Form, { onSubmit: handleSubmit, children: [jsxs(Field, { children: [jsx(FieldLabel, { children: "Asset" }), jsxs(TokenSelectorButton, { type: "button", onClick: handleOpenTokenSelector, children: [jsx(TokenSelectorContent, { children: jsx(TokenSelectorValue, { "$primary": true, children: selected.symbol }) }), jsxs(TokenSelectorRight, { children: [jsx(TokenSelectorValue, { children: availableLabel === '--' ? '--' : `${availableLabel} ${selected.symbol}` }), jsx(Arrow, { width: "13", height: "12", viewBox: "0 0 13 12", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: jsx(ArrowChevron, { stroke: "currentColor", d: "M7.51431 1.5L11.757 5.74264M7.5 10.4858L11.7426 6.24314", strokeWidth: "2", strokeLinecap: "round" }) })] })] })] }), jsxs(Field, { children: [jsx(FieldLabel, { children: "Amount" }), jsxs(AmountCard, { style: { marginTop: 12 }, children: [jsx(AmountInput, { placeholder: "0.00", value: sendForm.amount, onChange: handleAmountChange, inputMode: "decimal", autoComplete: "off" }), jsx(MaxButton, { type: "button", onClick: handleMax, disabled: balanceBase === undefined, children: "Max" })] }), jsxs(HelperText, { children: ["Available: ", availableLabel, " ", selected.symbol] }), sendForm.amount && parsedAmount === null && jsx(ErrorText, { children: "Enter a valid amount." }), insufficientBalance && jsxs(ErrorText, { children: ["Insufficient ", selected.symbol, " balance for this transfer."] })] }), jsxs(Field, { children: [jsx(FieldLabel, { children: "Recipient address" }), jsx(Input, { placeholder: "Solana address", value: sendForm.recipient, onChange: (e) => setSendForm((prev) => ({ ...prev, recipient: e.target.value })), autoComplete: "off" }), sendForm.recipient && !recipientValid && jsx(ErrorText, { children: "Enter a valid Solana address." })] }), jsx(Button, { variant: "primary", disabled: !canProceed, children: "Review transfer" })] })] }));
|
|
86
91
|
};
|
|
87
92
|
|
|
88
93
|
export { SolanaSend };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SolanaSend.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SolanaSend.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -8,7 +8,6 @@ export declare const TokenSelectorValue: import("styled-components").StyledCompo
|
|
|
8
8
|
$muted?: boolean;
|
|
9
9
|
}, never>;
|
|
10
10
|
export declare const TokenSelectorRight: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
11
|
-
export declare const AmountInputWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
|
|
12
11
|
export declare const MaxButton: import("styled-components").StyledComponent<"button", any, {}, never>;
|
|
13
12
|
export declare const HelperText: import("styled-components").StyledComponent<"span", any, {}, never>;
|
|
14
13
|
export declare const ErrorText: import("styled-components").StyledComponent<"span", any, {}, never>;
|
|
@@ -62,19 +62,8 @@ const TokenSelectorRight = styled.div `
|
|
|
62
62
|
gap: 6px;
|
|
63
63
|
color: var(--ck-body-color-muted);
|
|
64
64
|
`;
|
|
65
|
-
const AmountInputWrapper = styled.div `
|
|
66
|
-
position: relative;
|
|
67
|
-
margin-top: 12px;
|
|
68
|
-
|
|
69
|
-
> div {
|
|
70
|
-
margin: 0;
|
|
71
|
-
}
|
|
72
|
-
`;
|
|
73
65
|
const MaxButton = styled.button `
|
|
74
|
-
|
|
75
|
-
right: 12px;
|
|
76
|
-
top: 50%;
|
|
77
|
-
transform: translateY(-50%);
|
|
66
|
+
flex-shrink: 0;
|
|
78
67
|
padding: 6px 14px;
|
|
79
68
|
border-radius: 16px;
|
|
80
69
|
border: 1px solid var(--ck-body-divider);
|
|
@@ -108,5 +97,5 @@ const ErrorText = styled.span `
|
|
|
108
97
|
color: var(--ck-body-color-danger);
|
|
109
98
|
`;
|
|
110
99
|
|
|
111
|
-
export {
|
|
100
|
+
export { ErrorText, Field, FieldLabel, Form, HelperText, MaxButton, TokenSelectorButton, TokenSelectorContent, TokenSelectorRight, TokenSelectorValue };
|
|
112
101
|
//# sourceMappingURL=styles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"styles.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
type ConfirmationAddress = {
|
|
3
|
+
display: string;
|
|
4
|
+
value: string;
|
|
5
|
+
};
|
|
6
|
+
interface ConfirmationSummaryProps {
|
|
7
|
+
/** Total being sent, e.g. "0.5" + "ETH". */
|
|
8
|
+
amount: string;
|
|
9
|
+
symbol: string;
|
|
10
|
+
/** Optional fiat estimate shown next to the total, e.g. "$180.84". */
|
|
11
|
+
fiat?: string | null;
|
|
12
|
+
/** Recipient address (truncated for display, full for copy). */
|
|
13
|
+
to?: ConfirmationAddress;
|
|
14
|
+
networkName: string;
|
|
15
|
+
networkIcon?: ReactNode;
|
|
16
|
+
/** Fee cell — the live estimate, or a "Sponsored" indicator. */
|
|
17
|
+
fee: ReactNode;
|
|
18
|
+
/** The wallet the funds are paid from. */
|
|
19
|
+
payWith?: ConfirmationAddress;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Shared, chain-agnostic transaction preview used by the EVM and Solana send
|
|
23
|
+
* confirmation screens. Renders the approval-style rows (Total / To / Network /
|
|
24
|
+
* Estimated fee) plus a "Pay with" card.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ConfirmationSummary({ amount, symbol, fiat, to, networkName, networkIcon, fee, payWith, }: ConfirmationSummaryProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { CopyText } from '../../Common/CopyToClipboard/CopyText.js';
|
|
3
|
+
import { SummaryList, SummaryItem, SummaryLabel, AmountValue, FiatValue, AddressValue, NetworkValue, PayWithCard, PayWithMeta, PayWithAddress, PayWithBadge } from './styles.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared, chain-agnostic transaction preview used by the EVM and Solana send
|
|
7
|
+
* confirmation screens. Renders the approval-style rows (Total / To / Network /
|
|
8
|
+
* Estimated fee) plus a "Pay with" card.
|
|
9
|
+
*/
|
|
10
|
+
function ConfirmationSummary({ amount, symbol, fiat, to, networkName, networkIcon, fee, payWith, }) {
|
|
11
|
+
return (jsxs(Fragment, { children: [jsxs(SummaryList, { children: [jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Total" }), jsxs(AmountValue, { children: [amount || '0', " ", symbol, fiat ? jsxs(FiatValue, { children: ["\u2248 ", fiat] }) : null] })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "To" }), jsx(AddressValue, { children: to ? (jsx(CopyText, { size: "1rem", value: to.value, children: to.display })) : ('--') })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Network" }), jsxs(NetworkValue, { children: [networkIcon, networkName] })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Estimated fee" }), fee] })] }), jsxs(PayWithCard, { children: [jsxs(PayWithMeta, { children: [jsx(SummaryLabel, { children: "Pay with" }), jsx(PayWithAddress, { children: payWith ? (jsx(CopyText, { size: "0.875rem", value: payWith.value, children: payWith.display })) : ('--') })] }), jsxs(PayWithBadge, { children: [amount || '0', " ", symbol] })] })] }));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { ConfirmationSummary };
|
|
15
|
+
//# sourceMappingURL=ConfirmationSummary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConfirmationSummary.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
|