@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
@@ -3,110 +3,154 @@ import styled from '../../../styles/styled/index.js';
3
3
  const Form = styled.form `
4
4
  display: flex;
5
5
  flex-direction: column;
6
- gap: 20px;
6
+ gap: 14px;
7
7
  margin-top: 20px;
8
8
  `;
9
- const Field = styled.div `
9
+ /** Rounded container shared by the "To" and "Amount" boxes. */
10
+ const SendCard = styled.div `
10
11
  display: flex;
11
12
  flex-direction: column;
13
+ gap: 10px;
14
+ padding: 14px 16px;
15
+ border-radius: var(--ck-secondary-button-border-radius);
16
+ border: 1px solid var(--ck-body-divider);
17
+ background: var(--ck-secondary-button-background);
12
18
  text-align: left;
13
19
  `;
14
- const FieldLabel = styled.span `
15
- font-size: 14px;
20
+ const CardLabel = styled.span `
21
+ font-size: 13px;
16
22
  font-weight: 600;
23
+ color: var(--ck-body-color-muted);
24
+ `;
25
+ /** "To" box: inline label, recipient input, and a Paste button. */
26
+ const ToRow = styled.div `
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 10px;
30
+ `;
31
+ const RecipientInput = styled.input `
32
+ flex: 1;
33
+ min-width: 0;
34
+ border: none;
35
+ background: transparent;
17
36
  color: var(--ck-body-color);
37
+ font-size: 15px;
38
+ font-weight: 500;
39
+ padding: 0;
40
+ outline: none;
41
+
42
+ &::placeholder {
43
+ color: var(--ck-body-color-muted);
44
+ }
18
45
  `;
19
- const TokenSelectorButton = styled.button `
46
+ const PasteButton = styled.button `
47
+ flex-shrink: 0;
48
+ padding: 6px 12px;
49
+ border-radius: 16px;
50
+ border: 1px solid var(--ck-body-divider);
51
+ background: var(--ck-body-background);
52
+ color: var(--ck-body-color);
53
+ font-size: 12px;
54
+ font-weight: 600;
55
+ cursor: pointer;
56
+ transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
57
+
58
+ &:hover {
59
+ background: var(--ck-secondary-button-hover-background);
60
+ }
61
+ `;
62
+ /** "Amount" box: large amount input with an inline token selector. */
63
+ const AmountRow = styled.div `
20
64
  display: flex;
21
65
  align-items: center;
22
- justify-content: space-between;
23
- width: 100%;
24
- margin-top: 12px;
25
- padding: 12px 16px;
26
- border-radius: var(--ck-secondary-button-border-radius);
66
+ gap: 10px;
67
+ `;
68
+ const AmountField = styled.input `
69
+ flex: 1;
70
+ min-width: 0;
71
+ border: none;
72
+ background: transparent;
73
+ color: var(--ck-body-color);
74
+ font-size: 28px;
75
+ font-weight: 600;
76
+ line-height: 1.1;
77
+ padding: 0;
78
+ outline: none;
79
+
80
+ &::placeholder {
81
+ color: var(--ck-body-color-muted);
82
+ }
83
+ `;
84
+ const PillLogo = styled.span `
85
+ display: flex;
86
+ align-items: center;
87
+ width: 22px;
88
+ flex-shrink: 0;
89
+ `;
90
+ const TokenPill = styled.button `
91
+ display: flex;
92
+ align-items: center;
93
+ gap: 6px;
94
+ flex-shrink: 0;
95
+ padding: 7px 12px;
96
+ border-radius: 999px;
27
97
  border: 1px solid var(--ck-body-divider);
28
- background: var(--ck-secondary-button-background);
98
+ background: var(--ck-body-background);
29
99
  color: var(--ck-body-color);
100
+ font-size: 14px;
101
+ font-weight: 600;
30
102
  cursor: pointer;
31
103
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
32
- text-align: left;
33
104
 
