@openfort/react 1.5.1 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/build/components/Common/Chain/styles.d.ts +10 -0
  2. package/build/components/Common/Chain/styles.js +103 -1
  3. package/build/components/Common/Chain/styles.js.map +1 -1
  4. package/build/components/Common/Modal/index.js +16 -0
  5. package/build/components/Common/Modal/index.js.map +1 -1
  6. package/build/components/Common/SolanaChain/index.d.ts +5 -4
  7. package/build/components/Common/SolanaChain/index.js +9 -17
  8. package/build/components/Common/SolanaChain/index.js.map +1 -1
  9. package/build/components/ConnectModal/index.js +2 -0
  10. package/build/components/ConnectModal/index.js.map +1 -1
  11. package/build/components/Openfort/OpenfortProvider.js +4 -0
  12. package/build/components/Openfort/OpenfortProvider.js.map +1 -1
  13. package/build/components/Openfort/context.d.ts +4 -1
  14. package/build/components/Openfort/types.d.ts +22 -0
  15. package/build/components/Openfort/types.js +1 -0
  16. package/build/components/Openfort/types.js.map +1 -1
  17. package/build/components/Pages/AssetInventory/SolanaAssetInventory.js +7 -7
  18. package/build/components/Pages/Buy/styles.js +10 -10
  19. package/build/components/Pages/Connected/EthereumConnected.js +7 -1
  20. package/build/components/Pages/Connected/EthereumConnected.js.map +1 -1
  21. package/build/components/Pages/Deposit/TestnetNotice.js +15 -3
  22. package/build/components/Pages/Deposit/TestnetNotice.js.map +1 -1
  23. package/build/components/Pages/Deposit/index.js +1 -2
  24. package/build/components/Pages/Deposit/index.js.map +1 -1
  25. package/build/components/Pages/Deposit/sources.d.ts +3 -0
  26. package/build/components/Pages/Deposit/sources.js +4 -1
  27. package/build/components/Pages/Deposit/sources.js.map +1 -1
  28. package/build/components/Pages/Deposit/useFundingTarget.d.ts +6 -4
  29. package/build/components/Pages/Deposit/useFundingTarget.js +18 -7
  30. package/build/components/Pages/Deposit/useFundingTarget.js.map +1 -1
  31. package/build/components/Pages/DepositCex/index.js +2 -1
  32. package/build/components/Pages/DepositCex/index.js.map +1 -1
  33. package/build/components/Pages/DepositWallet/index.js +15 -10
  34. package/build/components/Pages/DepositWallet/index.js.map +1 -1
  35. package/build/components/Pages/ExportKey/index.js +44 -4
  36. package/build/components/Pages/ExportKey/index.js.map +1 -1
  37. package/build/components/Pages/ExportKey/styles.d.ts +5 -0
  38. package/build/components/Pages/ExportKey/styles.js +47 -0
  39. package/build/components/Pages/ExportKey/styles.js.map +1 -0
  40. package/build/components/Pages/Receive/index.js +2 -2
  41. package/build/components/Pages/SelectToken/SolanaSelectToken.js +7 -7
  42. package/build/components/Pages/SelectToken/index.js +7 -3
  43. package/build/components/Pages/SelectToken/index.js.map +1 -1
  44. package/build/components/Pages/Send/EthereumSend.js +32 -6
  45. package/build/components/Pages/Send/EthereumSend.js.map +1 -1
  46. package/build/components/Pages/Send/SolanaSend.js +29 -5
  47. package/build/components/Pages/Send/SolanaSend.js.map +1 -1
  48. package/build/components/Pages/Send/styles.d.ts +17 -12
  49. package/build/components/Pages/Send/styles.js +104 -60
  50. package/build/components/Pages/Send/styles.js.map +1 -1
  51. package/build/components/Pages/SendConfirmation/ConfirmationSummary.d.ts +29 -0
  52. package/build/components/Pages/SendConfirmation/ConfirmationSummary.js +15 -0
  53. package/build/components/Pages/SendConfirmation/ConfirmationSummary.js.map +1 -0
  54. package/build/components/Pages/SendConfirmation/EstimatedFees.d.ts +3 -1
  55. package/build/components/Pages/SendConfirmation/EstimatedFees.js +22 -15
  56. package/build/components/Pages/SendConfirmation/EstimatedFees.js.map +1 -1
  57. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js +18 -6
  58. package/build/components/Pages/SendConfirmation/SolanaSendConfirmation.js.map +1 -1
  59. package/build/components/Pages/SendConfirmation/index.js +23 -33
  60. package/build/components/Pages/SendConfirmation/index.js.map +1 -1
  61. package/build/components/Pages/SendConfirmation/styles.d.ts +9 -1
  62. package/build/components/Pages/SendConfirmation/styles.js +65 -19
  63. package/build/components/Pages/SendConfirmation/styles.js.map +1 -1
  64. package/build/components/Pages/SignMessage/index.d.ts +2 -0
  65. package/build/components/Pages/SignMessage/index.js +106 -0
  66. package/build/components/Pages/SignMessage/index.js.map +1 -0
  67. package/build/components/Pages/SignMessage/styles.d.ts +13 -0
  68. package/build/components/Pages/SignMessage/styles.js +99 -0
  69. package/build/components/Pages/SignMessage/styles.js.map +1 -0
  70. package/build/hooks/openfort/useSignMessage.d.ts +27 -0
  71. package/build/hooks/openfort/useSignMessage.js +52 -0
  72. package/build/hooks/openfort/useSignMessage.js.map +1 -0
  73. package/build/hooks/openfort/useUI.d.ts +6 -1
  74. package/build/hooks/openfort/useUI.js +15 -3
  75. package/build/hooks/openfort/useUI.js.map +1 -1
  76. package/build/index.d.ts +2 -1
  77. package/build/index.js +1 -0
  78. package/build/index.js.map +1 -1
  79. package/build/localizations/locales/en-US.js +1 -1
  80. package/build/solana/transfer.d.ts +12 -0
  81. package/build/solana/transfer.js +29 -1
  82. package/build/solana/transfer.js.map +1 -1
  83. package/build/utils/rpc.d.ts +6 -0
  84. package/build/utils/rpc.js +12 -1
  85. package/build/utils/rpc.js.map +1 -1
  86. package/build/version.d.ts +1 -1
  87. package/build/version.js +1 -1
  88. package/build/wagmi/components/ChainSelect/index.js +1 -93
  89. package/build/wagmi/components/ChainSelect/index.js.map +1 -1
  90. package/package.json +1 -1
