@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,482 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zustand store for popup UI state and navigation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { create } from 'zustand';
|
|
6
|
+
import { INTERNAL_METHODS, APPROVAL_CONSTANTS, NOCK_TO_NICKS } from '../shared/constants';
|
|
7
|
+
import { hasIncompleteOnboarding } from '../shared/onboarding';
|
|
8
|
+
import {
|
|
9
|
+
Account,
|
|
10
|
+
AccountBalance,
|
|
11
|
+
TransactionDetails,
|
|
12
|
+
SignRequest,
|
|
13
|
+
SignRawTxRequest,
|
|
14
|
+
TransactionRequest,
|
|
15
|
+
ConnectRequest,
|
|
16
|
+
WalletTransaction,
|
|
17
|
+
} from '../shared/types';
|
|
18
|
+
import { send } from './utils/messaging';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* All available screens in the wallet
|
|
22
|
+
*/
|
|
23
|
+
export type Screen =
|
|
24
|
+
// Onboarding flow
|
|
25
|
+
| 'onboarding-start'
|
|
26
|
+
| 'onboarding-create'
|
|
27
|
+
| 'onboarding-backup'
|
|
28
|
+
| 'onboarding-verify'
|
|
29
|
+
| 'onboarding-success'
|
|
30
|
+
| 'onboarding-import'
|
|
31
|
+
| 'onboarding-import-v0'
|
|
32
|
+
| 'onboarding-import-success'
|
|
33
|
+
| 'onboarding-resume-backup'
|
|
34
|
+
|
|
35
|
+
// Main app screens
|
|
36
|
+
| 'home'
|
|
37
|
+
| 'settings'
|
|
38
|
+
| 'theme-settings'
|
|
39
|
+
| 'lock-time'
|
|
40
|
+
| 'key-settings'
|
|
41
|
+
| 'view-secret-phrase'
|
|
42
|
+
| 'wallet-permissions'
|
|
43
|
+
| 'wallet-settings'
|
|
44
|
+
| 'wallet-styling'
|
|
45
|
+
| 'about'
|
|
46
|
+
| 'recovery-phrase'
|
|
47
|
+
|
|
48
|
+
// Transaction screens
|
|
49
|
+
| 'send'
|
|
50
|
+
| 'send-review'
|
|
51
|
+
| 'send-submitted'
|
|
52
|
+
| 'sent'
|
|
53
|
+
| 'receive'
|
|
54
|
+
| 'tx-details'
|
|
55
|
+
|
|
56
|
+
// Approval screens
|
|
57
|
+
| 'connect-approval'
|
|
58
|
+
| 'sign-message'
|
|
59
|
+
| 'approve-transaction'
|
|
60
|
+
| 'approve-sign-raw-tx'
|
|
61
|
+
|
|
62
|
+
// System
|
|
63
|
+
| 'locked';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Wallet state synced from background service worker
|
|
67
|
+
*/
|
|
68
|
+
interface WalletState {
|
|
69
|
+
locked: boolean;
|
|
70
|
+
address: string | null;
|
|
71
|
+
accounts: Account[];
|
|
72
|
+
currentAccount: Account | null;
|
|
73
|
+
balance: number;
|
|
74
|
+
availableBalance: number;
|
|
75
|
+
spendableBalance: number; // Sum of UTXOs that are available (not in_flight) - can be spent NOW
|
|
76
|
+
accountBalances: Record<string, number>; // Map of address -> confirmed balance
|
|
77
|
+
accountSpendableBalances: Record<string, number>; // Map of address -> spendable balance (available UTXOs only)
|
|
78
|
+
accountBalanceDetails: Record<string, AccountBalance>; // Map of address -> detailed balance
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Main app store
|
|
83
|
+
*/
|
|
84
|
+
interface AppStore {
|
|
85
|
+
// Navigation
|
|
86
|
+
currentScreen: Screen;
|
|
87
|
+
navigate: (screen: Screen) => void;
|
|
88
|
+
|
|
89
|
+
// Navigation history for back button
|
|
90
|
+
history: Screen[];
|
|
91
|
+
goBack: () => void;
|
|
92
|
+
|
|
93
|
+
// Wallet state (synced from service worker)
|
|
94
|
+
wallet: WalletState;
|
|
95
|
+
syncWallet: (state: WalletState) => void;
|
|
96
|
+
|
|
97
|
+
// Temporary onboarding state (cleared after completion)
|
|
98
|
+
onboardingMnemonic: string | null;
|
|
99
|
+
setOnboardingMnemonic: (mnemonic: string | null) => void;
|
|
100
|
+
onboardingMnemonicV0: string | null;
|
|
101
|
+
setOnboardingMnemonicV0: (mnemonicV0: string | null) => void;
|
|
102
|
+
|
|
103
|
+
// Last transaction details (for showing confirmation screen)
|
|
104
|
+
lastTransaction: TransactionDetails | null;
|
|
105
|
+
setLastTransaction: (transaction: TransactionDetails | null) => void;
|
|
106
|
+
|
|
107
|
+
// Pending connect request (for showing approval screen)
|
|
108
|
+
pendingConnectRequest: ConnectRequest | null;
|
|
109
|
+
setPendingConnectRequest: (request: ConnectRequest | null) => void;
|
|
110
|
+
|
|
111
|
+
// Pending sign request (for showing approval screen)
|
|
112
|
+
pendingSignRequest: SignRequest | null;
|
|
113
|
+
setPendingSignRequest: (request: SignRequest | null) => void;
|
|
114
|
+
|
|
115
|
+
// Pending sign raw transaction request (for showing approval screen)
|
|
116
|
+
pendingSignRawTxRequest: SignRawTxRequest | null;
|
|
117
|
+
setPendingSignRawTxRequest: (request: SignRawTxRequest | null) => void;
|
|
118
|
+
|
|
119
|
+
// Pending transaction request (for showing approval screen)
|
|
120
|
+
pendingTransactionRequest: TransactionRequest | null;
|
|
121
|
+
setPendingTransactionRequest: (request: TransactionRequest | null) => void;
|
|
122
|
+
|
|
123
|
+
// Wallet transactions for current account (from UTXO store)
|
|
124
|
+
walletTransactions: WalletTransaction[];
|
|
125
|
+
setWalletTransactions: (transactions: WalletTransaction[]) => void;
|
|
126
|
+
|
|
127
|
+
// Selected transaction for viewing details
|
|
128
|
+
selectedTransaction: WalletTransaction | null;
|
|
129
|
+
setSelectedTransaction: (transaction: WalletTransaction | null) => void;
|
|
130
|
+
|
|
131
|
+
// Balance fetching state
|
|
132
|
+
isBalanceFetching: boolean;
|
|
133
|
+
|
|
134
|
+
// Initialization state - true once cached balances have been loaded
|
|
135
|
+
isInitialized: boolean;
|
|
136
|
+
|
|
137
|
+
// Price data
|
|
138
|
+
priceUsd: number;
|
|
139
|
+
priceChange24h: number;
|
|
140
|
+
isPriceFetching: boolean;
|
|
141
|
+
|
|
142
|
+
// Initialize app - checks vault status and navigates appropriately
|
|
143
|
+
initialize: () => Promise<void>;
|
|
144
|
+
|
|
145
|
+
// Fetch balance from blockchain
|
|
146
|
+
fetchBalance: () => Promise<void>;
|
|
147
|
+
|
|
148
|
+
// Fetch price from CoinGecko
|
|
149
|
+
fetchPrice: () => Promise<void>;
|
|
150
|
+
|
|
151
|
+
// Fetch wallet transactions from UTXO store
|
|
152
|
+
fetchWalletTransactions: () => Promise<void>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create the store
|
|
157
|
+
*/
|
|
158
|
+
export const useStore = create<AppStore>((set, get) => ({
|
|
159
|
+
// Initial state
|
|
160
|
+
currentScreen: 'locked',
|
|
161
|
+
history: [],
|
|
162
|
+
|
|
163
|
+
wallet: {
|
|
164
|
+
locked: true,
|
|
165
|
+
address: null,
|
|
166
|
+
accounts: [],
|
|
167
|
+
currentAccount: null,
|
|
168
|
+
balance: 0,
|
|
169
|
+
availableBalance: 0,
|
|
170
|
+
spendableBalance: 0,
|
|
171
|
+
accountBalances: {},
|
|
172
|
+
accountSpendableBalances: {},
|
|
173
|
+
accountBalanceDetails: {},
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
onboardingMnemonic: null,
|
|
177
|
+
onboardingMnemonicV0: null,
|
|
178
|
+
onboardingImportVersion: 1,
|
|
179
|
+
lastTransaction: null,
|
|
180
|
+
pendingConnectRequest: null,
|
|
181
|
+
pendingSignRequest: null,
|
|
182
|
+
pendingSignRawTxRequest: null,
|
|
183
|
+
pendingTransactionRequest: null,
|
|
184
|
+
walletTransactions: [],
|
|
185
|
+
selectedTransaction: null,
|
|
186
|
+
isBalanceFetching: false,
|
|
187
|
+
isInitialized: false,
|
|
188
|
+
priceUsd: 0,
|
|
189
|
+
priceChange24h: 0,
|
|
190
|
+
isPriceFetching: false,
|
|
191
|
+
|
|
192
|
+
// Navigate to a new screen
|
|
193
|
+
navigate: (screen: Screen) => {
|
|
194
|
+
const current = get().currentScreen;
|
|
195
|
+
set({
|
|
196
|
+
currentScreen: screen,
|
|
197
|
+
history: [...get().history, current],
|
|
198
|
+
});
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Go back to previous screen
|
|
202
|
+
goBack: () => {
|
|
203
|
+
const history = get().history;
|
|
204
|
+
if (history.length === 0) return;
|
|
205
|
+
|
|
206
|
+
const previous = history[history.length - 1];
|
|
207
|
+
set({
|
|
208
|
+
currentScreen: previous,
|
|
209
|
+
history: history.slice(0, -1),
|
|
210
|
+
});
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
// Sync wallet state from background
|
|
214
|
+
syncWallet: (state: WalletState) => {
|
|
215
|
+
set({ wallet: state });
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
// Set temporary mnemonic during onboarding
|
|
219
|
+
setOnboardingMnemonic: (mnemonic: string | null) => {
|
|
220
|
+
set({ onboardingMnemonic: mnemonic });
|
|
221
|
+
},
|
|
222
|
+
setOnboardingMnemonicV0: (mnemonicV0: string | null) => {
|
|
223
|
+
set({ onboardingMnemonicV0: mnemonicV0 });
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
// Set last transaction details
|
|
227
|
+
setLastTransaction: (transaction: TransactionDetails | null) => {
|
|
228
|
+
set({ lastTransaction: transaction });
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
// Set pending connect request
|
|
232
|
+
setPendingConnectRequest: (request: ConnectRequest | null) => {
|
|
233
|
+
set({ pendingConnectRequest: request });
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// Set pending sign request
|
|
237
|
+
setPendingSignRequest: (request: SignRequest | null) => {
|
|
238
|
+
set({ pendingSignRequest: request });
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
// Set pending sign raw transaction request
|
|
242
|
+
setPendingSignRawTxRequest: (request: SignRawTxRequest | null) => {
|
|
243
|
+
set({ pendingSignRawTxRequest: request });
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
// Set pending transaction request
|
|
247
|
+
setPendingTransactionRequest: (request: TransactionRequest | null) => {
|
|
248
|
+
set({ pendingTransactionRequest: request });
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
// Set wallet transactions
|
|
252
|
+
setWalletTransactions: (transactions: WalletTransaction[]) => {
|
|
253
|
+
set({ walletTransactions: transactions });
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// Set selected transaction for viewing details
|
|
257
|
+
setSelectedTransaction: (transaction: WalletTransaction | null) => {
|
|
258
|
+
set({ selectedTransaction: transaction });
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// Initialize app on load
|
|
262
|
+
initialize: async () => {
|
|
263
|
+
try {
|
|
264
|
+
// Check if we're opening for an approval request
|
|
265
|
+
const hash = window.location.hash.slice(1); // Remove '#'
|
|
266
|
+
const isApprovalRequest =
|
|
267
|
+
hash.startsWith(APPROVAL_CONSTANTS.CONNECT_HASH_PREFIX) ||
|
|
268
|
+
hash.startsWith(APPROVAL_CONSTANTS.TRANSACTION_HASH_PREFIX) ||
|
|
269
|
+
hash.startsWith(APPROVAL_CONSTANTS.SIGN_MESSAGE_HASH_PREFIX);
|
|
270
|
+
|
|
271
|
+
// Get current vault state from service worker
|
|
272
|
+
const state = await send<{
|
|
273
|
+
locked: boolean;
|
|
274
|
+
hasVault: boolean;
|
|
275
|
+
address: string;
|
|
276
|
+
accounts: Account[];
|
|
277
|
+
currentAccount: Account | null;
|
|
278
|
+
}>(INTERNAL_METHODS.GET_STATE);
|
|
279
|
+
|
|
280
|
+
// Load cached balances from storage (for offline access)
|
|
281
|
+
const { STORAGE_KEYS } = await import('../shared/constants');
|
|
282
|
+
const stored = await chrome.storage.local.get([STORAGE_KEYS.CACHED_BALANCES]);
|
|
283
|
+
const cachedBalances = (stored[STORAGE_KEYS.CACHED_BALANCES] || {}) as Record<string, number>;
|
|
284
|
+
|
|
285
|
+
// Initial wallet state with confirmed balances (available balance computed after TX fetch)
|
|
286
|
+
const confirmedBalance = state.currentAccount
|
|
287
|
+
? cachedBalances[state.currentAccount.address] || 0
|
|
288
|
+
: 0;
|
|
289
|
+
const walletState: WalletState = {
|
|
290
|
+
locked: state.locked,
|
|
291
|
+
address: state.address || null,
|
|
292
|
+
accounts: state.accounts || [],
|
|
293
|
+
currentAccount: state.currentAccount || null,
|
|
294
|
+
balance: confirmedBalance,
|
|
295
|
+
availableBalance: confirmedBalance, // Will be recalculated after fetching transactions
|
|
296
|
+
spendableBalance: confirmedBalance, // Will be recalculated after fetching transactions
|
|
297
|
+
accountBalances: cachedBalances, // Load all cached balances
|
|
298
|
+
accountSpendableBalances: cachedBalances, // Will be recalculated after fetching transactions
|
|
299
|
+
accountBalanceDetails: {},
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
// Determine initial screen
|
|
303
|
+
let initialScreen: Screen;
|
|
304
|
+
|
|
305
|
+
if (isApprovalRequest) {
|
|
306
|
+
// For approval requests, don't override the screen
|
|
307
|
+
// Let the approval useEffect handle navigation
|
|
308
|
+
initialScreen = walletState.locked ? 'locked' : 'home';
|
|
309
|
+
} else if (!state.hasVault) {
|
|
310
|
+
// No vault exists - start onboarding
|
|
311
|
+
initialScreen = 'onboarding-start';
|
|
312
|
+
} else {
|
|
313
|
+
// Check if user has incomplete onboarding (created wallet but didn't complete backup)
|
|
314
|
+
const incompleteOnboarding = await hasIncompleteOnboarding();
|
|
315
|
+
|
|
316
|
+
if (incompleteOnboarding) {
|
|
317
|
+
// User needs to complete their backup - show resume screen
|
|
318
|
+
initialScreen = 'onboarding-resume-backup';
|
|
319
|
+
} else if (walletState.locked) {
|
|
320
|
+
// Vault exists but locked
|
|
321
|
+
initialScreen = 'locked';
|
|
322
|
+
} else {
|
|
323
|
+
// Vault unlocked - go to home
|
|
324
|
+
initialScreen = 'home';
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
set({
|
|
329
|
+
wallet: walletState,
|
|
330
|
+
currentScreen: initialScreen,
|
|
331
|
+
isInitialized: true,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Fetch balance if wallet is unlocked
|
|
335
|
+
if (!walletState.locked && walletState.address) {
|
|
336
|
+
get().fetchBalance();
|
|
337
|
+
get().fetchWalletTransactions();
|
|
338
|
+
}
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error('Failed to initialize app:', error);
|
|
341
|
+
// Default to locked screen on error
|
|
342
|
+
set({ currentScreen: 'locked' });
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
// Fetch balance from UTXO store for all accounts
|
|
347
|
+
// Also syncs UTXOs from chain (runs in popup context where WASM works)
|
|
348
|
+
fetchBalance: async () => {
|
|
349
|
+
try {
|
|
350
|
+
set({ isBalanceFetching: true });
|
|
351
|
+
|
|
352
|
+
const accounts = get().wallet.accounts;
|
|
353
|
+
const currentAccount = get().wallet.currentAccount;
|
|
354
|
+
|
|
355
|
+
if (!currentAccount || accounts.length === 0) {
|
|
356
|
+
set({ isBalanceFetching: false });
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Sync UTXOs from chain for all accounts (runs in popup context where WASM works)
|
|
361
|
+
try {
|
|
362
|
+
const { syncAccountUTXOs } = await import('../shared/utxo-sync');
|
|
363
|
+
const { createBrowserClient } = await import('../shared/rpc-client-browser');
|
|
364
|
+
const rpcClient = createBrowserClient();
|
|
365
|
+
|
|
366
|
+
for (const account of accounts) {
|
|
367
|
+
try {
|
|
368
|
+
await syncAccountUTXOs(account.address, rpcClient);
|
|
369
|
+
} catch (syncErr) {
|
|
370
|
+
console.warn(`[Store] UTXO sync failed for ${account.name}:`, syncErr);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} catch (importErr) {
|
|
374
|
+
console.warn('[Store] Could not import sync modules:', importErr);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Fetch UTXO store balance for ALL accounts
|
|
378
|
+
const accountBalances: Record<string, number> = {};
|
|
379
|
+
const accountSpendableBalances: Record<string, number> = {};
|
|
380
|
+
|
|
381
|
+
for (const account of accounts) {
|
|
382
|
+
try {
|
|
383
|
+
const storeBalance = await send<{
|
|
384
|
+
available: number;
|
|
385
|
+
spendableNow: number;
|
|
386
|
+
pendingOut: number;
|
|
387
|
+
pendingChange: number;
|
|
388
|
+
total: number;
|
|
389
|
+
utxoCount: number;
|
|
390
|
+
availableUtxoCount: number;
|
|
391
|
+
}>(INTERNAL_METHODS.GET_BALANCE_FROM_STORE, [account.address]);
|
|
392
|
+
|
|
393
|
+
// Convert from nicks to NOCK for display
|
|
394
|
+
const availableNock = storeBalance.available / NOCK_TO_NICKS;
|
|
395
|
+
const spendableNock = storeBalance.spendableNow / NOCK_TO_NICKS;
|
|
396
|
+
accountBalances[account.address] = availableNock;
|
|
397
|
+
accountSpendableBalances[account.address] = spendableNock;
|
|
398
|
+
} catch (err) {
|
|
399
|
+
console.warn(`[Store] Could not get balance for ${account.name}:`, err);
|
|
400
|
+
// Keep previous balance if fetch fails
|
|
401
|
+
accountBalances[account.address] = get().wallet.accountBalances[account.address] ?? 0;
|
|
402
|
+
accountSpendableBalances[account.address] =
|
|
403
|
+
get().wallet.accountSpendableBalances[account.address] ?? 0;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Get current account's detailed balance
|
|
408
|
+
const currentBalance = accountBalances[currentAccount.address] ?? 0;
|
|
409
|
+
const currentSpendable = accountSpendableBalances[currentAccount.address] ?? 0;
|
|
410
|
+
|
|
411
|
+
// Persist balances to chrome.storage.local for offline access
|
|
412
|
+
try {
|
|
413
|
+
const { STORAGE_KEYS } = await import('../shared/constants');
|
|
414
|
+
await chrome.storage.local.set({
|
|
415
|
+
[STORAGE_KEYS.CACHED_BALANCES]: accountBalances,
|
|
416
|
+
});
|
|
417
|
+
} catch (cacheErr) {
|
|
418
|
+
console.warn('[Store] Failed to cache balances:', cacheErr);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
set({
|
|
422
|
+
wallet: {
|
|
423
|
+
...get().wallet,
|
|
424
|
+
balance: currentBalance,
|
|
425
|
+
availableBalance: currentBalance,
|
|
426
|
+
spendableBalance: currentSpendable,
|
|
427
|
+
accountBalances,
|
|
428
|
+
accountSpendableBalances,
|
|
429
|
+
},
|
|
430
|
+
isBalanceFetching: false,
|
|
431
|
+
});
|
|
432
|
+
} catch (error) {
|
|
433
|
+
console.error('[Store] Failed to fetch balance:', error);
|
|
434
|
+
set({ isBalanceFetching: false });
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
|
|
438
|
+
// Fetch price from CoinGecko
|
|
439
|
+
fetchPrice: async () => {
|
|
440
|
+
try {
|
|
441
|
+
set({ isPriceFetching: true });
|
|
442
|
+
|
|
443
|
+
const { fetchNockPrice } = await import('../shared/price-api');
|
|
444
|
+
const priceData = await fetchNockPrice();
|
|
445
|
+
|
|
446
|
+
set({
|
|
447
|
+
priceUsd: priceData.usd,
|
|
448
|
+
priceChange24h: priceData.usd_24h_change,
|
|
449
|
+
isPriceFetching: false,
|
|
450
|
+
});
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error('[Store] Failed to fetch price:', error);
|
|
453
|
+
set({ isPriceFetching: false });
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
|
|
457
|
+
// Fetch wallet transactions from UTXO store
|
|
458
|
+
// Called directly from popup context (not via background) to avoid service worker limitations
|
|
459
|
+
fetchWalletTransactions: async () => {
|
|
460
|
+
try {
|
|
461
|
+
const currentAccount = get().wallet.currentAccount;
|
|
462
|
+
if (!currentAccount) return;
|
|
463
|
+
|
|
464
|
+
// Capture the address we're fetching for to detect account switches
|
|
465
|
+
const fetchingForAddress = currentAccount.address;
|
|
466
|
+
|
|
467
|
+
// Import and call directly from popup context (avoids service worker document issue)
|
|
468
|
+
const { getWalletTransactions } = await import('../shared/utxo-store');
|
|
469
|
+
const transactions = await getWalletTransactions(fetchingForAddress);
|
|
470
|
+
|
|
471
|
+
// Check if user switched accounts while we were fetching
|
|
472
|
+
const accountAfterFetch = get().wallet.currentAccount;
|
|
473
|
+
if (accountAfterFetch?.address !== fetchingForAddress) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
set({ walletTransactions: transactions });
|
|
478
|
+
} catch (error) {
|
|
479
|
+
console.error('Failed to fetch wallet transactions:', error);
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
}));
|