@nockchain/rose 0.1.4-nightly.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/artifacts.yml +33 -0
- package/.github/workflows/ci.yml +68 -0
- package/.github/workflows/publish-sdk.yml +35 -0
- package/.nvmrc +1 -0
- package/.prettierignore +5 -0
- package/.prettierrc +8 -0
- package/LICENSE +22 -0
- package/README.md +117 -0
- package/extension/background/index.ts +1500 -0
- package/extension/content/index.ts +59 -0
- package/extension/icons/rose.svg +27 -0
- package/extension/icons/rose128.png +0 -0
- package/extension/icons/rose16.png +0 -0
- package/extension/icons/rose256.png +0 -0
- package/extension/icons/rose32.png +0 -0
- package/extension/icons/rose48.png +0 -0
- package/extension/icons/rose512.png +0 -0
- package/extension/inpage/index.ts +86 -0
- package/extension/manifest.json +48 -0
- package/extension/popup/Popup.tsx +94 -0
- package/extension/popup/Router.tsx +121 -0
- package/extension/popup/assets/arrow-down-icon.svg +3 -0
- package/extension/popup/assets/arrow-left-icon.svg +3 -0
- package/extension/popup/assets/arrow-right-icon.svg +3 -0
- package/extension/popup/assets/arrow-up-icon.svg +3 -0
- package/extension/popup/assets/arrow-up-right-icon.svg +3 -0
- package/extension/popup/assets/checkmark-icon.svg +3 -0
- package/extension/popup/assets/checkmark-pencil-icon.svg +3 -0
- package/extension/popup/assets/checkmark-success-icon.svg +3 -0
- package/extension/popup/assets/clock-icon.svg +3 -0
- package/extension/popup/assets/close-x-icon.svg +3 -0
- package/extension/popup/assets/copy-icon.svg +6 -0
- package/extension/popup/assets/explorer-icon.svg +3 -0
- package/extension/popup/assets/eye-off-icon.svg +3 -0
- package/extension/popup/assets/eye-open-icon.svg +4 -0
- package/extension/popup/assets/feedback-icon.svg +3 -0
- package/extension/popup/assets/green-status-dot.svg +3 -0
- package/extension/popup/assets/info-icon.svg +3 -0
- package/extension/popup/assets/iris-logo-40.svg +27 -0
- package/extension/popup/assets/iris-logo-96.svg +27 -0
- package/extension/popup/assets/iris-logo-blue.svg +27 -0
- package/extension/popup/assets/iris-logo-no-eye.svg +27 -0
- package/extension/popup/assets/iris-logo-orange.svg +27 -0
- package/extension/popup/assets/iris-logo.svg +27 -0
- package/extension/popup/assets/key-icon.svg +3 -0
- package/extension/popup/assets/lock-icon-yellow.svg +3 -0
- package/extension/popup/assets/lock-icon.svg +3 -0
- package/extension/popup/assets/pencil-edit-icon.svg +3 -0
- package/extension/popup/assets/permissions-icon.svg +3 -0
- package/extension/popup/assets/receipt-icon.svg +5 -0
- package/extension/popup/assets/refresh-icon.svg +3 -0
- package/extension/popup/assets/settings-gear-icon.svg +8 -0
- package/extension/popup/assets/settings-icon.svg +3 -0
- package/extension/popup/assets/theme-icon.svg +3 -0
- package/extension/popup/assets/trash-bin-icon.svg +3 -0
- package/extension/popup/assets/trend-down-arrow.svg +5 -0
- package/extension/popup/assets/trend-up-arrow.svg +5 -0
- package/extension/popup/assets/user-account-icon.svg +3 -0
- package/extension/popup/assets/vector-bottom-left.svg +9 -0
- package/extension/popup/assets/vector-left.svg +9 -0
- package/extension/popup/assets/vector-right.svg +9 -0
- package/extension/popup/assets/vector-top-right-rotated.svg +8 -0
- package/extension/popup/assets/vector-top-right.svg +9 -0
- package/extension/popup/assets/wallet-dropdown-arrow.svg +5 -0
- package/extension/popup/assets/wallet-icon-style-1.svg +6 -0
- package/extension/popup/assets/wallet-icon-style-10.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-11.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-12.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-13.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-14.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-15.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-2.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-3.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-4.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-5.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-6.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-7.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-8.svg +8 -0
- package/extension/popup/assets/wallet-icon-style-9.svg +8 -0
- package/extension/popup/components/AccountIcon.tsx +78 -0
- package/extension/popup/components/AccountSelector.tsx +246 -0
- package/extension/popup/components/Alert.tsx +48 -0
- package/extension/popup/components/ConfirmModal.tsx +81 -0
- package/extension/popup/components/PasswordInput.tsx +49 -0
- package/extension/popup/components/ScreenContainer.tsx +17 -0
- package/extension/popup/components/SiteIcon.tsx +60 -0
- package/extension/popup/components/ThemeToggle.tsx +44 -0
- package/extension/popup/components/icons/ArrowDownLeftIcon.tsx +20 -0
- package/extension/popup/components/icons/ArrowUpRightIcon.tsx +20 -0
- package/extension/popup/components/icons/CheckIcon.tsx +20 -0
- package/extension/popup/components/icons/ChevronDownIcon.tsx +15 -0
- package/extension/popup/components/icons/ChevronLeftIcon.tsx +15 -0
- package/extension/popup/components/icons/ChevronRightIcon.tsx +15 -0
- package/extension/popup/components/icons/ChevronUpIcon.tsx +15 -0
- package/extension/popup/components/icons/CloseIcon.tsx +26 -0
- package/extension/popup/components/icons/CopyIcon.tsx +20 -0
- package/extension/popup/components/icons/EditIcon.tsx +20 -0
- package/extension/popup/components/icons/EyeIcon.tsx +13 -0
- package/extension/popup/components/icons/EyeOffIcon.tsx +13 -0
- package/extension/popup/components/icons/InfoIcon.tsx +20 -0
- package/extension/popup/components/icons/LockIcon.tsx +20 -0
- package/extension/popup/components/icons/PlusIcon.tsx +15 -0
- package/extension/popup/components/icons/ReceiveArrowIcon.tsx +14 -0
- package/extension/popup/components/icons/ReceiveCircleIcon.tsx +20 -0
- package/extension/popup/components/icons/SendPaperPlaneIcon.tsx +18 -0
- package/extension/popup/components/icons/SentArrowIcon.tsx +21 -0
- package/extension/popup/components/icons/SettingsIcon.tsx +26 -0
- package/extension/popup/components/icons/ShieldIcon.tsx +20 -0
- package/extension/popup/components/icons/UploadIcon.tsx +20 -0
- package/extension/popup/components/icons/WalletIcon.tsx +20 -0
- package/extension/popup/contexts/ThemeContext.tsx +105 -0
- package/extension/popup/hooks/useApprovalDetection.ts +128 -0
- package/extension/popup/hooks/useAutoFocus.ts +36 -0
- package/extension/popup/hooks/useAutoRejectOnClose.ts +25 -0
- package/extension/popup/hooks/useClickOutside.ts +33 -0
- package/extension/popup/hooks/useCopyToClipboard.ts +33 -0
- package/extension/popup/hooks/useFavicon.ts +64 -0
- package/extension/popup/hooks/useNumericInput.ts +93 -0
- package/extension/popup/index.html +13 -0
- package/extension/popup/index.tsx +24 -0
- package/extension/popup/screens/AboutScreen.tsx +118 -0
- package/extension/popup/screens/HomeScreen.tailwind.css +85 -0
- package/extension/popup/screens/HomeScreen.tsx +902 -0
- package/extension/popup/screens/KeySettingsPasswordScreen.tsx +164 -0
- package/extension/popup/screens/LockTimeScreen.tsx +155 -0
- package/extension/popup/screens/ReceiveScreen.tsx +149 -0
- package/extension/popup/screens/RecoveryPhraseScreen.tsx +183 -0
- package/extension/popup/screens/SendReviewScreen.tsx +308 -0
- package/extension/popup/screens/SendScreen.tsx +825 -0
- package/extension/popup/screens/SendSubmittedScreen.tsx +193 -0
- package/extension/popup/screens/SettingsScreen.tsx +116 -0
- package/extension/popup/screens/ThemeSettingsScreen.tsx +107 -0
- package/extension/popup/screens/TransactionDetailsScreen.tsx +346 -0
- package/extension/popup/screens/ViewSecretPhraseScreen.tsx +212 -0
- package/extension/popup/screens/WalletPermissionsScreen.tsx +123 -0
- package/extension/popup/screens/WalletSettingsScreen.tsx +381 -0
- package/extension/popup/screens/WalletStylingScreen.tsx +306 -0
- package/extension/popup/screens/approvals/ConnectApprovalScreen.tsx +136 -0
- package/extension/popup/screens/approvals/SignMessageScreen.tsx +140 -0
- package/extension/popup/screens/approvals/SignRawTxScreen.tsx +320 -0
- package/extension/popup/screens/approvals/TransactionApprovalScreen.tsx +167 -0
- package/extension/popup/screens/onboarding/BackupScreen.tsx +254 -0
- package/extension/popup/screens/onboarding/CreateScreen.tsx +273 -0
- package/extension/popup/screens/onboarding/ImportScreen.tsx +676 -0
- package/extension/popup/screens/onboarding/ImportScreenV0.tsx +678 -0
- package/extension/popup/screens/onboarding/ImportSuccessScreen.tsx +236 -0
- package/extension/popup/screens/onboarding/ResumeBackupScreen.tsx +166 -0
- package/extension/popup/screens/onboarding/StartScreen.tsx +142 -0
- package/extension/popup/screens/onboarding/SuccessScreen.tsx +193 -0
- package/extension/popup/screens/onboarding/VerifyScreen.tsx +220 -0
- package/extension/popup/screens/system/LockedScreen.tsx +288 -0
- package/extension/popup/screens/transactions/ReceiveScreen.tsx +84 -0
- package/extension/popup/screens/transactions/SentScreen.tsx +138 -0
- package/extension/popup/store.ts +482 -0
- package/extension/popup/styles.css +246 -0
- package/extension/popup/utils/format.ts +58 -0
- package/extension/popup/utils/formatWalletError.ts +36 -0
- package/extension/popup/utils/memo.ts +299 -0
- package/extension/popup/utils/messaging.ts +16 -0
- package/extension/shared/address-encoding.ts +69 -0
- package/extension/shared/balance-query.ts +123 -0
- package/extension/shared/constants.ts +386 -0
- package/extension/shared/currency.ts +128 -0
- package/extension/shared/first-name-derivation.ts +128 -0
- package/extension/shared/keyfile.ts +58 -0
- package/extension/shared/onboarding.ts +78 -0
- package/extension/shared/price-api.ts +79 -0
- package/extension/shared/rpc-client-browser.ts +315 -0
- package/extension/shared/transaction-builder.ts +443 -0
- package/extension/shared/types.ts +450 -0
- package/extension/shared/utxo-diff.ts +212 -0
- package/extension/shared/utxo-store.ts +548 -0
- package/extension/shared/utxo-sync.ts +343 -0
- package/extension/shared/validators.ts +26 -0
- package/extension/shared/vault.ts +1580 -0
- package/extension/shared/wallet-crypto.ts +77 -0
- package/extension/shared/wasm-utils.ts +76 -0
- package/extension/shared/webcrypto.ts +67 -0
- package/extension/types/wasm.d.ts +13 -0
- package/package.json +39 -0
- package/postcss.config.js +6 -0
- package/rose-extension-dist.zip +0 -0
- package/sdk/README.md +88 -0
- package/sdk/examples/app.ts +166 -0
- package/sdk/examples/index.html +51 -0
- package/sdk/examples/tsconfig.json +15 -0
- package/sdk/examples/tx-builder.html +532 -0
- package/sdk/examples/tx-builder.ts +1766 -0
- package/sdk/package-lock.json +424 -0
- package/sdk/package.json +68 -0
- package/sdk/src/constants.ts +28 -0
- package/sdk/src/errors.ts +74 -0
- package/sdk/src/hooks/index.ts +1 -0
- package/sdk/src/hooks/use-rose.ts +94 -0
- package/sdk/src/index.ts +12 -0
- package/sdk/src/provider.ts +396 -0
- package/sdk/src/transaction.ts +163 -0
- package/sdk/src/types/rose-wasm.d.ts +14 -0
- package/sdk/src/types.ts +97 -0
- package/sdk/src/wasm.ts +13 -0
- package/sdk/tsconfig.json +20 -0
- package/sdk/vite.config.examples.ts +32 -0
- package/tailwind.config.ts +38 -0
- package/tsconfig.json +20 -0
- package/vite.config.ts +60 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { useStore } from '../../store';
|
|
2
|
+
import { ChevronRightIcon } from '../../components/icons/ChevronRightIcon';
|
|
3
|
+
import { AccountIcon } from '../../components/AccountIcon';
|
|
4
|
+
import { SiteIcon } from '../../components/SiteIcon';
|
|
5
|
+
import { truncateAddress } from '../../utils/format';
|
|
6
|
+
import { send } from '../../utils/messaging';
|
|
7
|
+
import {
|
|
8
|
+
INTERNAL_METHODS,
|
|
9
|
+
NOCK_TO_NICKS,
|
|
10
|
+
DEFAULT_TRANSACTION_FEE,
|
|
11
|
+
} from '../../../shared/constants';
|
|
12
|
+
import { formatNock, formatNick } from '../../../shared/currency';
|
|
13
|
+
import { useAutoRejectOnClose } from '../../hooks/useAutoRejectOnClose';
|
|
14
|
+
|
|
15
|
+
export function TransactionApprovalScreen() {
|
|
16
|
+
const { navigate, pendingTransactionRequest, setPendingTransactionRequest, wallet } = useStore();
|
|
17
|
+
|
|
18
|
+
if (!pendingTransactionRequest) {
|
|
19
|
+
navigate('home');
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { id, origin, to, amount } = pendingTransactionRequest;
|
|
24
|
+
const fee = DEFAULT_TRANSACTION_FEE;
|
|
25
|
+
const total = amount + fee;
|
|
26
|
+
const displayOrigin = origin.includes('://') ? new URL(origin).hostname : origin;
|
|
27
|
+
|
|
28
|
+
useAutoRejectOnClose(id, INTERNAL_METHODS.REJECT_TRANSACTION);
|
|
29
|
+
|
|
30
|
+
async function handleReject() {
|
|
31
|
+
await send(INTERNAL_METHODS.REJECT_TRANSACTION, [id]);
|
|
32
|
+
setPendingTransactionRequest(null);
|
|
33
|
+
window.close();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function handleApprove() {
|
|
37
|
+
await send(INTERNAL_METHODS.APPROVE_TRANSACTION, [id]);
|
|
38
|
+
setPendingTransactionRequest(null);
|
|
39
|
+
window.close();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const bg = 'var(--color-bg)';
|
|
43
|
+
const surface = 'var(--color-surface-800)';
|
|
44
|
+
const textPrimary = 'var(--color-text-primary)';
|
|
45
|
+
const textMuted = 'var(--color-text-muted)';
|
|
46
|
+
const divider = 'var(--color-divider)';
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div className="h-screen flex items-center justify-center" style={{ backgroundColor: bg }}>
|
|
50
|
+
<div
|
|
51
|
+
className="w-full h-full flex flex-col"
|
|
52
|
+
style={{ backgroundColor: bg, maxWidth: '357px', maxHeight: '600px' }}
|
|
53
|
+
>
|
|
54
|
+
{/* Header */}
|
|
55
|
+
<div className="flex items-center justify-center px-4 py-4 shrink-0">
|
|
56
|
+
<h2 className="text-xl font-semibold" style={{ color: textPrimary }}>
|
|
57
|
+
Approve Transaction
|
|
58
|
+
</h2>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
{/* Content */}
|
|
62
|
+
<div className="flex-1 min-h-0 overflow-y-auto">
|
|
63
|
+
<div className="px-4 pb-2">
|
|
64
|
+
{/* Site Badge */}
|
|
65
|
+
<div
|
|
66
|
+
className="flex items-center justify-center gap-2 px-3 py-1.5 rounded-lg mb-3"
|
|
67
|
+
style={{ backgroundColor: surface }}
|
|
68
|
+
>
|
|
69
|
+
<span className="text-xs" style={{ color: textMuted }}>
|
|
70
|
+
From
|
|
71
|
+
</span>
|
|
72
|
+
<SiteIcon origin={origin} domain={displayOrigin} size="sm" />
|
|
73
|
+
<span
|
|
74
|
+
className="text-sm font-semibold truncate max-w-[160px]"
|
|
75
|
+
style={{ color: textPrimary }}
|
|
76
|
+
>
|
|
77
|
+
{displayOrigin}
|
|
78
|
+
</span>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
{/* Amount */}
|
|
82
|
+
<div className="text-center mb-4">
|
|
83
|
+
<div className="font-[Lora] text-[32px] font-semibold leading-none">
|
|
84
|
+
{formatNock(amount / NOCK_TO_NICKS)} <span style={{ color: textMuted }}>NOCK</span>
|
|
85
|
+
</div>
|
|
86
|
+
<div className="text-[10px] mt-1" style={{ color: textMuted }}>
|
|
87
|
+
{formatNick(amount)} nicks
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div className="space-y-2">
|
|
92
|
+
{/* From/To */}
|
|
93
|
+
<div
|
|
94
|
+
className="rounded-lg p-3 flex items-center gap-2"
|
|
95
|
+
style={{ backgroundColor: surface }}
|
|
96
|
+
>
|
|
97
|
+
<div className="flex-1">
|
|
98
|
+
<div className="text-xs mb-1" style={{ color: textMuted }}>
|
|
99
|
+
From
|
|
100
|
+
</div>
|
|
101
|
+
<div className="flex items-center gap-1.5">
|
|
102
|
+
<AccountIcon
|
|
103
|
+
styleId={wallet.currentAccount?.iconStyleId}
|
|
104
|
+
color={wallet.currentAccount?.iconColor}
|
|
105
|
+
className="w-4 h-4"
|
|
106
|
+
/>
|
|
107
|
+
<span className="text-sm">
|
|
108
|
+
{truncateAddress(wallet.currentAccount?.address)}
|
|
109
|
+
</span>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
<ChevronRightIcon className="w-4 h-4 shrink-0" />
|
|
113
|
+
<div className="flex-1">
|
|
114
|
+
<div className="text-xs mb-1" style={{ color: textMuted }}>
|
|
115
|
+
To
|
|
116
|
+
</div>
|
|
117
|
+
<span className="text-sm">{truncateAddress(to)}</span>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{/* Fee & Total */}
|
|
122
|
+
<div className="rounded-lg p-3 space-y-2" style={{ backgroundColor: surface }}>
|
|
123
|
+
<div className="flex justify-between text-sm">
|
|
124
|
+
<span>Network fee</span>
|
|
125
|
+
<div className="text-right">
|
|
126
|
+
<div>{formatNock(fee / NOCK_TO_NICKS)} NOCK</div>
|
|
127
|
+
<div className="text-[10px]" style={{ color: textMuted }}>
|
|
128
|
+
{formatNick(fee)} nicks
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div className="h-px" style={{ backgroundColor: 'var(--color-surface-700)' }} />
|
|
133
|
+
<div className="flex justify-between text-sm font-semibold">
|
|
134
|
+
<span>Total</span>
|
|
135
|
+
<div className="text-right">
|
|
136
|
+
<div>{formatNock(total / NOCK_TO_NICKS)} NOCK</div>
|
|
137
|
+
<div className="text-[10px] font-normal" style={{ color: textMuted }}>
|
|
138
|
+
{formatNick(total)} nicks
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
{/* Balance After */}
|
|
145
|
+
<div className="text-center text-xs py-2" style={{ color: textMuted }}>
|
|
146
|
+
Balance after: {formatNock(wallet.balance - total / NOCK_TO_NICKS)} NOCK
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
|
|
152
|
+
{/* Footer Buttons */}
|
|
153
|
+
<div
|
|
154
|
+
className="px-4 py-2.5 shrink-0 flex gap-3"
|
|
155
|
+
style={{ borderTop: `1px solid ${divider}` }}
|
|
156
|
+
>
|
|
157
|
+
<button onClick={handleReject} className="btn-secondary flex-1">
|
|
158
|
+
Reject
|
|
159
|
+
</button>
|
|
160
|
+
<button onClick={handleApprove} className="btn-primary flex-1">
|
|
161
|
+
Approve
|
|
162
|
+
</button>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding Backup Screen - Display mnemonic for user to write down
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import { useStore } from '../../store';
|
|
7
|
+
import { Alert } from '../../components/Alert';
|
|
8
|
+
import { CheckIcon } from '../../components/icons/CheckIcon';
|
|
9
|
+
import lockIcon from '../../assets/lock-icon.svg';
|
|
10
|
+
|
|
11
|
+
export function BackupScreen() {
|
|
12
|
+
const { onboardingMnemonic, navigate } = useStore();
|
|
13
|
+
const [isRevealed, setIsRevealed] = useState(false);
|
|
14
|
+
const [hasConfirmed, setHasConfirmed] = useState(false);
|
|
15
|
+
const [copiedAll, setCopiedAll] = useState(false);
|
|
16
|
+
|
|
17
|
+
async function handleCopyAll() {
|
|
18
|
+
if (onboardingMnemonic) {
|
|
19
|
+
try {
|
|
20
|
+
await navigator.clipboard.writeText(onboardingMnemonic);
|
|
21
|
+
setCopiedAll(true);
|
|
22
|
+
setTimeout(() => setCopiedAll(false), 2000);
|
|
23
|
+
} catch (err) {
|
|
24
|
+
console.error('Failed to copy secret phrase:', err);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!onboardingMnemonic) {
|
|
30
|
+
// Should never happen, but handle gracefully
|
|
31
|
+
return (
|
|
32
|
+
<div className="w-[357px] h-[600px] bg-[var(--color-bg)] flex items-center justify-center p-4">
|
|
33
|
+
<Alert type="error">No mnemonic found. Please restart onboarding.</Alert>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const words = onboardingMnemonic.split(' ');
|
|
39
|
+
|
|
40
|
+
function handleContinue() {
|
|
41
|
+
if (hasConfirmed) {
|
|
42
|
+
navigate('onboarding-verify');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className="w-[357px] h-[600px] flex flex-col bg-[var(--color-bg)]">
|
|
48
|
+
{/* Header with back button */}
|
|
49
|
+
<div className="flex items-center justify-between h-16 px-4 py-3 border-b border-[var(--color-divider)] shrink-0">
|
|
50
|
+
<button
|
|
51
|
+
onClick={() => navigate('onboarding-create')}
|
|
52
|
+
className="p-2 -ml-2 hover:opacity-70 transition-opacity"
|
|
53
|
+
aria-label="Go back"
|
|
54
|
+
>
|
|
55
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
56
|
+
<path
|
|
57
|
+
d="M10 12L6 8L10 4"
|
|
58
|
+
stroke="var(--color-text-primary)"
|
|
59
|
+
strokeWidth="2"
|
|
60
|
+
strokeLinecap="round"
|
|
61
|
+
strokeLinejoin="round"
|
|
62
|
+
/>
|
|
63
|
+
</svg>
|
|
64
|
+
</button>
|
|
65
|
+
<h2
|
|
66
|
+
className="font-sans font-medium text-[var(--color-text-primary)]"
|
|
67
|
+
style={{
|
|
68
|
+
fontSize: 'var(--font-size-lg)',
|
|
69
|
+
lineHeight: 'var(--line-height-normal)',
|
|
70
|
+
letterSpacing: '0.01em',
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
Save your secret phrase
|
|
74
|
+
</h2>
|
|
75
|
+
<div className="w-8" /> {/* Spacer for centering */}
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
{/* Main content - scrollable */}
|
|
79
|
+
<div className="flex-1 overflow-y-auto no-scrollbar">
|
|
80
|
+
<div className="px-4 py-2 pb-4 flex flex-col gap-6">
|
|
81
|
+
{/* Icon and heading */}
|
|
82
|
+
<div className="flex flex-col items-center gap-3">
|
|
83
|
+
<div className="w-10 h-10">
|
|
84
|
+
<img src={lockIcon} alt="" className="w-full h-full" />
|
|
85
|
+
</div>
|
|
86
|
+
<h1
|
|
87
|
+
className="font-serif font-medium text-center text-[var(--color-text-primary)]"
|
|
88
|
+
style={{
|
|
89
|
+
fontSize: 'var(--font-size-xl)',
|
|
90
|
+
lineHeight: 'var(--line-height-relaxed)',
|
|
91
|
+
letterSpacing: '-0.02em',
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
Write these words
|
|
95
|
+
<br />
|
|
96
|
+
down in order.
|
|
97
|
+
</h1>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
{/* Secret phrase section */}
|
|
101
|
+
<div className="flex flex-col gap-3">
|
|
102
|
+
{/* Warning box */}
|
|
103
|
+
<div className="bg-[var(--color-surface-900)] rounded-lg p-3">
|
|
104
|
+
<p
|
|
105
|
+
className="font-sans font-medium text-center text-[var(--color-text-muted)]"
|
|
106
|
+
style={{
|
|
107
|
+
fontSize: 'var(--font-size-xs)',
|
|
108
|
+
lineHeight: 'var(--line-height-tight)',
|
|
109
|
+
letterSpacing: '0.02em',
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
This is your ONLY way to recover your wallet.
|
|
113
|
+
</p>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
{/* Secret phrase grid */}
|
|
117
|
+
<div className="relative">
|
|
118
|
+
<div className="grid grid-cols-2 gap-2">
|
|
119
|
+
{words.map((word, index) => (
|
|
120
|
+
<div
|
|
121
|
+
key={index}
|
|
122
|
+
className="bg-[var(--color-bg)] border border-[var(--color-surface-900)] rounded-lg p-2 flex items-center gap-2.5 h-11"
|
|
123
|
+
>
|
|
124
|
+
<span
|
|
125
|
+
className="bg-[var(--color-surface-900)] rounded min-w-[28px] h-7 flex items-center justify-center font-sans font-medium text-[var(--color-text-primary)]"
|
|
126
|
+
style={{
|
|
127
|
+
fontSize: 'var(--font-size-base)',
|
|
128
|
+
lineHeight: 'var(--line-height-snug)',
|
|
129
|
+
letterSpacing: '0.01em',
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
{index + 1}
|
|
133
|
+
</span>
|
|
134
|
+
<span
|
|
135
|
+
className="font-sans font-medium text-[var(--color-text-primary)]"
|
|
136
|
+
style={{
|
|
137
|
+
fontSize: 'var(--font-size-base)',
|
|
138
|
+
lineHeight: 'var(--line-height-snug)',
|
|
139
|
+
letterSpacing: '0.01em',
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
{word}
|
|
143
|
+
</span>
|
|
144
|
+
</div>
|
|
145
|
+
))}
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
{/* Blur overlay when not revealed */}
|
|
149
|
+
{!isRevealed && (
|
|
150
|
+
<div
|
|
151
|
+
className="absolute inset-0 backdrop-blur-[6px] rounded-lg"
|
|
152
|
+
style={{
|
|
153
|
+
backgroundColor: 'var(--color-popover)',
|
|
154
|
+
border: '1px solid var(--color-surface-900)',
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
)}
|
|
158
|
+
</div>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
{/* Checkbox confirmation */}
|
|
162
|
+
<div className="bg-[var(--color-surface-900)] rounded-lg p-3 flex items-center gap-2.5">
|
|
163
|
+
<button
|
|
164
|
+
onClick={() => setHasConfirmed(!hasConfirmed)}
|
|
165
|
+
className={`w-7 h-7 rounded-md flex items-center justify-center transition-colors ${
|
|
166
|
+
hasConfirmed
|
|
167
|
+
? 'btn-primary'
|
|
168
|
+
: 'bg-transparent border-2 border-[var(--color-surface-700)]'
|
|
169
|
+
}`}
|
|
170
|
+
aria-label="Confirm you've written down the secret phrase"
|
|
171
|
+
>
|
|
172
|
+
{hasConfirmed && (
|
|
173
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
|
|
174
|
+
<path
|
|
175
|
+
d="M16 6L7.5 14.5L4 11"
|
|
176
|
+
stroke="#000000"
|
|
177
|
+
strokeWidth="2"
|
|
178
|
+
strokeLinecap="round"
|
|
179
|
+
strokeLinejoin="round"
|
|
180
|
+
/>
|
|
181
|
+
</svg>
|
|
182
|
+
)}
|
|
183
|
+
</button>
|
|
184
|
+
<p
|
|
185
|
+
className="flex-1 font-sans font-medium text-[var(--color-text-muted)]"
|
|
186
|
+
style={{
|
|
187
|
+
fontSize: 'var(--font-size-xs)',
|
|
188
|
+
lineHeight: 'var(--line-height-tight)',
|
|
189
|
+
letterSpacing: '0.02em',
|
|
190
|
+
}}
|
|
191
|
+
>
|
|
192
|
+
I've securely written down all 24 words of my secret phrase
|
|
193
|
+
</p>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
{/* Bottom buttons - Pinned to bottom */}
|
|
199
|
+
<div className="border-t border-[var(--color-surface-800)] px-4 py-3 bg-[var(--color-bg)] shrink-0">
|
|
200
|
+
{!isRevealed ? (
|
|
201
|
+
<button
|
|
202
|
+
onClick={() => setIsRevealed(true)}
|
|
203
|
+
className="w-full h-12 px-5 py-[15px] btn-secondary text-[var(--color-bg)] rounded-lg flex items-center justify-center transition-opacity hover:opacity-90"
|
|
204
|
+
style={{
|
|
205
|
+
fontFamily: 'var(--font-sans)',
|
|
206
|
+
fontSize: 'var(--font-size-base)',
|
|
207
|
+
fontWeight: 500,
|
|
208
|
+
lineHeight: 'var(--line-height-snug)',
|
|
209
|
+
letterSpacing: '0.01em',
|
|
210
|
+
}}
|
|
211
|
+
>
|
|
212
|
+
Show secret phrase
|
|
213
|
+
</button>
|
|
214
|
+
) : (
|
|
215
|
+
<div className="flex gap-3">
|
|
216
|
+
<button
|
|
217
|
+
onClick={handleCopyAll}
|
|
218
|
+
disabled={copiedAll}
|
|
219
|
+
className="flex-1 h-12 px-5 py-[15px] rounded-lg flex items-center justify-center gap-2 transition-opacity bg-[var(--color-surface-900)] text-[var(--color-text-primary)] hover:opacity-90 disabled:opacity-100"
|
|
220
|
+
style={{
|
|
221
|
+
fontFamily: 'var(--font-sans)',
|
|
222
|
+
fontSize: 'var(--font-size-base)',
|
|
223
|
+
fontWeight: 500,
|
|
224
|
+
lineHeight: 'var(--line-height-snug)',
|
|
225
|
+
letterSpacing: '0.01em',
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
228
|
+
{copiedAll && <CheckIcon className="w-5 h-5" />}
|
|
229
|
+
{copiedAll ? 'Copied!' : 'Copy all'}
|
|
230
|
+
</button>
|
|
231
|
+
<button
|
|
232
|
+
onClick={handleContinue}
|
|
233
|
+
disabled={!hasConfirmed}
|
|
234
|
+
className={`flex-1 h-12 px-5 py-[15px] rounded-lg flex items-center justify-center transition-opacity ${
|
|
235
|
+
hasConfirmed
|
|
236
|
+
? 'btn-secondary text-[var(--color-bg)] hover:opacity-90'
|
|
237
|
+
: 'bg-[var(--color-surface-700)] text-[var(--color-text-muted)] cursor-not-allowed opacity-50'
|
|
238
|
+
}`}
|
|
239
|
+
style={{
|
|
240
|
+
fontFamily: 'var(--font-sans)',
|
|
241
|
+
fontSize: 'var(--font-size-base)',
|
|
242
|
+
fontWeight: 500,
|
|
243
|
+
lineHeight: 'var(--line-height-snug)',
|
|
244
|
+
letterSpacing: '0.01em',
|
|
245
|
+
}}
|
|
246
|
+
>
|
|
247
|
+
Continue
|
|
248
|
+
</button>
|
|
249
|
+
</div>
|
|
250
|
+
)}
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onboarding Create Screen - Set password and create wallet
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useState } from 'react';
|
|
6
|
+
import { INTERNAL_METHODS, UI_CONSTANTS } from '../../../shared/constants';
|
|
7
|
+
import { setOnboardingInProgress } from '../../../shared/onboarding';
|
|
8
|
+
import { useStore } from '../../store';
|
|
9
|
+
import { send } from '../../utils/messaging';
|
|
10
|
+
import { formatWalletError } from '../../utils/formatWalletError';
|
|
11
|
+
import { Alert } from '../../components/Alert';
|
|
12
|
+
import lockIcon from '../../assets/lock-icon.svg';
|
|
13
|
+
import { EyeIcon } from '../../components/icons/EyeIcon';
|
|
14
|
+
import { EyeOffIcon } from '../../components/icons/EyeOffIcon';
|
|
15
|
+
|
|
16
|
+
export function CreateScreen() {
|
|
17
|
+
const [password, setPassword] = useState('');
|
|
18
|
+
const [confirmPassword, setConfirmPassword] = useState('');
|
|
19
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
20
|
+
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
|
21
|
+
const [error, setError] = useState('');
|
|
22
|
+
const { navigate, syncWallet, setOnboardingMnemonic } = useStore();
|
|
23
|
+
|
|
24
|
+
async function handleCreate() {
|
|
25
|
+
// Clear previous errors
|
|
26
|
+
setError('');
|
|
27
|
+
|
|
28
|
+
// Validate password
|
|
29
|
+
if (!password) {
|
|
30
|
+
setError('Please enter a password');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (password.length < UI_CONSTANTS.MIN_PASSWORD_LENGTH) {
|
|
35
|
+
setError(`Password must be at least ${UI_CONSTANTS.MIN_PASSWORD_LENGTH} characters`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (password !== confirmPassword) {
|
|
40
|
+
setError('Passwords do not match');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Create wallet
|
|
45
|
+
const result = await send<{
|
|
46
|
+
ok?: boolean;
|
|
47
|
+
address?: string;
|
|
48
|
+
mnemonic?: string;
|
|
49
|
+
error?: string;
|
|
50
|
+
}>(INTERNAL_METHODS.SETUP, [password]);
|
|
51
|
+
|
|
52
|
+
if (result?.error) {
|
|
53
|
+
setError(formatWalletError(result.error));
|
|
54
|
+
} else {
|
|
55
|
+
// Mark onboarding as in-progress (backup not yet complete)
|
|
56
|
+
await setOnboardingInProgress();
|
|
57
|
+
|
|
58
|
+
// Store mnemonic temporarily for backup/verification flow
|
|
59
|
+
setOnboardingMnemonic(result.mnemonic || '');
|
|
60
|
+
// After setup, we have the first account (Wallet 1)
|
|
61
|
+
const firstAccount = {
|
|
62
|
+
name: 'Wallet 1',
|
|
63
|
+
address: result.address || '',
|
|
64
|
+
index: 0,
|
|
65
|
+
};
|
|
66
|
+
syncWallet({
|
|
67
|
+
locked: false,
|
|
68
|
+
address: result.address || null,
|
|
69
|
+
accounts: [firstAccount],
|
|
70
|
+
currentAccount: firstAccount,
|
|
71
|
+
balance: 0, // New wallet starts with 0 balance
|
|
72
|
+
availableBalance: 0,
|
|
73
|
+
spendableBalance: 0,
|
|
74
|
+
accountBalances: {},
|
|
75
|
+
accountSpendableBalances: {},
|
|
76
|
+
accountBalanceDetails: {},
|
|
77
|
+
});
|
|
78
|
+
navigate('onboarding-backup');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div className="relative w-[357px] h-[600px] bg-[var(--color-bg)]">
|
|
84
|
+
{/* Header with back button */}
|
|
85
|
+
<div className="flex items-center justify-between h-16 px-4 py-3 border-b border-[var(--color-divider)]">
|
|
86
|
+
<button
|
|
87
|
+
onClick={() => navigate('onboarding-start')}
|
|
88
|
+
className="p-2 -ml-2 hover:opacity-70 transition-opacity"
|
|
89
|
+
aria-label="Go back"
|
|
90
|
+
>
|
|
91
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
|
|
92
|
+
<path
|
|
93
|
+
d="M10 12L6 8L10 4"
|
|
94
|
+
stroke="var(--color-text-primary)"
|
|
95
|
+
strokeWidth="2"
|
|
96
|
+
strokeLinecap="round"
|
|
97
|
+
strokeLinejoin="round"
|
|
98
|
+
/>
|
|
99
|
+
</svg>
|
|
100
|
+
</button>
|
|
101
|
+
<h2
|
|
102
|
+
className="font-sans font-medium text-[var(--color-text-primary)]"
|
|
103
|
+
style={{
|
|
104
|
+
fontSize: 'var(--font-size-lg)',
|
|
105
|
+
lineHeight: 'var(--line-height-normal)',
|
|
106
|
+
letterSpacing: '0.01em',
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
Create new wallet
|
|
110
|
+
</h2>
|
|
111
|
+
<div className="w-8" /> {/* Spacer for centering */}
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
{/* Main content */}
|
|
115
|
+
<div className="flex flex-col justify-between h-[536px]">
|
|
116
|
+
<div className="px-4 py-2 flex flex-col gap-6">
|
|
117
|
+
{/* Icon and heading */}
|
|
118
|
+
<div className="flex flex-col items-center gap-3">
|
|
119
|
+
<div className="w-10 h-10">
|
|
120
|
+
<img src={lockIcon} alt="" className="w-full h-full" />
|
|
121
|
+
</div>
|
|
122
|
+
<h1
|
|
123
|
+
className="font-serif font-medium text-center text-[var(--color-text-primary)]"
|
|
124
|
+
style={{
|
|
125
|
+
fontSize: 'var(--font-size-xl)',
|
|
126
|
+
lineHeight: 'var(--line-height-relaxed)',
|
|
127
|
+
letterSpacing: '-0.02em',
|
|
128
|
+
}}
|
|
129
|
+
>
|
|
130
|
+
First, let's secure your
|
|
131
|
+
<br />
|
|
132
|
+
wallet with a password
|
|
133
|
+
</h1>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
{/* Password inputs */}
|
|
137
|
+
<div className="flex flex-col gap-6">
|
|
138
|
+
{/* Create password */}
|
|
139
|
+
<div className="flex flex-col gap-1.5">
|
|
140
|
+
<label
|
|
141
|
+
htmlFor="password"
|
|
142
|
+
className="font-sans font-medium text-[var(--color-text-primary)]"
|
|
143
|
+
style={{
|
|
144
|
+
fontSize: 'var(--font-size-sm)',
|
|
145
|
+
lineHeight: 'var(--line-height-snug)',
|
|
146
|
+
letterSpacing: '0.02em',
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
Create password
|
|
150
|
+
</label>
|
|
151
|
+
<div className="relative">
|
|
152
|
+
<input
|
|
153
|
+
id="password"
|
|
154
|
+
type={showPassword ? 'text' : 'password'}
|
|
155
|
+
value={password}
|
|
156
|
+
onChange={e => {
|
|
157
|
+
setPassword(e.target.value);
|
|
158
|
+
setError('');
|
|
159
|
+
}}
|
|
160
|
+
className="w-full h-[52px] px-3 py-4 bg-transparent border border-[var(--color-surface-700)] rounded-lg font-sans font-medium text-[var(--color-text-primary)] placeholder:text-[var(--color-text-secondary)] focus:outline-none focus:border-[var(--color-primary)]"
|
|
161
|
+
style={{
|
|
162
|
+
fontSize: 'var(--font-size-base)',
|
|
163
|
+
lineHeight: 'var(--line-height-snug)',
|
|
164
|
+
letterSpacing: '0.01em',
|
|
165
|
+
}}
|
|
166
|
+
autoFocus
|
|
167
|
+
/>
|
|
168
|
+
<button
|
|
169
|
+
type="button"
|
|
170
|
+
onClick={() => setShowPassword(!showPassword)}
|
|
171
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] transition-colors"
|
|
172
|
+
tabIndex={-1}
|
|
173
|
+
aria-label={showPassword ? 'Hide password' : 'Show password'}
|
|
174
|
+
>
|
|
175
|
+
{showPassword ? (
|
|
176
|
+
<EyeIcon className="w-4 h-4" />
|
|
177
|
+
) : (
|
|
178
|
+
<EyeOffIcon className="w-4 h-4" />
|
|
179
|
+
)}
|
|
180
|
+
</button>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
{/* Confirm password */}
|
|
185
|
+
<div className="flex flex-col gap-1.5">
|
|
186
|
+
<label
|
|
187
|
+
htmlFor="confirmPassword"
|
|
188
|
+
className="font-sans font-medium text-[var(--color-text-primary)]"
|
|
189
|
+
style={{
|
|
190
|
+
fontSize: 'var(--font-size-sm)',
|
|
191
|
+
lineHeight: 'var(--line-height-snug)',
|
|
192
|
+
letterSpacing: '0.02em',
|
|
193
|
+
}}
|
|
194
|
+
>
|
|
195
|
+
Confirm password
|
|
196
|
+
</label>
|
|
197
|
+
<div className="relative">
|
|
198
|
+
<input
|
|
199
|
+
id="confirmPassword"
|
|
200
|
+
type={showConfirmPassword ? 'text' : 'password'}
|
|
201
|
+
value={confirmPassword}
|
|
202
|
+
onChange={e => {
|
|
203
|
+
setConfirmPassword(e.target.value);
|
|
204
|
+
setError('');
|
|
205
|
+
}}
|
|
206
|
+
onKeyDown={e => e.key === 'Enter' && handleCreate()}
|
|
207
|
+
className="w-full h-[52px] px-3 py-4 bg-transparent border border-[var(--color-surface-700)] rounded-lg font-sans font-medium text-[var(--color-text-primary)] placeholder:text-[var(--color-text-secondary)] focus:outline-none focus:border-[var(--color-primary)]"
|
|
208
|
+
style={{
|
|
209
|
+
fontSize: 'var(--font-size-base)',
|
|
210
|
+
lineHeight: 'var(--line-height-snug)',
|
|
211
|
+
letterSpacing: '0.01em',
|
|
212
|
+
}}
|
|
213
|
+
/>
|
|
214
|
+
<button
|
|
215
|
+
type="button"
|
|
216
|
+
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
|
217
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 text-[var(--color-text-muted)] hover:text-[var(--color-text-secondary)] transition-colors"
|
|
218
|
+
tabIndex={-1}
|
|
219
|
+
aria-label={showConfirmPassword ? 'Hide password' : 'Show password'}
|
|
220
|
+
>
|
|
221
|
+
{showConfirmPassword ? (
|
|
222
|
+
<EyeIcon className="w-4 h-4" />
|
|
223
|
+
) : (
|
|
224
|
+
<EyeOffIcon className="w-4 h-4" />
|
|
225
|
+
)}
|
|
226
|
+
</button>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
|
|
231
|
+
{/* Info box */}
|
|
232
|
+
<div className="bg-[var(--color-surface-900)] rounded-lg p-3">
|
|
233
|
+
<p
|
|
234
|
+
className="font-sans font-medium text-center text-[var(--color-text-muted)]"
|
|
235
|
+
style={{
|
|
236
|
+
fontSize: 'var(--font-size-xs)',
|
|
237
|
+
lineHeight: 'var(--line-height-tight)',
|
|
238
|
+
letterSpacing: '0.02em',
|
|
239
|
+
}}
|
|
240
|
+
>
|
|
241
|
+
This password encrypts your wallet on this device. Choose something strong but
|
|
242
|
+
memorable. Your private keys never leave your browser.
|
|
243
|
+
</p>
|
|
244
|
+
</div>
|
|
245
|
+
|
|
246
|
+
{/* Error message */}
|
|
247
|
+
{error && (
|
|
248
|
+
<Alert type="error" className="mt-2">
|
|
249
|
+
{error}
|
|
250
|
+
</Alert>
|
|
251
|
+
)}
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
{/* Bottom button */}
|
|
255
|
+
<div className="border-t border-[var(--color-surface-800)] px-4 py-3">
|
|
256
|
+
<button
|
|
257
|
+
onClick={handleCreate}
|
|
258
|
+
className="w-full h-12 px-5 py-[15px] btn-secondary text-[var(--color-bg)] rounded-lg flex items-center justify-center transition-opacity hover:opacity-90"
|
|
259
|
+
style={{
|
|
260
|
+
fontFamily: 'var(--font-sans)',
|
|
261
|
+
fontSize: 'var(--font-size-base)',
|
|
262
|
+
fontWeight: 500,
|
|
263
|
+
lineHeight: 'var(--line-height-snug)',
|
|
264
|
+
letterSpacing: '0.01em',
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
Continue
|
|
268
|
+
</button>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
}
|