@@ -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;
@@ -45,26 +46,64 @@ const FeesValue = styled(SummaryValue) `
45
46
  text-decoration: ${(props) => (props.$completed ? 'line-through' : 'none')};
46
47
  opacity: ${(props) => (props.$completed ? 0.6 : 1)};
47
48
  `;
48
- const CheckIconWrapper = styled.span `
49
- color: var(--ck-body-color);
50
- line-height: 0;
49
+ const FiatValue = styled.span `
50
+ margin-left: 6px;
51
+ font-size: 13px;
52
+ font-weight: 400;
53
+ color: var(--ck-body-color-muted);
54
+ `;
55
+ const NetworkValue = styled(SummaryValue) `
51
56
  display: inline-flex;
52
57
  align-items: center;
53
-
54
- svg {
55
- width: 16px;
56
- height: 16px;
58
+ justify-content: flex-end;
59
+ gap: 6px;
60
+
61
+ svg,
62
+ img {
63
+ width: 18px;
64
+ height: 18px;
65
+ border-radius: 50%;
57
66
  }
58
67
  `;
59
- // export const BalanceSpinnerWrapper = styled.span`
60
- // display: inline-flex;
61
- // align-items: center;
62
- // margin-left: 8px;
63
- // svg {
64
- // width: 14px;
65
- // height: 14px;
66
- // }
67
- // `
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 `
74
+ color: var(--ck-body-color-valid, #16a34a);
75
+ font-weight: 600;
76
+ `;
77
+ const PayWithCard = styled.div `
78
+ display: flex;
79
+ align-items: center;
80
+ justify-content: space-between;
81
+ gap: 12px;
82
+ margin-top: 4px;
83
+ padding: 12px 14px;
84
+ border-radius: 14px;
85
+ background: var(--ck-body-background-secondary, rgba(0, 0, 0, 0.04));
86
+ `;
87
+ const PayWithMeta = styled.div `
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: 2px;
91
+ text-align: left;
92
+ `;
93
+ const PayWithAddress = styled.span `
94
+ font-size: 14px;
95
+ font-weight: 600;
96
+ color: var(--ck-body-color);
97
+ `;
98
+ const PayWithBadge = styled.span `
99
+ flex-shrink: 0;
100
+ padding: 4px 10px;
101
+ border-radius: 999px;
102
+ font-size: 13px;
103
+ font-weight: 600;
104
+ color: var(--ck-body-color);
105
+ background: var(--ck-body-background);
106
+ `;
68
107
  const InfoIconWrapper = styled.span `
69
108
  color: var(--ck-body-color-muted);
70
109
  opacity: 0.6;
@@ -83,9 +122,16 @@ const InfoIconWrapper = styled.span `
83
122
  `;
84
123
  const ButtonRow = styled.div `
85
124
  display: flex;
86
- flex-direction: column;
87
- gap: 4px;
125
+ gap: 12px;
88
126
  margin-top: 24px;
127
+
128
+ > button {
129
+ flex: 1;
130
+ }
131
+
132
+ ${ButtonContainer} {
133
+ margin: 0;
134
+ }
89
135
  `;
90
136
  const StatusMessage = styled.div `
91
137
  margin-top: 16px;
@@ -125,5 +171,5 @@ const ErrorAction = styled.div `
125
171
  line-height: 1.4;
126
172
  `;
127
173
 
128
- export { AddressValue, AmountValue, ButtonRow, CheckIconWrapper, ErrorAction, ErrorContainer, ErrorMessage, ErrorTitle, FeesValue, InfoIconWrapper, 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 };
129
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,2 @@
1
+ declare const SignMessage: () => import("react/jsx-runtime").JSX.Element | null;
2
+ export default SignMessage;
@@ -0,0 +1,106 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { ChainTypeEnum } from '@openfort/openfort-js';
3
+ import { useState, useRef, useEffect } from 'react';
4
+ import { useEthereumEmbeddedWallet } from '../../../ethereum/hooks/useEthereumEmbeddedWallet.js';
5
+ import { useOpenfortCore } from '../../../openfort/useOpenfort.js';
6
+ import { useSolanaEmbeddedWallet } from '../../../solana/hooks/useSolanaEmbeddedWallet.js';
7
+ import Button from '../../Common/Button/index.js';
8
+ import { CopyButton } from '../../Common/CopyToClipboard/CopyButton.js';
9
+ import { useOpenfort } from '../../Openfort/useOpenfort.js';
10
+ import { PageContent } from '../../PageContent/index.js';
11
+ import { SuccessWrap, SuccessCircle, SuccessTitle, SignaturePreview, SignContent, Subtitle, MessageBox, Footer, CopyRow, ErrorText, DataList, DataItem, DataKey } from './styles.js';
12
+
13
+ /** Renders an EIP-712 value as a readable nested bullet list. */
14
+ function DataNode({ value }) {
15
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
16
+ return (jsx(DataList, { children: Object.entries(value).map(([key, child]) => (jsxs(DataItem, { children: [jsxs(DataKey, { children: [key, ":"] }), ' ', child !== null && typeof child === 'object' ? jsx(DataNode, { value: child }) : String(child)] }, key))) }));
17
+ }
18
+ return jsx(Fragment, { children: String(value) });
19
+ }
20
+ const SignMessage = () => {
21
+ var _a;
22
+ const { signRequest, setSignRequest, setOpen, uiConfig, triggerResize } = useOpenfort();
23
+ const { chainType } = useOpenfortCore();
24
+ const wallet = useEthereumEmbeddedWallet();
25
+ const solana = useSolanaEmbeddedWallet();
26
+ const [signing, setSigning] = useState(false);
27
+ const [error, setError] = useState(null);
28
+ const [signature, setSignature] = useState(null);
29
+ const settledRef = useRef(false);
30
+ const mountedRef = useRef(false);
31
+ // Reject the pending request if the screen unmounts before signing (the user
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.
36
+ useEffect(() => {
37
+ mountedRef.current = true;
38
+ const request = signRequest;
39
+ return () => {
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
+ });
46
+ };
47
+ }, [signRequest]);
48
+ // Content height changes between the views; re-measure.
49
+ useEffect(() => {
50
+ triggerResize();
51
+ }, [triggerResize]);
52
+ if (!signRequest)
53
+ return null;
54
+ const appName = (_a = uiConfig.appName) !== null && _a !== void 0 ? _a : 'This app';
55
+ const close = () => {
56
+ setSignRequest(null);
57
+ setOpen(false);
58
+ };
59
+ const handleSign = async () => {
60
+ var _a;
61
+ setError(null);
62
+ setSigning(true);
63
+ try {
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
+ }
84
+ settledRef.current = true;
85
+ signRequest.resolve(signed);
86
+ setSignature(signed);
87
+ setSigning(false);
88
+ triggerResize();
89
+ }
90
+ catch (e) {
91
+ setError(e instanceof Error ? e.message : 'Failed to sign the message');
92
+ setSigning(false);
93
+ }
94
+ };
95
+ if (signature) {
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" })] }) }));
97
+ }
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" })] })] }) }));
103
+ };
104
+
105
+ export { SignMessage as default };
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,13 @@
1
+ export declare const SignContent: import("styled-components").StyledComponent<"div", any, {}, never>;
2
+ export declare const Subtitle: import("styled-components").StyledComponent<"p", any, {}, 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>;
5
+ export declare const DataList: import("styled-components").StyledComponent<"ul", any, {}, never>;
6
+ export declare const DataItem: import("styled-components").StyledComponent<"li", any, {}, never>;
7
+ export declare const DataKey: import("styled-components").StyledComponent<"span", any, {}, never>;
8
+ export declare const ErrorText: import("styled-components").StyledComponent<"div", any, {}, never>;
9
+ export declare const CopyRow: import("styled-components").StyledComponent<"div", any, {}, never>;
10
+ export declare const SuccessWrap: import("styled-components").StyledComponent<"div", any, {}, never>;
11
+ export declare const SuccessCircle: import("styled-components").StyledComponent<"div", any, {}, never>;
12
+ export declare const SuccessTitle: import("styled-components").StyledComponent<"div", any, {}, never>;
13
+ export declare const SignaturePreview: import("styled-components").StyledComponent<"code", any, {}, never>;
@@ -0,0 +1,99 @@
1
+ import styled from '../../../styles/styled/index.js';
2
+
3
+ const SignContent = styled.div `
4
+ display: flex;
5
+ flex-direction: column;
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);
13
+ `;
14
+ const Subtitle = styled.p `
15
+ flex-shrink: 0;
16
+ margin: 0;
17
+ text-align: center;
18
+ font-size: 15px;
19
+ line-height: 1.4;
20
+ color: var(--ck-body-color-muted, #999);
21
+ `;
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;
28
+ padding: 16px;
29
+ border-radius: 12px;
30
+ background: var(--ck-body-background-secondary, rgba(0, 0, 0, 0.04));
31
+ color: var(--ck-body-color, #111);
32
+ font-size: 14px;
33
+ line-height: 1.45;
34
+ text-align: left;
35
+ word-break: break-word;
36
+ white-space: pre-wrap;
37
+ `;
38
+ const Footer = styled.div `
39
+ flex-shrink: 0;
40
+ display: flex;
41
+ flex-direction: column;
42
+ gap: 12px;
43
+ `;
44
+ const DataList = styled.ul `
45
+ margin: 0;
46
+ padding-left: 18px;
47
+ list-style: disc;
48
+ `;
49
+ const DataItem = styled.li `
50
+ margin: 2px 0;
51
+ `;
52
+ const DataKey = styled.span `
53
+ color: var(--ck-body-color-muted, #777);
54
+ `;
55
+ const ErrorText = styled.div `
56
+ text-align: center;
57
+ font-size: 13px;
58
+ color: var(--ck-body-color-danger, #e7000b);
59
+ `;
60
+ const CopyRow = styled.div `
61
+ display: flex;
62
+ justify-content: center;
63
+ `;
64
+ const SuccessWrap = styled.div `
65
+ display: flex;
66
+ flex-direction: column;
67
+ align-items: center;
68
+ gap: 12px;
69
+ padding: 12px 0 4px;
70
+ `;
71
+ const SuccessCircle = styled.div `
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ width: 56px;
76
+ height: 56px;
77
+ border-radius: 50%;
78
+ color: #fff;
79
+ background: var(--ck-body-color-valid, #16a34a);
80
+ `;
81
+ const SuccessTitle = styled.div `
82
+ font-size: 17px;
83
+ font-weight: 600;
84
+ color: var(--ck-body-color, #111);
85
+ `;
86
+ const SignaturePreview = styled.code `
87
+ display: block;
88
+ max-width: 100%;
89
+ padding: 10px 12px;
90
+ border-radius: 10px;
91
+ background: var(--ck-body-background-secondary, rgba(0, 0, 0, 0.04));
92
+ color: var(--ck-body-color-muted, #777);
93
+ font-size: 12px;
94
+ word-break: break-all;
95
+ text-align: center;
96
+ `;
97
+
98
+ export { CopyRow, DataItem, DataKey, DataList, ErrorText, Footer, MessageBox, SignContent, SignaturePreview, Subtitle, SuccessCircle, SuccessTitle, SuccessWrap };
99
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,27 @@
1
+ import { type SignTypedDataPayload } from '../../components/Openfort/types';
2
+ /**
3
+ * Hook for signing messages with a confirmation modal (EVM only).
4
+ *
5
+ * Opens the Openfort "Sign message" screen, shows the message (or EIP-712 typed
6
+ * data) to the user, and resolves with the signature once they confirm. Rejects
7
+ * if the user dismisses the modal.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * const { signMessage, signTypedData, isPending } = useSignMessage()
12
+ *
13
+ * const signature = await signMessage('I hereby vote for foobar')
14
+ *
15
+ * const typedSignature = await signTypedData({
16
+ * domain: { name: 'Ether Mail', version: '1', chainId: 1, verifyingContract: '0x…' },
17
+ * types: { Person: [{ name: 'name', type: 'string' }] },
18
+ * primaryType: 'Person',
19
+ * message: { name: 'Cow' },
20
+ * })
21
+ * ```
22
+ */
23
+ export declare function useSignMessage(): {
24
+ signMessage: (message: string) => Promise<string>;
25
+ signTypedData: (typedData: SignTypedDataPayload) => Promise<string>;
26
+ isPending: boolean;
27
+ };
@@ -0,0 +1,52 @@
1
+ import { useState, useCallback } from 'react';
2
+ import { routes } from '../../components/Openfort/types.js';
3
+ import { useOpenfort } from '../../components/Openfort/useOpenfort.js';
4
+
5
+ /**
6
+ * Hook for signing messages with a confirmation modal (EVM only).
7
+ *
8
+ * Opens the Openfort "Sign message" screen, shows the message (or EIP-712 typed
9
+ * data) to the user, and resolves with the signature once they confirm. Rejects
10
+ * if the user dismisses the modal.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const { signMessage, signTypedData, isPending } = useSignMessage()
15
+ *
16
+ * const signature = await signMessage('I hereby vote for foobar')
17
+ *
18
+ * const typedSignature = await signTypedData({
19
+ * domain: { name: 'Ether Mail', version: '1', chainId: 1, verifyingContract: '0x…' },
20
+ * types: { Person: [{ name: 'name', type: 'string' }] },
21
+ * primaryType: 'Person',
22
+ * message: { name: 'Cow' },
23
+ * })
24
+ * ```
25
+ */
26
+ function useSignMessage() {
27
+ const { setSignRequest, setRoute, setOpen } = useOpenfort();
28
+ const [isPending, setIsPending] = useState(false);
29
+ const request = useCallback((args) => new Promise((resolve, reject) => {
30
+ setIsPending(true);
31
+ // setOpen(true) resets route/history, so it MUST run before setRoute.
32
+ setOpen(true);
33
+ setSignRequest({
34
+ ...args,
35
+ resolve: (signature) => {
36
+ setIsPending(false);
37
+ resolve(signature);
38
+ },
39
+ reject: (reason) => {
40
+ setIsPending(false);
41
+ reject(reason);
42
+ },
43
+ });
44
+ setRoute(routes.SIGN_MESSAGE);
45
+ }), [setSignRequest, setRoute, setOpen]);
46
+ const signMessage = useCallback((message) => request({ kind: 'message', message }), [request]);
47
+ const signTypedData = useCallback((typedData) => request({ kind: 'typedData', typedData }), [request]);
48
+ return { signMessage, signTypedData, isPending };
49
+ }
50
+
51
+ export { useSignMessage };
52
+ //# sourceMappingURL=useSignMessage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSignMessage.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,3 +1,4 @@
1
+ import { type Asset } from '../../components/Openfort/types';
1
2
  /**
2
3
  * Hook for controlling Openfort UI modal and navigation
3
4
  *
@@ -35,7 +36,11 @@ export declare function useUI(): {
35
36
  openSwitchNetworks: () => void;
36
37
  openProviders: () => void;
37
38
  openWallets: () => void;
38
- openSend: () => void;
39
+ openSend: (tx?: {
40
+ to: string;
41
+ amount: string;
42
+ asset?: Asset;
43
+ }) => void;
39
44
  openReceive: () => void;
40
45
  openFunding: () => void;
41
46
  openBuy: () => void;
@@ -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,
@@ -68,7 +70,7 @@ function isAccountId(id) {
68
70
  */
69
71
  function useUI() {
70
72
  var _a;
71
- const { open, setOpen, setRoute, setConnector, connector, chainType } = useOpenfort();
73
+ const { open, setOpen, setRoute, setConnector, setSendForm, connector, chainType } = useOpenfort();
72
74
  const { isLoading, user, needsRecovery, embeddedAccounts, activeEmbeddedAddress, embeddedState } = useOpenfortCore();
73
75
  const bridge = useEthereumBridge();
74
76
  const strategy = useConnectionStrategy();
@@ -100,6 +102,16 @@ function useUI() {
100
102
  else
101
103
  setRoute(routes.CONNECTED);
102
104
  }
105
+ /**
106
+ * Prefill the send form and jump straight to the confirmation (preview) screen
107
+ * for the active chain, skipping asset/amount/recipient entry.
108
+ */
109
+ const openSendPreview = (tx) => {
110
+ var _a;
111
+ setSendForm({ recipient: tx.to, amount: tx.amount, asset: (_a = tx.asset) !== null && _a !== void 0 ? _a : { type: 'native', balance: BigInt(0) } });
112
+ setOpen(true);
113
+ setRoute(chainType === ChainTypeEnum.SVM ? routes.SOL_SEND_CONFIRMATION : routes.SEND_CONFIRMATION);
114
+ };
103
115
  const gotoAndOpen = (route) => {
104
116
  const safeList = isConnected ? safeRoutes.connected : safeRoutes.disconnected;
105
117
  const fallback = isConnected ? routes.CONNECTED : routes.PROVIDERS;
@@ -123,8 +135,8 @@ function useUI() {
123
135
  openSwitchNetworks: () => gotoAndOpen(routes.ETH_SWITCH_NETWORK),
124
136
  openProviders: () => gotoAndOpen(routes.PROVIDERS),
125
137
  openWallets: () => gotoAndOpen({ route: routes.CONNECTORS, connectType: 'linkIfUserConnectIfNoUser' }),
126
- openSend: () => gotoAndOpen(routes.SEND),
127
- 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),
128
140
  openFunding: () => gotoAndOpen(routes.DEPOSIT),
129
141
  openBuy: () => gotoAndOpen(routes.BUY),
130
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/build/index.d.ts CHANGED
@@ -44,7 +44,7 @@ export { default as Avatar } from './components/Common/Avatar';
44
44
  export { default as ChainIcon } from './components/Common/Chain';
45
45
  export { OpenfortButton } from './components/ConnectButton';
46
46
  export { OpenfortProvider } from './components/Openfort/OpenfortProvider';
47
- export type { CustomizableRoutes, MultiChainAsset } from './components/Openfort/types';
47
+ export type { CustomizableRoutes, MultiChainAsset, SignTypedDataPayload } from './components/Openfort/types';
48
48
  export { FundingMethod, LinkWalletOnSignUpOption, UIAuthProvider as AuthProvider } from './components/Openfort/types';
49
49
  export { embeddedWalletId } from './constants/openfort';
50
50
  export { OpenfortError, OpenfortReactErrorType, OpenfortReactErrorType as OpenfortErrorType, } from './core/errors';
@@ -64,6 +64,7 @@ export type { FundingSession, FundingTarget, PaymentMethod, PaymentMethodInput,
64
64
  export { useFunding } from './hooks/openfort/useFunding';
65
65
  export { useGrantPermissions } from './hooks/openfort/useGrantPermissions';
66
66
  export { useRevokePermissions } from './hooks/openfort/useRevokePermissions';
67
+ export { useSignMessage } from './hooks/openfort/useSignMessage';
67
68
  export { useUI } from './hooks/openfort/useUI';
68
69
  export { useUser } from './hooks/openfort/useUser';
69
70
  export type { UserWallet } from './hooks/openfort/walletTypes';
package/build/index.js CHANGED
@@ -17,6 +17,7 @@ export { use7702Authorization } from './hooks/openfort/use7702Authorization.js';
17
17
  export { useFunding } from './hooks/openfort/useFunding.js';
18
18
  export { useGrantPermissions } from './hooks/openfort/useGrantPermissions.js';
19
19
  export { useRevokePermissions } from './hooks/openfort/useRevokePermissions.js';
20
+ export { useSignMessage } from './hooks/openfort/useSignMessage.js';
20
21
  export { useUI } from './hooks/openfort/useUI.js';
21
22
  export { useUser } from './hooks/openfort/useUser.js';
22
23
  export { invalidateBalance } from './hooks/useBalance.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -86,7 +86,7 @@ const enUS = {
86
86
  injectionScreen_notconnected_h1: `Login to {{ CONNECTORNAME }}`,
87
87
  injectionScreen_notconnected_p: `To continue, please login to your {{ CONNECTORNAME }} extension.`,
88
88
  profileScreen_heading: 'Connected',
89
- buyScreen_heading: 'Add funds',
89
+ buyScreen_heading: 'Add funds from card',
90
90
  buyScreen_subheading: 'Select an amount and token to top up.',
91
91
  buyScreen_payWithCard_title: 'Pay with card',
92
92
  buyScreen_payWithCard_description: 'Use Stripe or Google Pay for a quick card purchase.',
@@ -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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -6,6 +6,12 @@
6
6
  */
7
7
  import type { Chain } from 'viem';
8
8
  import type { SolanaCluster } from '../solana/types';
9
+ /**
10
+ * Whether an EVM chain id is a testnet. Reads viem's chain metadata (`testnet`)
11
+ * for the chains the SDK bundles, falling back to a small extra set. Use this to
12
+ * key behaviour off the wallet's active chain rather than the publishable key.
13
+ */
14
+ export declare function isTestnetChainId(chainId: number): boolean;
9
15
  export declare function getDefaultEthereumRpcUrl(chainId: number): string;
10
16
  /**
11
17
  * Get default Solana RPC URL for a cluster.
@@ -17,6 +17,17 @@ const KNOWN_CHAINS = {
17
17
  [optimismSepolia.id]: optimismSepolia,
18
18
  [arbitrumSepolia.id]: arbitrumSepolia,
19
19
  };
20
+ /** Testnets not in {@link KNOWN_CHAINS} but still worth recognizing (deprecated/uncommon). */
21
+ const EXTRA_TESTNET_CHAIN_IDS = new Set([5, 80001, 97, 4002]);
22
+ /**
23
+ * Whether an EVM chain id is a testnet. Reads viem's chain metadata (`testnet`)
24
+ * for the chains the SDK bundles, falling back to a small extra set. Use this to
25
+ * key behaviour off the wallet's active chain rather than the publishable key.
26
+ */
27
+ function isTestnetChainId(chainId) {
28
+ var _a;
29
+ return ((_a = KNOWN_CHAINS[chainId]) === null || _a === void 0 ? void 0 : _a.testnet) === true || EXTRA_TESTNET_CHAIN_IDS.has(chainId);
30
+ }
20
31
  /**
21
32
  * Default Solana RPC URLs by cluster.
22
33
  * Production apps should provide their own RPCs via walletConfig.solana.rpcUrls.
@@ -93,5 +104,5 @@ function buildChainFromConfig(chainId, rpcUrls) {
93
104
  });
94
105
  }
95
106
 
96
- export { buildChainFromConfig, getChainName, getDefaultEthereumRpcUrl, getDefaultSolanaRpcUrl, getNativeCurrency };
107
+ export { buildChainFromConfig, getChainName, getDefaultEthereumRpcUrl, getDefaultSolanaRpcUrl, getNativeCurrency, isTestnetChainId };
97
108
  //# sourceMappingURL=rpc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rpc.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"rpc.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- export declare const OPENFORT_VERSION = "1.5.1";
1
+ export declare const OPENFORT_VERSION = "1.6.1";