34
105
  &:hover {
35
106
  background: var(--ck-secondary-button-hover-background);
36
107
  border-color: var(--ck-body-color-muted);
37
108
  }
38
109
  `;
39
- const TokenSelectorContent = styled.div `
110
+ /** Bottom row of the amount box: fiat value (left), balance + Use max (right). */
111
+ const AmountMeta = styled.div `
40
112
  display: flex;
41
- flex-direction: column;
42
- gap: 2px;
43
- overflow: hidden;
113
+ align-items: center;
114
+ justify-content: space-between;
115
+ gap: 8px;
44
116
  `;
45
- const TokenSelectorValue = styled.span `
46
- font-size: ${(props) => (props.$primary ? '15px' : '13px')};
47
- font-weight: ${(props) => (props.$primary ? 600 : 500)};
48
- color: ${(props) => {
49
- if (props.$primary)
50
- return 'var(--ck-body-color)';
51
- if (props.$muted)
52
- return 'var(--ck-body-color-muted)';
53
- return 'var(--ck-body-color-muted)';
54
- }};
117
+ const MetaText = styled.span `
118
+ font-size: 13px;
119
+ font-weight: 500;
120
+ color: var(--ck-body-color-muted);
55
121
  white-space: nowrap;
56
122
  overflow: hidden;
57
123
  text-overflow: ellipsis;
58
124
  `;
59
- const TokenSelectorRight = styled.div `
125
+ const BalanceMeta = styled.span `
60
126
  display: flex;
61
127
  align-items: center;
62
128
  gap: 6px;
129
+ flex-shrink: 0;
130
+ font-size: 13px;
131
+ font-weight: 500;
63
132
  color: var(--ck-body-color-muted);
64
133
  `;
65
- const AmountInputWrapper = styled.div `
66
- position: relative;
67
- margin-top: 12px;
68
-
69
- > div {
70
- margin: 0;
71
- }
72
- `;
73
- const MaxButton = styled.button `
74
- position: absolute;
75
- right: 12px;
76
- top: 50%;
77
- transform: translateY(-50%);
78
- padding: 6px 14px;
79
- border-radius: 16px;
80
- border: 1px solid var(--ck-body-divider);
81
- background: var(--ck-body-background);
82
- color: var(--ck-body-color);
83
- font-size: 12px;
134
+ const UseMaxButton = styled.button `
135
+ border: none;
136
+ background: none;
137
+ padding: 0;
138
+ font-size: 13px;
84
139
  font-weight: 600;
