@openfort/react 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/build/components/Common/Modal/index.js +14 -0
  2. package/build/components/Common/Modal/index.js.map +1 -1
  3. package/build/components/Openfort/types.d.ts +1 -1
  4. package/build/components/Pages/AssetInventory/SolanaAssetInventory.js +47 -8
  5. package/build/components/Pages/AssetInventory/SolanaAssetInventory.js.map +1 -1
  6. package/build/components/Pages/AssetInventory/index.js +50 -6
  7. package/build/components/Pages/AssetInventory/index.js.map +1 -1
  8. package/build/components/Pages/Connected/EthereumConnected.js +2 -5
  9. package/build/components/Pages/Connected/EthereumConnected.js.map +1 -1
  10. package/build/components/Pages/DepositWallet/index.js +15 -10
  11. package/build/components/Pages/DepositWallet/index.js.map +1 -1
  12. package/build/components/Pages/ExportKey/index.js +44 -4
  13. package/build/components/Pages/ExportKey/index.js.map +1 -1
  14. package/build/components/Pages/ExportKey/styles.d.ts +5 -0
  15. package/build/components/Pages/ExportKey/styles.js +47 -0
  16. package/build/components/Pages/ExportKey/styles.js.map +1 -0
  17. package/build/components/Pages/Receive/index.js +2 -2
  18. package/build/components/Pages/SelectToken/SolanaSelectToken.js +7 -7
  19. package/build/components/Pages/SelectToken/index.js +7 -3
  20. package/build/components/Pages/SelectToken/index.js.map +1 -1
  21. package/build/components/Pages/Send/EthereumSend.js +25 -5
  22. package/build/components/Pages/Send/EthereumSend.js.map +1 -1
  23. package/build/components/Pages/Send/SolanaSend.js +23 -4
  24. package/build/components/Pages/Send/SolanaSend.js.map +1 -1
  25. package/build/components/Pages/Send/styles.d.ts +17 -11
  26. package/build/components/Pages/Send/styles.js +104 -49
  27. package/build/components/Pages/Send/styles.js.map +1 -1
  28. package/build/components/Pages/SendConfirmation/ConfirmationSummary.d.ts +3 -1
  29. package/build/components/Pages/SendConfirmation/ConfirmationSummary.js +2 -2
  30. package/build/components/Pages/SendConfirmation/EstimatedFees.d.ts +3 -1
  31. package/build/components/Pages/SendConfirmation/EstimatedFees.js +22 -15
  32. package/build/components/Pages/SendConfirmation/EstimatedFees.js.map +1 -1
  33. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js +17 -5
  34. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js.map +1 -1
  35. package/build/components/Pages/SendConfirmation/index.js +11 -5
  36. package/build/components/Pages/SendConfirmation/index.js.map +1 -1
  37. package/build/components/Pages/SendConfirmation/styles.d.ts +3 -1
  38. package/build/components/Pages/SendConfirmation/styles.js +20 -6
  39. package/build/components/Pages/SendConfirmation/styles.js.map +1 -1
  40. package/build/components/Pages/SignMessage/index.js +44 -19
  41. package/build/components/Pages/SignMessage/index.js.map +1 -1
  42. package/build/components/Pages/SignMessage/styles.d.ts +2 -3
  43. package/build/components/Pages/SignMessage/styles.js +19 -2
  44. package/build/components/Pages/SignMessage/styles.js.map +1 -1
  45. package/build/constants/defaultAssets.d.ts +18 -0
  46. package/build/constants/defaultAssets.js +70 -0
  47. package/build/constants/defaultAssets.js.map +1 -0
  48. package/build/hooks/openfort/useSignMessage.d.ts +2 -2
  49. package/build/hooks/openfort/useUI.js +4 -2
  50. package/build/hooks/openfort/useUI.js.map +1 -1
  51. package/build/solana/transfer.d.ts +12 -0
  52. package/build/solana/transfer.js +29 -1
  53. package/build/solana/transfer.js.map +1 -1
  54. package/build/version.d.ts +1 -1
  55. package/build/version.js +1 -1
  56. package/package.json +1 -1
@@ -3,6 +3,7 @@ import { ChainTypeEnum } from '@openfort/openfort-js';
3
3
  import { useMemo, useEffect, useState, useRef } from 'react';
4
4
  import { isAddress, createPublicClient, http, erc20Abi, parseUnits, encodeFunctionData } from 'viem';
5
5
  import { TickIcon } from '../../../assets/icons.js';
6
+ import { chainLogoUrl } from '../../../constants/logos.js';
6
7
  import { useEthereumEmbeddedWallet } from '../../../ethereum/hooks/useEthereumEmbeddedWallet.js';
7
8
  import { useEthereumWalletAssets } from '../../../ethereum/hooks/useEthereumWalletAssets.js';
8
9
  import { useBalance } from '../../../hooks/useBalance.js';
@@ -20,16 +21,16 @@ import { ModalHeading, ModalBody } from '../../Common/Modal/styles.js';
20
21
  import { routes } from '../../Openfort/types.js';
21
22
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
22
23
  import { PageContent } from '../../PageContent/index.js';
23
- import { sanitizeForParsing, isSameToken, getAssetDecimals, getAssetSymbol } from '../Send/utils.js';
24
+ import { sanitizeForParsing, isSameToken, getAssetDecimals, getAssetSymbol, formatBalanceWithSymbol } from '../Send/utils.js';
24
25
  import { ConfirmationSummary } from './ConfirmationSummary.js';
25
26
  import { EstimatedFees } from './EstimatedFees.js';
26
- import { ButtonRow, SponsoredFee, FeesValue, StatusMessage, ErrorContainer, ErrorTitle, ErrorMessage, ErrorAction } from './styles.js';
27
+ import { ButtonRow, FeesValue, StatusMessage, ErrorContainer, ErrorTitle, ErrorMessage, ErrorAction } from './styles.js';
27
28
 
28
29
  const SendConfirmation = () => {
29
- var _a, _b, _c, _d, _e, _f;
30
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
30
31
  const wallet = useEthereumEmbeddedWallet();
31
32
  const { chainType } = useOpenfortCore();
32
- const { sendForm, setRoute, triggerResize, walletConfig } = useOpenfort();
33
+ const { sendForm, setRoute, triggerResize, walletConfig, chains } = useOpenfort();
33
34
  const address = wallet.status === 'connected' ? wallet.address : undefined;
34
35
  const chainId = wallet.status === 'connected' ? wallet.chainId : undefined;
35
36
  const blockExplorerUrl = chainId ? getExplorerUrl(ChainTypeEnum.EVM, { chainId }) : undefined;
@@ -316,12 +317,17 @@ const SendConfirmation = () => {
316
317
  return null;
317
318
  return `$${(n * perToken).toFixed(2)}`;
318
319
  }, [(_f = (_e = token.metadata) === null || _e === void 0 ? void 0 : _e.fiat) === null || _f === void 0 ? void 0 : _f.value, normalisedAmount]);
320
+ // Prefer the configured chain's display name over the numeric id fallback.
321
+ const networkName = (_h = (_g = chains === null || chains === void 0 ? void 0 : chains.find((c) => c.id === chainId)) === null || _g === void 0 ? void 0 : _g.name) !== null && _h !== void 0 ? _h : getChainName(chainId !== null && chainId !== void 0 ? chainId : 0);
322
+ const balanceLabel = currentBalance !== undefined
323
+ ? formatBalanceWithSymbol(currentBalance, (_j = getAssetDecimals(token)) !== null && _j !== void 0 ? _j : 18, getAssetSymbol(token))
324
+ : undefined;
319
325
  if (isSuccess) {
320
326
  const successAmount = normalisedAmount || '0';
321
327
  const successSymbol = getAssetSymbol(token);
322
328
  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" })] })] }));
323
329
  }