140
+ color: var(--ck-accent-color, var(--ck-focus-color, #1a88f8));
85
141
  cursor: pointer;
86
- transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
87
-
88
- &:hover {
89
- background: var(--ck-secondary-button-hover-background);
90
- }
91
142
 
92
143
  &:disabled {
93
144
  opacity: 0.5;
94
145
  cursor: not-allowed;
95
- background: var(--ck-body-background-secondary);
96
146
  }
97
147
  `;
98
- const HelperText = styled.span `
99
- display: block;
100
- margin-top: 8px;
101
- font-size: 13px;
102
- color: var(--ck-body-color-muted);
103
- `;
104
148
  const ErrorText = styled.span `
105
149
  display: block;
106
- margin-top: 6px;
150
+ margin-top: 2px;
107
151
  font-size: 13px;
108
152
  color: var(--ck-body-color-danger);
109
153
  `;
110
154
 
111
- export { AmountInputWrapper, ErrorText, Field, FieldLabel, Form, HelperText, MaxButton, TokenSelectorButton, TokenSelectorContent, TokenSelectorRight, TokenSelectorValue };
155
+ export { AmountField, AmountMeta, AmountRow, BalanceMeta, CardLabel, ErrorText, Form, MetaText, PasteButton, PillLogo, RecipientInput, SendCard, ToRow, TokenPill, UseMaxButton };
112
156
  //# 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,29 @@
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
+ /** Current balance of the pay-with wallet, e.g. "134.32 USDC". */
21
+ balance?: string;
22
+ }
23
+ /**
24
+ * Shared, chain-agnostic transaction preview used by the EVM and Solana send
25
+ * confirmation screens. Renders the approval-style rows (Total / To / Network /
26
+ * Estimated fee) plus a "Pay with" card.
27
+ */
28
+ export declare function ConfirmationSummary({ amount, symbol, fiat, to, networkName, networkIcon, fee, payWith, balance, }: ConfirmationSummaryProps): import("react/jsx-runtime").JSX.Element;
29
+ 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, balance, }) {
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 })) : ('--') })] }), balance ? jsx(PayWithBadge, { children: balance }) : null] })] }));
12
+ }
13
+
14
+ export { ConfirmationSummary };
15
+ //# sourceMappingURL=ConfirmationSummary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfirmationSummary.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;"}
@@ -8,6 +8,8 @@ type EstimatedFeesProps = {
8
8
  nativeSymbol: string;
9
9
  enabled?: boolean;
10
10
  hideInfoIcon?: boolean;
11
+ /** When fees are sponsored, show the estimate struck through next to "Sponsored". */
12
+ sponsored?: boolean;
11
13
  };
12
- export declare const EstimatedFees: ({ account, to, value, data, chainId, nativeSymbol, enabled, hideInfoIcon, }: EstimatedFeesProps) => import("react/jsx-runtime").JSX.Element;
14
+ export declare const EstimatedFees: ({ account, to, value, data, chainId, nativeSymbol, enabled, hideInfoIcon, sponsored, }: EstimatedFeesProps) => import("react/jsx-runtime").JSX.Element;
13
15
  export {};
@@ -1,4 +1,4 @@
1
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { createPublicClient, http, formatUnits } from 'viem';
3
3
  import { useEthereumWalletAssets } from '../../../ethereum/hooks/useEthereumWalletAssets.js';
4
4
  import { useAsyncData } from '../../../shared/hooks/useAsyncData.js';
@@ -7,7 +7,7 @@ import { getDefaultEthereumRpcUrl } from '../../../utils/rpc.js';
7
7
  import Tooltip from '../../Common/Tooltip/index.js';
8
8
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
9
9
  import { formatBalance } from '../Send/utils.js';
10
- import { InfoIconWrapper } from './styles.js';
10
+ import { InfoIconWrapper, FeeStrike, SponsoredText } from './styles.js';
11
11
 
12
12
  const InfoIcon = () => (jsxs("svg", { "aria-hidden": "true", width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsx("circle", { cx: "7", cy: "7", r: "6", stroke: "currentColor", strokeWidth: "1.5" }), jsx("path", { d: "M7 10V6.5M7 4.5H7.005", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })] }));
13
13
  const usdFormatter = new Intl.NumberFormat('en-US', {
@@ -16,8 +16,8 @@ const usdFormatter = new Intl.NumberFormat('en-US', {
16
16
  minimumFractionDigits: 2,
17
17
  maximumFractionDigits: 4,
18
18
  });
19
- const EstimatedFees = ({ account, to, value, data, chainId, nativeSymbol, enabled = true, hideInfoIcon = false, }) => {
20
- var _a, _b, _c;
19
+ const EstimatedFees = ({ account, to, value, data, chainId, nativeSymbol, enabled = true, hideInfoIcon = false, sponsored = false, }) => {
20
+ var _a, _b, _c, _d;
21
21
  const { walletConfig } = useOpenfort();
22
22
  const { data: assets } = useEthereumWalletAssets();
23
23
  const pricePerToken = (_c = (_b = (_a = assets === null || assets === void 0 ? void 0 : assets.find((a) => a.type === 'native')) === null || _a === void 0 ? void 0 : _a.metadata) === null || _b === void 0 ? void 0 : _b.fiat) === null || _c === void 0 ? void 0 : _c.value;
@@ -49,19 +49,26 @@ const EstimatedFees = ({ account, to, value, data, chainId, nativeSymbol, enable
49
49
  },
50
50
  enabled: enabled && !!account && !!to && !!chainId,
51
51
  });
52
- // Handle query states
53
- if (!gas.data || gas.error) {
54
- return jsx(Fragment, { children: "--" });
52
+ // Format the estimate (USD when a native price is known, otherwise native units).
53
+ const gasUnits = (_d = gas.data) === null || _d === void 0 ? void 0 : _d.gasLimit;
54
+ let feeText = null;
55
+ if (gas.data && !gas.error) {
56
+ const gasCost = gas.data.estimatedCost;
57
+ if (pricePerToken !== undefined) {
58
+ feeText = `≈ ${usdFormatter.format(Number.parseFloat(formatUnits(gasCost, 18)) * pricePerToken)}`;
59
+ }
60
+ else {
61
+ feeText = `≈ ${formatBalance(gasCost, 18)} ${nativeSymbol}`;
62
+ }
55
63
  }
56
- const gasCost = gas.data.estimatedCost;
57
- const gasUnits = gas.data.gasLimit;
58
- if (pricePerToken !== undefined) {
59
- const gasCostInEth = Number.parseFloat(formatUnits(gasCost, 18));
60
- const gasCostInUsd = gasCostInEth * pricePerToken;
61
- return (jsxs(Fragment, { children: ["\u2248 ", usdFormatter.format(gasCostInUsd), !hideInfoIcon && (jsx(Tooltip, { message: `${gasUnits.toString()} gas units (paid in ${nativeSymbol})`, delay: 0.2, children: jsx(InfoIconWrapper, { children: jsx(InfoIcon, {}) }) }))] }));
64
+ const info = !hideInfoIcon && gasUnits ? (jsx(Tooltip, { message: `${gasUnits.toString()} gas units (paid in ${nativeSymbol})`, delay: 0.2, children: jsx(InfoIconWrapper, { children: jsx(InfoIcon, {}) }) })) : null;
65
+ // Sponsored: show what it would have cost, struck through, next to "Sponsored".
66
+ if (sponsored) {
67
+ return (jsxs(Fragment, { children: [feeText && jsx(FeeStrike, { children: feeText }), jsx(SponsoredText, { children: "Sponsored" })] }));
62
68
  }
63
- // Fallback to native token if price not available
64
- return (jsxs(Fragment, { children: ["\u2248 ", formatBalance(gasCost, 18), " ", nativeSymbol, !hideInfoIcon && (jsx(Tooltip, { message: `${gasUnits.toString()} gas units`, delay: 0.2, children: jsx(InfoIconWrapper, { children: jsx(InfoIcon, {}) }) }))] }));
69
+ if (!feeText)
70
+ return jsx(Fragment, { children: "--" });
71
+ return (jsxs(Fragment, { children: [feeText, info] }));
65
72
  };
66
73
 
67
74
  export { EstimatedFees };
@@ -1 +1 @@
1
- {"version":3,"file":"EstimatedFees.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"EstimatedFees.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,24 +1,27 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { ChainTypeEnum } from '@openfort/openfort-js';
3
3
  import { useState, useEffect } from 'react';
4
4
  import { parseUnits } from 'viem';
5
+ import { currencyLogoUrl } from '../../../constants/logos.js';
6
+ import { useAsyncData } from '../../../shared/hooks/useAsyncData.js';
5
7
  import { getExplorerUrl } from '../../../shared/utils/explorer.js';
6
8
  import { useSolanaEmbeddedWallet } from '../../../solana/hooks/useSolanaEmbeddedWallet.js';
7
- import { sendSplTokenGasless, sendSplToken, sendSolGasless, sendSol } from '../../../solana/transfer.js';
9
+ import { estimateSolanaTransferFeeLamports, sendSplTokenGasless, sendSplToken, sendSolGasless, sendSol } from '../../../solana/transfer.js';
8
10
  import 'detect-browser';
9
11
  import { truncateSolanaAddress } from '../../../utils/format.js';
10
12
  import Button from '../../Common/Button/index.js';
11
- import { CopyText } from '../../Common/CopyToClipboard/CopyText.js';
12
13
  import Loader from '../../Common/Loading/index.js';
13
14
  import { ModalHeading, ModalBody } from '../../Common/Modal/styles.js';
14
15
  import { routes } from '../../Openfort/types.js';
15
16
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
16
17
  import { PageContent } from '../../PageContent/index.js';
17
- import { ButtonRow, SummaryList, SummaryItem, SummaryLabel, AmountValue, AddressValue, FeesValue, ErrorContainer, ErrorTitle, ErrorMessage } from './styles.js';
18
+ import { formatBalance, formatBalanceWithSymbol } from '../Send/utils.js';
19
+ import { ConfirmationSummary } from './ConfirmationSummary.js';
20
+ import { ButtonRow, FeesValue, FeeStrike, SponsoredText, ErrorContainer, ErrorTitle, ErrorMessage } from './styles.js';
18
21
 
19
22
  const SOL_DECIMALS = 9;
20
23
  const SolanaSendConfirmation = () => {
21
- var _a, _b;
24
+ var _a, _b, _c;
22
25
  const { sendForm, setRoute, publishableKey, triggerResize, walletConfig } = useOpenfort();
23
26
  const wallet = useSolanaEmbeddedWallet();
24
27
  const address = wallet.status === 'connected' ? wallet.address : undefined;
@@ -32,6 +35,15 @@ const SolanaSendConfirmation = () => {
32
35
  const decimals = asset.type === 'spl' ? asset.metadata.decimals : SOL_DECIMALS;
33
36
  // Fees are sponsored from config (the SVM counterpart of ethereumFeeSponsorshipId).
34
37
  const isSponsored = Boolean((_b = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.solana) === null || _b === void 0 ? void 0 : _b.sponsorFees);
38
+ // Real network fee from the RPC (getFeeForMessage). Null while loading or on failure.
39
+ const feeQuery = useAsyncData({
40
+ queryKey: ['sol-fee', address, recipient, rpcUrl],
41
+ queryFn: () => address && recipient && rpcUrl
42
+ ? estimateSolanaTransferFeeLamports({ from: address, to: recipient, rpcUrl })
43
+ : Promise.resolve(null),
44
+ enabled: Boolean(address && recipient && rpcUrl),
45
+ });
46
+ const feeText = feeQuery.data != null ? `≈ ${formatBalance(feeQuery.data, SOL_DECIMALS)} SOL` : null;
35
47
  const [isLoading, setIsLoading] = useState(false);
36
48
  const [signature, setSignature] = useState(null);
37
49
  const [error, setError] = useState(null);
@@ -114,7 +126,7 @@ const SolanaSendConfirmation = () => {
114
126
  if (signature) {
115
127
  return (jsxs(PageContent, { children: [jsx(Loader, { isSuccess: true, header: "Transfer sent", description: `${amount} ${symbol} sent successfully` }), jsxs(ButtonRow, { children: [jsx(Button, { variant: "primary", onClick: handleViewExplorer, children: "View on Explorer" }), jsx(Button, { variant: "secondary", onClick: handleFinish, children: "Back to profile" })] })] }));
116
128
  }
117
- return (jsxs(PageContent, { onBack: routes.SOL_SEND, children: [jsx(ModalHeading, { children: "Confirm transfer" }), jsx(ModalBody, { children: "Review the transaction details before sending." }), jsxs(SummaryList, { children: [jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Sending" }), jsxs(AmountValue, { children: [amount || '0', " ", symbol] })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "From" }), jsx(AddressValue, { children: address ? (jsx(CopyText, { size: "1rem", value: address, children: truncateSolanaAddress(address) })) : ('--') })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "To" }), jsx(AddressValue, { children: recipient ? (jsx(CopyText, { size: "1rem", value: recipient, children: truncateSolanaAddress(recipient) })) : ('--') })] }), isSponsored && (jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Network fee" }), jsx(FeesValue, { "$completed": true, children: "Sponsored" })] }))] }), error && (jsxs(ErrorContainer, { children: [jsx(ErrorTitle, { children: "Transaction failed" }), jsx(ErrorMessage, { children: error })] })), jsxs(ButtonRow, { children: [jsx(Button, { variant: "primary", onClick: handleConfirm, disabled: !address || isLoading, waiting: isLoading, children: isLoading ? 'Confirming...' : 'Confirm' }), jsx(Button, { variant: "secondary", onClick: () => setRoute(routes.SOL_SEND), disabled: isLoading, children: "Cancel" })] })] }));
129
+ return (jsxs(PageContent, { onBack: routes.SOL_SEND, children: [jsx(ModalHeading, { children: "Confirm transfer" }), jsx(ModalBody, { children: "Review the transaction before sending." }), jsx(ConfirmationSummary, { amount: amount || '0', symbol: symbol, to: recipient ? { display: truncateSolanaAddress(recipient), value: recipient } : undefined, networkName: `Solana ${cluster.charAt(0).toUpperCase()}${cluster.slice(1)}`, networkIcon: currencyLogoUrl('SOL') ? jsx("img", { src: (_c = currencyLogoUrl('SOL')) !== null && _c !== void 0 ? _c : '', alt: "" }) : undefined, balance: formatBalanceWithSymbol(asset.balance, decimals, symbol), payWith: address ? { display: truncateSolanaAddress(address), value: address } : undefined, fee: jsx(FeesValue, { children: isSponsored ? (jsxs(Fragment, { children: [feeText && jsx(FeeStrike, { children: feeText }), " ", jsx(SponsoredText, { children: "Sponsored" })] })) : ((feeText !== null && feeText !== void 0 ? feeText : '--')) }) }), error && (jsxs(ErrorContainer, { children: [jsx(ErrorTitle, { children: "Transaction failed" }), jsx(ErrorMessage, { children: error })] })), jsxs(ButtonRow, { children: [jsx(Button, { variant: "primary", onClick: handleConfirm, disabled: !address || isLoading, waiting: isLoading, children: isLoading ? 'Confirming...' : 'Confirm' }), jsx(Button, { variant: "secondary", onClick: () => setRoute(routes.SOL_SEND), disabled: isLoading, children: "Cancel" })] })] }));
118
130
  };
119
131
 
120
132
  export { SolanaSendConfirmation };
@@ -1 +1 @@
1
- {"version":3,"file":"SolanaSendConfirmation.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"SolanaSendConfirmation.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -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';
@@ -13,43 +14,26 @@ import 'detect-browser';
13
14
  import { truncateEthAddress } from '../../../utils/format.js';
14
15
  import { parseTransactionError } from '../../../utils/errorHandling.js';
15
16
  import { logger } from '../../../utils/logger.js';
16
- import { getChainName, getDefaultEthereumRpcUrl } from '../../../utils/rpc.js';
17
+ import { getDefaultEthereumRpcUrl, getChainName } from '../../../utils/rpc.js';
17
18
  import Button from '../../Common/Button/index.js';
18
- import { CopyText } from '../../Common/CopyToClipboard/CopyText.js';
19
19
  import Loader from '../../Common/Loading/index.js';
20
20
  import { ModalHeading, ModalBody } from '../../Common/Modal/styles.js';
21
21
  import { routes } from '../../Openfort/types.js';
22
22
  import { useOpenfort } from '../../Openfort/useOpenfort.js';
23
23
  import { PageContent } from '../../PageContent/index.js';
24
- import { sanitizeForParsing, isSameToken, getAssetDecimals, getAssetSymbol } from '../Send/utils.js';
24
+ import { sanitizeForParsing, isSameToken, getAssetDecimals, getAssetSymbol, formatBalanceWithSymbol } from '../Send/utils.js';
25
+ import { ConfirmationSummary } from './ConfirmationSummary.js';
25
26
  import { EstimatedFees } from './EstimatedFees.js';
26
- import { ButtonRow, SummaryList, SummaryItem, SummaryLabel, AmountValue, AddressValue, FeesValue, CheckIconWrapper, StatusMessage, ErrorContainer, ErrorTitle, ErrorMessage, ErrorAction } from './styles.js';
27
+ import { ButtonRow, FeesValue, StatusMessage, ErrorContainer, ErrorTitle, ErrorMessage, ErrorAction } from './styles.js';
27
28
 
28
- /** Check if chain is a testnet */
29
- function isTestnetChain(chainId) {
30
- const testnets = new Set([5, 11155111, 80001, 84532, 421614, 97, 4002]);
31
- return testnets.has(chainId);
32
- }
33
29
  const SendConfirmation = () => {
34
- var _a, _b, _c, _d, _e, _f;
30
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
35
31
  const wallet = useEthereumEmbeddedWallet();
36
32
  const { chainType } = useOpenfortCore();
37
- const { sendForm, setRoute, triggerResize, walletConfig } = useOpenfort();
33
+ const { sendForm, setRoute, triggerResize, walletConfig, chains } = useOpenfort();
38
34
  const address = wallet.status === 'connected' ? wallet.address : undefined;
39
35
  const chainId = wallet.status === 'connected' ? wallet.chainId : undefined;
40
- // Build chain info for block explorer
41
- const chain = chainId
42
- ? {
43
- id: chainId,
44
- name: getChainName(chainId),
45
- blockExplorers: {
46
- default: {
47
- url: getExplorerUrl(ChainTypeEnum.EVM, { chainId }),
48
- },
49
- },
50
- testnet: isTestnetChain(chainId),
51
- }
52
- : undefined;
36
+ const blockExplorerUrl = chainId ? getExplorerUrl(ChainTypeEnum.EVM, { chainId }) : undefined;
53
37
  const recipientAddress = isAddress(sendForm.recipient) ? sendForm.recipient : undefined;
54
38
  const normalisedAmount = sanitizeForParsing(sendForm.amount);
55
39
  const { data: assets } = useEthereumWalletAssets();
@@ -308,7 +292,6 @@ const SendConfirmation = () => {
308
292
  };
309
293
  const status = isSuccess ? 'success' : firstError ? 'error' : 'idle';
310
294
  const errorDetails = status === 'error' ? parseTransactionError(firstError) : null;
311
- const blockExplorerUrl = (_e = (_d = chain === null || chain === void 0 ? void 0 : chain.blockExplorers) === null || _d === void 0 ? void 0 : _d.default) === null || _e === void 0 ? void 0 : _e.url;
312
295
  const handleOpenBlockExplorer = () => {
313
296
  if ((receipt === null || receipt === void 0 ? void 0 : receipt.transactionHash) && blockExplorerUrl) {
314
297
  window.open(`${blockExplorerUrl}/tx/${receipt.transactionHash}`, '_blank', 'noopener,noreferrer');
@@ -325,19 +308,26 @@ const SendConfirmation = () => {
325
308
  if (typeof feeSponsorship === 'string')
326
309
  return true;
327
310
  return feeSponsorship[chainId !== null && chainId !== void 0 ? chainId : 0] !== undefined;
328
- }, [(_f = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _f === void 0 ? void 0 : _f.ethereumFeeSponsorshipId, chainId]);
311
+ }, [(_d = walletConfig === null || walletConfig === void 0 ? void 0 : walletConfig.ethereum) === null || _d === void 0 ? void 0 : _d.ethereumFeeSponsorshipId, chainId]);
312
+ const fiatTotal = useMemo(() => {
313
+ var _a, _b;
314
+ const perToken = (_b = (_a = token.metadata) === null || _a === void 0 ? void 0 : _a.fiat) === null || _b === void 0 ? void 0 : _b.value;
315
+ const n = Number(normalisedAmount);
316
+ if (!perToken || !Number.isFinite(n) || n <= 0)
317
+ return null;
318
+ return `$${(n * perToken).toFixed(2)}`;
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;
329
325
  if (isSuccess) {
330
326
  const successAmount = normalisedAmount || '0';
331
327
  const successSymbol = getAssetSymbol(token);
332
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" })] })] }));
333
329
  }
334
- return (jsxs(PageContent, { children: [jsx(ModalHeading, { children: "Confirm transfer" }), jsx(ModalBody, { children: "Review the transaction details before sending." }), jsxs(SummaryList, { children: [jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Sending" }), jsxs(AmountValue, { children: [normalisedAmount || '0', " ", getAssetSymbol(token)] })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "From" }), jsx(AddressValue, { children: address ? (jsx(CopyText, { size: "1rem", value: address, children: truncateEthAddress(address) })) : ('--') })] }), jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "To" }), jsx(AddressValue, { children: recipientAddress ? (jsx(CopyText, { size: "1rem", value: recipientAddress, children: truncateEthAddress(recipientAddress) })) : ('--') })] }), jsxs("div", { children: [jsxs(SummaryItem, { children: [jsx(SummaryLabel, { children: "Estimated fees" }), jsxs(FeesValue, { "$completed": isSponsored, children: [jsx(EstimatedFees, { account: address, to: token.type === 'erc20' ? token.address : recipientAddress, value: token.type === 'native' && parsedAmount ? parsedAmount : undefined, data: transferData, chainId: chainId, nativeSymbol: nativeSymbol, enabled: Boolean(address && recipientAddress && parsedAmount && parsedAmount > BigInt(0)), hideInfoIcon: isSponsored }), jsx(CheckIconWrapper, { children: jsx(TickIcon, {}) })] })] }), isSponsored && (jsx("div", { style: {
335
- textAlign: 'end',
336
- marginTop: '4px',
337
- width: '100%',
338
- color: 'var(--ck-body-color-valid)',
339
- fontSize: '12px',
340
- }, children: "Sponsored transaction" }))] })] }), 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
341
331
  ? false
342
332
  : isLoading ||
343
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -6,7 +6,15 @@ export declare const AddressValue: import("styled-components").StyledComponent<"
6
6
  export declare const FeesValue: import("styled-components").StyledComponent<"span", any, {
7
7
  $completed?: boolean;
8
8
  }, never>;
9
- export declare const CheckIconWrapper: import("styled-components").StyledComponent<"span", any, {}, never>;
9
+ export declare const FiatValue: import("styled-components").StyledComponent<"span", any, {}, never>;
10
+ export declare const NetworkValue: 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>;
14
+ export declare const PayWithCard: import("styled-components").StyledComponent<"div", any, {}, never>;
15
+ export declare const PayWithMeta: import("styled-components").StyledComponent<"div", any, {}, never>;
16
+ export declare const PayWithAddress: import("styled-components").StyledComponent<"span", any, {}, never>;
17
+ export declare const PayWithBadge: import("styled-components").StyledComponent<"span", any, {}, never>;
10
18
  export declare const InfoIconWrapper: import("styled-components").StyledComponent<"span", any, {}, never>;
11
19
  export declare const ButtonRow: import("styled-components").StyledComponent<"div", any, {}, never>;
12
20
  export declare const StatusMessage: import("styled-components").StyledComponent<"div", any, {