324
- return (jsxs(PageContent, { children: [jsx(ModalHeading, { children: "Confirm transfer" }), jsx(ModalBody, { children: "Review the transaction before sending." }), jsx(ConfirmationSummary, { amount: normalisedAmount || '0', symbol: getAssetSymbol(token), fiat: fiatTotal, to: recipientAddress ? { display: truncateEthAddress(recipientAddress), value: recipientAddress } : undefined, networkName: getChainName(chainId !== null && chainId !== void 0 ? chainId : 0), payWith: address ? { display: truncateEthAddress(address), value: address } : undefined, fee: isSponsored ? (jsx(SponsoredFee, { children: "Sponsored \u00B7 gasless" })) : (jsx(FeesValue, { 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: false }) })) }), insufficientBalance && !isSuccess && (jsx(StatusMessage, { "$status": "error", children: "Insufficient balance for this transfer." })), errorDetails && (jsxs(ErrorContainer, { children: [jsx(ErrorTitle, { children: errorDetails.title }), jsx(ErrorMessage, { children: errorDetails.message }), errorDetails.action && jsx(ErrorAction, { children: errorDetails.action })] })), jsxs(ButtonRow, { children: [jsx(Button, { variant: "primary", onClick: isSuccess ? handleOpenBlockExplorer : handleConfirm, disabled: isSuccess
330
+ return (jsxs(PageContent, { children: [jsx(ModalHeading, { children: "Confirm transfer" }), jsx(ModalBody, { children: "Review the transaction before sending." }), jsx(ConfirmationSummary, { amount: normalisedAmount || '0', symbol: getAssetSymbol(token), fiat: fiatTotal, to: recipientAddress ? { display: truncateEthAddress(recipientAddress), value: recipientAddress } : undefined, networkName: networkName, networkIcon: chainLogoUrl(chainId) ? jsx("img", { src: (_k = chainLogoUrl(chainId)) !== null && _k !== void 0 ? _k : '', alt: "" }) : undefined, balance: balanceLabel, payWith: address ? { display: truncateEthAddress(address), value: address } : undefined, fee: jsx(FeesValue, { 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)), sponsored: isSponsored, hideInfoIcon: false }) }) }), insufficientBalance && !isSuccess && (jsx(StatusMessage, { "$status": "error", children: "Insufficient balance for this transfer." })), errorDetails && (jsxs(ErrorContainer, { children: [jsx(ErrorTitle, { children: errorDetails.title }), jsx(ErrorMessage, { children: errorDetails.message }), errorDetails.action && jsx(ErrorAction, { children: errorDetails.action })] })), jsxs(ButtonRow, { children: [jsx(Button, { variant: "primary", onClick: isSuccess ? handleOpenBlockExplorer : handleConfirm, disabled: isSuccess
325
331
  ? false
326
332
  : isLoading ||
327
333
  Boolean(transactionHash) ||
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -8,7 +8,9 @@ export declare const FeesValue: import("styled-components").StyledComponent<"spa
8
8
  }, never>;
9
9
  export declare const FiatValue: import("styled-components").StyledComponent<"span", any, {}, never>;
10
10
  export declare const NetworkValue: import("styled-components").StyledComponent<"span", any, {}, never>;
11
- export declare const SponsoredFee: import("styled-components").StyledComponent<"span", any, {}, never>;
11
+ /** The would-be fee, struck through, shown next to "Sponsored". */
12
+ export declare const FeeStrike: import("styled-components").StyledComponent<"span", any, {}, never>;
13
+ export declare const SponsoredText: import("styled-components").StyledComponent<"span", any, {}, never>;
12
14
  export declare const PayWithCard: import("styled-components").StyledComponent<"div", any, {}, never>;
13
15
  export declare const PayWithMeta: import("styled-components").StyledComponent<"div", any, {}, never>;
14
16
  export declare const PayWithAddress: import("styled-components").StyledComponent<"span", any, {}, never>;
@@ -1,4 +1,5 @@
1
1
  import styled from '../../../styles/styled/index.js';
2
+ import { ButtonContainer } from '../../Common/Button/styles.js';
2
3
 
3
4
  const SummaryList = styled.div `
4
5
  display: flex;
@@ -64,8 +65,14 @@ const NetworkValue = styled(SummaryValue) `
64
65
  border-radius: 50%;
65
66
  }
66
67
  `;
67
- const SponsoredFee = styled(SummaryValue) `
68
+ /** The would-be fee, struck through, shown next to "Sponsored". */
69
+ const FeeStrike = styled.span `
70
+ text-decoration: line-through;
71
+ opacity: 0.55;
72
+ `;
73
+ const SponsoredText = styled.span `
68
74
  color: var(--ck-body-color-valid, #16a34a);
75
+ font-weight: 600;
69
76
  `;
70
77
  const PayWithCard = styled.div `
71
78
  display: flex;
@@ -94,8 +101,8 @@ const PayWithBadge = styled.span `
94
101
  border-radius: 999px;
95
102
  font-size: 13px;
96
103
  font-weight: 600;
97
- color: var(--ck-body-color-valid, #16a34a);
98
- background: var(--ck-body-background, rgba(22, 163, 74, 0.12));
104
+ color: var(--ck-body-color);
105
+ background: var(--ck-body-background);
99
106
  `;
100
107
  const InfoIconWrapper = styled.span `
101
108
  color: var(--ck-body-color-muted);
@@ -115,9 +122,16 @@ const InfoIconWrapper = styled.span `
115
122
  `;
116
123
  const ButtonRow = styled.div `
117
124
  display: flex;
118
- flex-direction: column;
119
- gap: 4px;
125
+ gap: 12px;
120
126
  margin-top: 24px;
127
+
128
+ > button {
129
+ flex: 1;
130
+ }
131
+
132
+ ${ButtonContainer} {
133
+ margin: 0;
134
+ }
121
135
  `;
122
136
  const StatusMessage = styled.div `
123
137
  margin-top: 16px;
@@ -157,5 +171,5 @@ const ErrorAction = styled.div `
157
171
  line-height: 1.4;
158
172
  `;
159
173
 
160
- export { AddressValue, AmountValue, ButtonRow, ErrorAction, ErrorContainer, ErrorMessage, ErrorTitle, FeesValue, FiatValue, InfoIconWrapper, NetworkValue, PayWithAddress, PayWithBadge, PayWithCard, PayWithMeta, SponsoredFee, StatusMessage, SummaryItem, SummaryLabel, SummaryList };
174
+ export { AddressValue, AmountValue, ButtonRow, ErrorAction, ErrorContainer, ErrorMessage, ErrorTitle, FeeStrike, FeesValue, FiatValue, InfoIconWrapper, NetworkValue, PayWithAddress, PayWithBadge, PayWithCard, PayWithMeta, SponsoredText, StatusMessage, SummaryItem, SummaryLabel, SummaryList };
161
175
  //# 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,11 +1,14 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { ChainTypeEnum } from '@openfort/openfort-js';
2
3
  import { useState, useRef, useEffect } from 'react';
3
4
  import { useEthereumEmbeddedWallet } from '../../../ethereum/hooks/useEthereumEmbeddedWallet.js';
5
+ import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
6
+ import { useSolanaEmbeddedWallet } from '../../../solana/hooks/useSolanaEmbeddedWallet.js';
4
7
  import Button from '../../Common/Button/index.js';
5
8
  import { CopyButton } from '../../Common/CopyToClipboard/CopyButton.js';
6
9
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
7
10
  import { PageContent } from '../../PageContent/index.js';
8
- import { SuccessWrap, SuccessCircle, SuccessTitle, SignaturePreview, SignContent, Subtitle, MessageBox, CopyRow, ErrorText, DataList, DataItem, DataKey } from './styles.js';
11
+ import { SuccessWrap, SuccessCircle, SuccessTitle, SignaturePreview, SignContent, Subtitle, MessageBox, Footer, CopyRow, ErrorText, DataList, DataItem, DataKey } from './styles.js';
9
12
 
10
13
  /** Renders an EIP-712 value as a readable nested bullet list. */
11
14
  function DataNode({ value }) {
@@ -17,17 +20,29 @@ function DataNode({ value }) {
17
20
  const SignMessage = () => {
18
21
  var _a;
19
22
  const { signRequest, setSignRequest, setOpen, uiConfig, triggerResize } = useOpenfort();
23
+ const { chainType } = useOpenfortCore();
20
24
  const wallet = useEthereumEmbeddedWallet();
25
+ const solana = useSolanaEmbeddedWallet();
21
26
  const [signing, setSigning] = useState(false);
22
27
  const [error, setError] = useState(null);
23
28
  const [signature, setSignature] = useState(null);
24
29
  const settledRef = useRef(false);
30
+ const mountedRef = useRef(false);
25
31
  // Reject the pending request if the screen unmounts before signing (the user
26
- // closed the modal or navigated away).
32
+ // closed the modal or navigated away). React StrictMode invokes effect cleanup
33
+ // on a dev-only synchronous remount, so defer the reject to a microtask: the
34
+ // immediate remount flips mountedRef back to true and cancels the spurious
35
+ // "User rejected" that would otherwise fire while the wallet UI is still open.
27
36
  useEffect(() => {
37
+ mountedRef.current = true;
38
+ const request = signRequest;
28
39
  return () => {
29
- if (!settledRef.current)
30
- signRequest === null || signRequest === void 0 ? void 0 : signRequest.reject(new Error('User rejected the signature request'));
40
+ mountedRef.current = false;
41
+ queueMicrotask(() => {
42
+ if (!mountedRef.current && !settledRef.current) {
43
+ request === null || request === void 0 ? void 0 : request.reject(new Error('User rejected the signature request'));
44
+ }
45
+ });
31
46
  };
32
47
  }, [signRequest]);
33
48
  // Content height changes between the views; re-measure.
@@ -46,16 +61,26 @@ const SignMessage = () => {
46
61
  setError(null);
47
62
  setSigning(true);
48
63
  try {
49
- const provider = await ((_a = wallet.activeWallet) === null || _a === void 0 ? void 0 : _a.getProvider());
50
- const address = wallet.address;
51
- if (!provider || !address)
52
- throw new Error('No connected wallet to sign with');
53
- const signed = (signRequest.kind === 'message'
54
- ? await provider.request({ method: 'personal_sign', params: [signRequest.message, address] })
55
- : await provider.request({
56
- method: 'eth_signTypedData_v4',
57
- params: [address, JSON.stringify(signRequest.typedData)],
58
- }));
64
+ let signed;
65
+ if (chainType === ChainTypeEnum.SVM) {
66
+ if (signRequest.kind !== 'message')
67
+ throw new Error('Typed data signing is not supported on Solana.');
68
+ if (solana.status !== 'connected')
69
+ throw new Error('No connected wallet to sign with');
70
+ signed = await solana.provider.signMessage(signRequest.message);
71
+ }
72
+ else {
73
+ const provider = await ((_a = wallet.activeWallet) === null || _a === void 0 ? void 0 : _a.getProvider());
74
+ const address = wallet.address;
75
+ if (!provider || !address)
76
+ throw new Error('No connected wallet to sign with');
77
+ signed = (signRequest.kind === 'message'
78
+ ? await provider.request({ method: 'personal_sign', params: [signRequest.message, address] })
79
+ : await provider.request({
80
+ method: 'eth_signTypedData_v4',
81
+ params: [address, JSON.stringify(signRequest.typedData)],
82
+ }));
83
+ }
59
84
  settledRef.current = true;
60
85
  signRequest.resolve(signed);
61
86
  setSignature(signed);
@@ -70,11 +95,11 @@ const SignMessage = () => {
70
95
  if (signature) {
71
96
  return (jsx(PageContent, { onBack: null, children: jsxs(SuccessWrap, { children: [jsx(SuccessCircle, { children: jsx("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: jsx("path", { d: "M20 6 9 17l-5-5", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), jsx(SuccessTitle, { children: "Message signed" }), jsx(SignaturePreview, { children: `${signature.slice(0, 14)}…${signature.slice(-12)}` }), jsx(Button, { variant: "primary", onClick: close, children: "Done" })] }) }));
72
97
  }
73
- return (jsx(PageContent, { onBack: null, children: jsxs(SignContent, { children: [jsxs(Subtitle, { children: [appName, " wants you to sign a message. It will not cost you any fees."] }), signRequest.kind === 'message' ? (jsx(MessageBox, { children: signRequest.message })) : (jsxs(Fragment, { children: [jsx(MessageBox, { "$scroll": true, children: jsx(DataNode, { value: {
74
- domain: signRequest.typedData.domain,
75
- primaryType: signRequest.typedData.primaryType,
76
- message: signRequest.typedData.message,
77
- } }) }), jsx(CopyRow, { children: jsx(CopyButton, { value: JSON.stringify(signRequest.typedData, null, 2), children: "Copy to clipboard" }) })] })), error && jsx(ErrorText, { children: error }), jsx(Button, { variant: "primary", onClick: handleSign, waiting: signing, disabled: signing, arrow: true, children: "Sign and continue" })] }) }));
98
+ return (jsx(PageContent, { onBack: null, children: jsxs(SignContent, { children: [jsxs(Subtitle, { children: [appName, " wants you to sign a message. It will not cost you any fees."] }), signRequest.kind === 'message' ? (jsx(MessageBox, { children: signRequest.message })) : (jsx(MessageBox, { children: jsx(DataNode, { value: {
99
+ domain: signRequest.typedData.domain,
100
+ primaryType: signRequest.typedData.primaryType,
101
+ message: signRequest.typedData.message,
102
+ } }) })), jsxs(Footer, { children: [signRequest.kind === 'typedData' && (jsx(CopyRow, { children: jsx(CopyButton, { value: JSON.stringify(signRequest.typedData, null, 2), children: "Copy to clipboard" }) })), error && jsx(ErrorText, { children: error }), jsx(Button, { variant: "primary", onClick: handleSign, waiting: signing, disabled: signing, arrow: true, children: "Sign and continue" })] })] }) }));
78
103
  };
79
104
 
80
105
  export { SignMessage as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,8 +1,7 @@
1
1
  export declare const SignContent: import("styled-components").StyledComponent<"div", any, {}, never>;
2
2
  export declare const Subtitle: import("styled-components").StyledComponent<"p", any, {}, never>;
3
- export declare const MessageBox: import("styled-components").StyledComponent<"div", any, {
4
- $scroll?: boolean;
5
- }, never>;
3
+ export declare const MessageBox: import("styled-components").StyledComponent<"div", any, {}, never>;
4
+ export declare const Footer: import("styled-components").StyledComponent<"div", any, {}, never>;
6
5
  export declare const DataList: import("styled-components").StyledComponent<"ul", any, {}, never>;
7
6
  export declare const DataItem: import("styled-components").StyledComponent<"li", any, {}, never>;
8
7
  export declare const DataKey: import("styled-components").StyledComponent<"span", any, {}, never>;
@@ -4,8 +4,15 @@ const SignContent = styled.div `
4
4
  display: flex;
5
5
  flex-direction: column;
6
6
  gap: 16px;
7
+ min-height: 0;
8
+ /* Cap to the modal viewport (InnerContainer caps at 88vh) so the message body
9
+ is the only part that scrolls and the Sign button stays pinned and reachable
10
+ on small screens. 112px ≈ PageContent top padding + PageContents padding,
11
+ mirroring DepositWallet's Layout. */
12
+ max-height: calc(88vh - 112px);
7
13
  `;
8
14
  const Subtitle = styled.p `
15
+ flex-shrink: 0;
9
16
  margin: 0;
10
17
  text-align: center;
11
18
  font-size: 15px;
@@ -13,6 +20,11 @@ const Subtitle = styled.p `
13
20
  color: var(--ck-body-color-muted, #999);
14
21
  `;
15
22
  const MessageBox = styled.div `
23
+ flex: 1 1 auto;
24
+ min-height: 0;
25
+ overflow-y: auto;
26
+ -webkit-overflow-scrolling: touch;
27
+ overscroll-behavior: contain;
16
28
  padding: 16px;
17
29
  border-radius: 12px;
18
30
  background: var(--ck-body-background-secondary, rgba(0, 0, 0, 0.04));
@@ -22,7 +34,12 @@ const MessageBox = styled.div `
22
34
  text-align: left;
23
35
  word-break: break-word;
24
36
  white-space: pre-wrap;
25
- ${({ $scroll }) => $scroll && 'max-height: 320px; overflow-y: auto;'}
37
+ `;
38
+ const Footer = styled.div `
39
+ flex-shrink: 0;
40
+ display: flex;
41
+ flex-direction: column;
42
+ gap: 12px;
26
43
  `;
27
44
  const DataList = styled.ul `
28
45
  margin: 0;
@@ -78,5 +95,5 @@ const SignaturePreview = styled.code `
78
95
  text-align: center;
79
96
  `;
80
97
 
81
- export { CopyRow, DataItem, DataKey, DataList, ErrorText, MessageBox, SignContent, SignaturePreview, Subtitle, SuccessCircle, SuccessTitle, SuccessWrap };
98
+ export { CopyRow, DataItem, DataKey, DataList, ErrorText, Footer, MessageBox, SignContent, SignaturePreview, Subtitle, SuccessCircle, SuccessTitle, SuccessWrap };
82
99
  //# 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,18 @@
1
+ import type { Hex } from 'viem';
2
+ /** A default token surfaced when no custom assets are configured. */
3
+ type DefaultAsset = {
4
+ symbol: string;
5
+ name: string;
6
+ decimals: number;
7
+ address: Hex;
8
+ };
9
+ /**
10
+ * Default ERC-20 assets per chain id, mirroring the set returned by
11
+ * `wallet_getAssets` — see https://www.openfort.io/docs/configuration/default-assets.
12
+ * Used to surface common tokens (USDC / USDT / DAI / wrapped native) in the asset
13
+ * inventory even at zero balance. Native tokens come from `getNativeCurrency`.
14
+ */
15
+ export declare const DEFAULT_ASSETS: Record<number, DefaultAsset[]>;
16
+ /** Whether a symbol is a ~$1 stablecoin (for the inventory's USD estimate). */
17
+ export declare function isStableSymbol(symbol: string): boolean;
18
+ export {};
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Default ERC-20 assets per chain id, mirroring the set returned by
3
+ * `wallet_getAssets` — see https://www.openfort.io/docs/configuration/default-assets.
4
+ * Used to surface common tokens (USDC / USDT / DAI / wrapped native) in the asset
5
+ * inventory even at zero balance. Native tokens come from `getNativeCurrency`.
6
+ */
7
+ const DEFAULT_ASSETS = {
8
+ // Ethereum Mainnet
9
+ 1: [
10
+ { symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' },
11
+ { symbol: 'USDT', name: 'Tether USD', decimals: 6, address: '0xdAC17F958D2ee523a2206206994597C13D831ec7' },
12
+ { symbol: 'DAI', name: 'Dai Stablecoin', decimals: 18, address: '0x6B175474E89094C44Da98b954EedeAC495271d0F' },
13
+ { symbol: 'WETH', name: 'Wrapped Ether', decimals: 18, address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' },
14
+ ],
15
+ // Optimism
16
+ 10: [
17
+ { symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85' },
18
+ { symbol: 'USDT', name: 'Tether USD', decimals: 6, address: '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58' },
19
+ { symbol: 'DAI', name: 'Dai Stablecoin', decimals: 18, address: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1' },
20
+ { symbol: 'WETH', name: 'Wrapped Ether', decimals: 18, address: '0x4200000000000000000000000000000000000006' },
21
+ ],
22
+ // BSC
23
+ 56: [
24
+ { symbol: 'USDC', name: 'USD Coin', decimals: 18, address: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d' },
25
+ { symbol: 'USDT', name: 'Tether USD', decimals: 18, address: '0x55d398326f99059fF775485246999027B3197955' },
26
+ { symbol: 'DAI', name: 'Dai Stablecoin', decimals: 18, address: '0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3' },
27
+ { symbol: 'WBNB', name: 'Wrapped BNB', decimals: 18, address: '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c' },
28
+ ],
29
+ // Polygon
30
+ 137: [
31
+ { symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359' },
32
+ { symbol: 'USDT', name: 'Tether USD', decimals: 6, address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F' },
33
+ { symbol: 'DAI', name: 'Dai Stablecoin', decimals: 18, address: '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063' },
34
+ { symbol: 'WMATIC', name: 'Wrapped Matic', decimals: 18, address: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270' },
35
+ ],
36
+ // Base
37
+ 8453: [
38
+ { symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' },
39
+ { symbol: 'DAI', name: 'Dai Stablecoin', decimals: 18, address: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb' },
40
+ { symbol: 'WETH', name: 'Wrapped Ether', decimals: 18, address: '0x4200000000000000000000000000000000000006' },
41
+ ],
42
+ // Arbitrum
43
+ 42161: [
44
+ { symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831' },
45
+ { symbol: 'USDT', name: 'Tether USD', decimals: 6, address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9' },
46
+ { symbol: 'DAI', name: 'Dai Stablecoin', decimals: 18, address: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1' },
47
+ { symbol: 'WETH', name: 'Wrapped Ether', decimals: 18, address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1' },
48
+ ],
49
+ // Avalanche
50
+ 43114: [
51
+ { symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E' },
52
+ { symbol: 'USDT', name: 'Tether USD', decimals: 6, address: '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7' },
53
+ { symbol: 'DAI.e', name: 'Dai Stablecoin', decimals: 18, address: '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70' },
54
+ { symbol: 'WETH.e', name: 'Wrapped Ether', decimals: 18, address: '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB' },
55
+ ],
56
+ // Sepolia
57
+ 11155111: [{ symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238' }],
58
+ // Amoy
59
+ 80002: [{ symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582' }],
60
+ // Base Sepolia
61
+ 84532: [{ symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0x036CbD53842c5426634e7929541eC2318f3dCF7e' }],
62
+ };
63
+ const STABLE_SYMBOLS = new Set(['USDC', 'USDT', 'DAI', 'DAI.E']);
64
+ /** Whether a symbol is a ~$1 stablecoin (for the inventory's USD estimate). */
65
+ function isStableSymbol(symbol) {
66
+ return STABLE_SYMBOLS.has(symbol.toUpperCase());
67
+ }
68
+
69
+ export { DEFAULT_ASSETS, isStableSymbol };
70
+ //# sourceMappingURL=defaultAssets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultAssets.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -21,7 +21,7 @@ import { type SignTypedDataPayload } from '../../components/Openfort/types';
21
21
  * ```
22
22
  */
23
23
  export declare function useSignMessage(): {
24
- signMessage: (message: string) => Promise<`0x${string}`>;
25
- signTypedData: (typedData: SignTypedDataPayload) => Promise<`0x${string}`>;
24
+ signMessage: (message: string) => Promise<string>;
25
+ signTypedData: (typedData: SignTypedDataPayload) => Promise<string>;
26
26
  isPending: boolean;
27
27
  };
@@ -22,7 +22,9 @@ const safeRoutes = {
22
22
  routes.PROVIDERS,
23
23
  routes.PROFILE,
24
24
  routes.SEND,
25
+ routes.SOL_SEND,
25
26
  routes.RECEIVE,
27
+ routes.SOL_RECEIVE,
26
28
  routes.DEPOSIT,
27
29
  routes.BUY,
28
30
  routes.EXPORT_KEY,
@@ -133,8 +135,8 @@ function useUI() {
133
135
  openSwitchNetworks: () => gotoAndOpen(routes.ETH_SWITCH_NETWORK),
134
136
  openProviders: () => gotoAndOpen(routes.PROVIDERS),
135
137
  openWallets: () => gotoAndOpen({ route: routes.CONNECTORS, connectType: 'linkIfUserConnectIfNoUser' }),
136
- openSend: (tx) => tx ? openSendPreview(tx) : gotoAndOpen(routes.SEND),
137
- openReceive: () => gotoAndOpen(routes.RECEIVE),
138
+ openSend: (tx) => tx ? openSendPreview(tx) : gotoAndOpen(chainType === ChainTypeEnum.SVM ? routes.SOL_SEND : routes.SEND),
139
+ openReceive: () => gotoAndOpen(chainType === ChainTypeEnum.SVM ? routes.SOL_RECEIVE : routes.RECEIVE),
138
140
  openFunding: () => gotoAndOpen(routes.DEPOSIT),
139
141
  openBuy: () => gotoAndOpen(routes.BUY),
140
142
  openExportKey: () => gotoAndOpen(routes.EXPORT_KEY),
@@ -1 +1 @@
1
- {"version":3,"file":"useUI.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"useUI.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -58,4 +58,16 @@ type SendSplTokenGaslessParams = {
58
58
  };
59
59
  /** Send an SPL token transfer with fees sponsored by the Openfort paymaster (Kora). */
60
60
  export declare function sendSplTokenGasless({ from, to, mint, amount, provider, cluster, publishableKey, }: SendSplTokenGaslessParams): Promise<string>;
61
+ /**
62
+ * Read the network fee (in lamports) for a single-signer transfer from the RPC
63
+ * via `getFeeForMessage`. Returns null on any failure so callers can fall back to
64
+ * a neutral "--" rather than a fabricated number. The base fee is per-signature
65
+ * and identical for native and SPL transfers (a single fee-payer signature), so a
66
+ * representative SOL transfer message is enough to price it.
67
+ */
68
+ export declare function estimateSolanaTransferFeeLamports({ from, to, rpcUrl, }: {
69
+ from: string;
70
+ to: string;
71
+ rpcUrl: string;
72
+ }): Promise<bigint | null>;
61
73
  export {};
@@ -214,6 +214,34 @@ async function sendSplTokenGasless({ from, to, mint, amount, provider, cluster,
214
214
  publishableKey,
215
215
  });
216
216
  }
217
+ /**
218
+ * Read the network fee (in lamports) for a single-signer transfer from the RPC
219
+ * via `getFeeForMessage`. Returns null on any failure so callers can fall back to
220
+ * a neutral "--" rather than a fabricated number. The base fee is per-signature
221
+ * and identical for native and SPL transfers (a single fee-payer signature), so a
222
+ * representative SOL transfer message is enough to price it.
223
+ */
224
+ async function estimateSolanaTransferFeeLamports({ from, to, rpcUrl, }) {
225
+ try {
226
+ const kit = await import('@solana/kit');
227
+ const { getTransferSolInstruction } = await import('@solana-program/system');
228
+ const fromAddress = kit.address(from);
229
+ const rpc = kit.createSolanaRpc(rpcUrl);
230
+ const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
231
+ const message = kit.pipe(kit.createTransactionMessage({ version: 0 }), (tx) => kit.setTransactionMessageFeePayer(fromAddress, tx), (tx) => kit.setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => kit.appendTransactionMessageInstruction(getTransferSolInstruction({
232
+ source: kit.createNoopSigner(fromAddress),
233
+ destination: kit.address(to),
234
+ amount: kit.lamports(BigInt(1)),
235
+ }), tx));
236
+ const compiled = kit.compileTransactionMessage(message);
237
+ const base64Message = kit.getBase64Decoder().decode(kit.getCompiledTransactionMessageEncoder().encode(compiled));
238
+ const { value } = await rpc.getFeeForMessage(base64Message).send();
239
+ return value == null ? null : BigInt(value);
240
+ }
241
+ catch {
242
+ return null;
243
+ }
244
+ }
217
245
 
218
- export { sendSol, sendSolGasless, sendSplToken, sendSplTokenGasless };
246
+ export { estimateSolanaTransferFeeLamports, sendSol, sendSolGasless, sendSplToken, sendSplTokenGasless };
219
247
  //# sourceMappingURL=transfer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transfer.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"transfer.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- export declare const OPENFORT_VERSION = "1.6.0";
1
+ export declare const OPENFORT_VERSION = "1.6.2";
package/build/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const OPENFORT_VERSION = '1.6.0';
1
+ const OPENFORT_VERSION = '1.6.2';
2
2
 
3
3
  export { OPENFORT_VERSION };
4
4
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfort/react",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
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.",