@unisat/wallet-state 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 UniSat
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ADDITIONAL TERMS:
24
+ This software is primarily intended for use with UniSat products and internal
25
+ development purposes. Third-party usage is at your own risk. UniSat does not
26
+ provide support or warranty for external usage of this toolkit.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # @unisat/wallet-state
2
+
3
+ Redux state management for UniSat wallet, designed to be shared between browser extension and mobile applications.
4
+
5
+ ## Features
6
+
7
+ - 🔄 Cross-platform Redux store configuration
8
+ - 🏪 Typed state slices for all wallet data
9
+ - 🪝 Typed React hooks for state access
10
+ - 🔧 Flexible middleware and persistence configuration
11
+
12
+ ## Usage
13
+
14
+ ```typescript
15
+ import { createWalletStore, useAccountsState, useWalletDispatch } from '@unisat/wallet-state';
16
+
17
+ // Create store with platform-specific persistence
18
+ const store = createWalletStore({
19
+ persistedKeys: ['ui', 'settings'],
20
+ middleware: [/* platform-specific middleware */]
21
+ });
22
+
23
+ // Use in React components
24
+ function WalletComponent() {
25
+ const accounts = useAccountsState();
26
+ const dispatch = useWalletDispatch();
27
+
28
+ // Component logic...
29
+ }
30
+ ```
31
+
32
+ ## Architecture
33
+
34
+ This package contains the Redux state management logic extracted from the browser extension, designed to be reused in the mobile application.
35
+
36
+ ### Store
37
+ - `createWalletStore()` - Configurable store factory with platform-specific options
38
+
39
+ ### Slices
40
+ - `accounts` - Account and keyring data
41
+ - `transactions` - Transaction history and pending transactions
42
+ - `settings` - User preferences and configuration
43
+ - `global` - Global application state
44
+ - `keyrings` - Keyring management state
45
+ - `ui` - UI-specific state (modals, navigation, etc.)
46
+
47
+ ### Hooks
48
+ - Typed React hooks for each state slice
49
+ - `useWalletDispatch` - Typed dispatch hook
50
+ - `useWalletSelector` - Typed selector hook
51
+
52
+ ## Migration Notes
53
+
54
+ The actual state structures and reducers are placeholders and will be populated by migrating the existing Redux logic from the extension and mobile applications.
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@unisat/wallet-state",
3
+ "version": "1.0.0",
4
+ "description": "Redux state management for UniSat wallet, shared between platforms",
5
+ "main": "lib/index.js",
6
+ "module": "lib/index.mjs",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./lib/index.mjs",
11
+ "require": "./lib/index.js",
12
+ "types": "./lib/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "lib",
17
+ "src"
18
+ ],
19
+ "peerDependencies": {
20
+ "@reduxjs/toolkit": "^1.9.0",
21
+ "react": "^18.0.0",
22
+ "react-redux": "^8.0.0",
23
+ "@unisat/wallet-types": "1.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20.0.0",
27
+ "@types/react": "^18.0.0",
28
+ "eslint": "^8.0.0",
29
+ "tsup": "^7.0.0",
30
+ "typescript": "^5.0.0",
31
+ "vitest": "^0.34.0"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "build:cjs": "tsup --format cjs",
39
+ "build:esm": "tsup --format esm",
40
+ "build:typed": "tsc --declaration --declarationMap --emitDeclarationOnly --outDir lib",
41
+ "test": "vitest",
42
+ "lint": "eslint src"
43
+ }
44
+ }
@@ -0,0 +1,244 @@
1
+ import { useCallback } from 'react'
2
+
3
+ import { Account, AddressType } from '@/shared/types'
4
+ import { useWallet } from '@/ui/utils'
5
+ import { KeyringType } from '@unisat/keyring-service/types'
6
+
7
+ import { AppState } from '..'
8
+ import { useAppDispatch, useAppSelector } from '../hooks'
9
+ import { useCurrentKeyring } from '../keyrings/hooks'
10
+ import { keyringsActions } from '../keyrings/reducer'
11
+ import { settingsActions } from '../settings/reducer'
12
+ import { accountActions } from './reducer'
13
+
14
+ export function useAccountsState(): AppState['accounts'] {
15
+ return useAppSelector(state => state.accounts)
16
+ }
17
+
18
+ export function useCurrentAccount() {
19
+ const accountsState = useAccountsState()
20
+ return accountsState.current
21
+ }
22
+
23
+ export function useCurrentAddress() {
24
+ const accountsState = useAccountsState()
25
+ return accountsState.current.address
26
+ }
27
+
28
+ export function useAccounts() {
29
+ const accountsState = useAccountsState()
30
+ return accountsState.accounts
31
+ }
32
+
33
+ export function useAccountBalance() {
34
+ const accountsState = useAccountsState()
35
+ const currentAccount = useCurrentAccount()
36
+ return (
37
+ accountsState.balanceV2Map[currentAccount.address] || {
38
+ availableBalance: 0,
39
+ unavailableBalance: 0,
40
+ totalBalance: 0,
41
+ }
42
+ )
43
+ }
44
+
45
+ export function useAddressSummary() {
46
+ const accountsState = useAccountsState()
47
+ return accountsState.addressSummary
48
+ }
49
+
50
+ export function useAccountInscriptions() {
51
+ const accountsState = useAccountsState()
52
+ const currentAccount = useCurrentAccount()
53
+ return accountsState.inscriptionsMap[currentAccount.address] || { list: [], expired: true }
54
+ }
55
+
56
+ export function useInscriptionSummary() {
57
+ const accountsState = useAccountsState()
58
+ return accountsState.inscriptionSummary
59
+ }
60
+
61
+ export function useAppSummary() {
62
+ const accountsState = useAccountsState()
63
+ return accountsState.appSummary
64
+ }
65
+
66
+ export function useUnreadAppSummary() {
67
+ const accountsState = useAccountsState()
68
+ const summary = accountsState.appSummary
69
+ return summary.apps.find(w => w.time && summary.readTabTime && w.time > summary.readTabTime)
70
+ }
71
+
72
+ export function useReadTab() {
73
+ const wallet = useWallet()
74
+ const dispatch = useAppDispatch()
75
+ const appSummary = useAppSummary()
76
+ return useCallback(
77
+ async (name: 'app' | 'home' | 'settings') => {
78
+ await wallet.readTab(name)
79
+ if (name == 'app') {
80
+ const appSummary = await wallet.getAppSummary()
81
+ dispatch(accountActions.setAppSummary(appSummary))
82
+ }
83
+ },
84
+ [dispatch, wallet, appSummary]
85
+ )
86
+ }
87
+
88
+ export function useReadApp() {
89
+ const wallet = useWallet()
90
+ const dispatch = useAppDispatch()
91
+ const appSummary = useAppSummary()
92
+ return useCallback(
93
+ async (id: number) => {
94
+ await wallet.readApp(id)
95
+ const appSummary = await wallet.getAppSummary()
96
+ dispatch(accountActions.setAppSummary(appSummary))
97
+ },
98
+ [dispatch, wallet, appSummary]
99
+ )
100
+ }
101
+
102
+ export function useHistory() {
103
+ const accountsState = useAccountsState()
104
+ const address = useAccountAddress()
105
+ return accountsState.historyMap[address] || { list: [], expired: true }
106
+ }
107
+
108
+ export function useAccountAddress() {
109
+ const currentAccount = useCurrentAccount()
110
+ return currentAccount.address
111
+ }
112
+
113
+ export function useSetCurrentAccountCallback() {
114
+ const dispatch = useAppDispatch()
115
+ return useCallback(
116
+ (account: Account) => {
117
+ dispatch(accountActions.setCurrent(account))
118
+ },
119
+ [dispatch]
120
+ )
121
+ }
122
+
123
+ export function useImportAccountCallback() {
124
+ const wallet = useWallet()
125
+ const dispatch = useAppDispatch()
126
+ const currentKeyring = useCurrentKeyring()
127
+ return useCallback(
128
+ async (privateKey: string, addressType: AddressType) => {
129
+ let success = false
130
+ let error
131
+ try {
132
+ const alianName = await wallet.getNextAlianName(currentKeyring)
133
+ await wallet.createKeyringWithPrivateKey(privateKey, addressType, alianName)
134
+ const currentAccount = await wallet.getCurrentAccount()
135
+ dispatch(accountActions.setCurrent(currentAccount))
136
+
137
+ success = true
138
+ } catch (e) {
139
+ console.log(e)
140
+ error = (e as any).message
141
+ }
142
+ return { success, error }
143
+ },
144
+ [dispatch, wallet, currentKeyring]
145
+ )
146
+ }
147
+
148
+ export function useChangeAddressFlagCallback() {
149
+ const dispatch = useAppDispatch()
150
+ const wallet = useWallet()
151
+ const currentAccount = useCurrentAccount()
152
+ return useCallback(
153
+ async (isAdd: boolean, flag: number) => {
154
+ const account = isAdd
155
+ ? await wallet.addAddressFlag(currentAccount, flag)
156
+ : await wallet.removeAddressFlag(currentAccount, flag)
157
+ dispatch(accountActions.setCurrentAddressFlag(account.flag))
158
+ },
159
+ [dispatch, wallet, currentAccount]
160
+ )
161
+ }
162
+
163
+ // export function useFetchHistoryCallback() {
164
+ // const dispatch = useAppDispatch();
165
+ // const wallet = useWallet();
166
+ // const address = useAccountAddress();
167
+ // return useCallback(async () => {
168
+ // const _accountHistory = await wallet.getAddressHistory(address);
169
+ // dispatch(
170
+ // accountActions.setHistory({
171
+ // address: address,
172
+ // list: _accountHistory
173
+ // })
174
+ // );
175
+ // }, [dispatch, wallet, address]);
176
+ // }
177
+
178
+ export function useFetchBalanceCallback() {
179
+ const dispatch = useAppDispatch()
180
+ const wallet = useWallet()
181
+ const currentAccount = useCurrentAccount()
182
+ const balance = useAccountBalance()
183
+ return useCallback(async () => {
184
+ if (!currentAccount.address) return
185
+ // const cachedBalance = await wallet.getAddressCacheBalance(currentAccount.address);
186
+ // const _accountBalance = await wallet.getAddressBalance(currentAccount.address);
187
+ // dispatch(
188
+ // accountActions.setBalance({
189
+ // address: currentAccount.address,
190
+ // amount: _accountBalance.amount,
191
+ // btc_amount: _accountBalance.btc_amount,
192
+ // inscription_amount: _accountBalance.inscription_amount,
193
+ // confirm_btc_amount: _accountBalance.confirm_btc_amount,
194
+ // pending_btc_amount: _accountBalance.pending_btc_amount
195
+ // })
196
+ // );
197
+ // if (cachedBalance.amount !== _accountBalance.amount) {
198
+ // wallet.expireUICachedData(currentAccount.address);
199
+ // dispatch(accountActions.expireHistory());
200
+ // }
201
+
202
+ const summary = await wallet.getAddressSummary(currentAccount.address)
203
+ summary.address = currentAccount.address
204
+ dispatch(accountActions.setAddressSummary(summary))
205
+
206
+ const balanceV2 = await wallet.getAddressBalanceV2(currentAccount.address)
207
+ dispatch(
208
+ accountActions.setBalanceV2({
209
+ address: currentAccount.address,
210
+ balance: balanceV2,
211
+ })
212
+ )
213
+ }, [dispatch, wallet, currentAccount, balance])
214
+ }
215
+
216
+ export function useReloadAccounts() {
217
+ const dispatch = useAppDispatch()
218
+ const wallet = useWallet()
219
+ return useCallback(async () => {
220
+ const keyrings = await wallet.getKeyrings()
221
+ dispatch(keyringsActions.setKeyrings(keyrings))
222
+
223
+ const currentKeyring = await wallet.getCurrentKeyring()
224
+ dispatch(keyringsActions.setCurrent(currentKeyring))
225
+
226
+ const _accounts = await wallet.getAccounts()
227
+ dispatch(accountActions.setAccounts(_accounts))
228
+
229
+ const account = await wallet.getCurrentAccount()
230
+ dispatch(accountActions.setCurrent(account))
231
+
232
+ dispatch(accountActions.expireBalance())
233
+ dispatch(accountActions.expireInscriptions())
234
+
235
+ wallet.getWalletConfig().then(data => {
236
+ dispatch(settingsActions.updateSettings({ walletConfig: data }))
237
+ })
238
+ }, [dispatch, wallet])
239
+ }
240
+
241
+ export function useIsKeystoneWallet() {
242
+ const currentKeyring = useCurrentKeyring()
243
+ return currentKeyring.type === KeyringType.KeystoneKeyring
244
+ }
@@ -0,0 +1,7 @@
1
+ import { useWalletSelector } from './index';
2
+
3
+ export const useGlobalState = () => {
4
+ return useWalletSelector((state) => state.global);
5
+ };
6
+
7
+ // Additional global hooks will be migrated here
@@ -0,0 +1,15 @@
1
+ import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
2
+ import type { WalletDispatch } from '../store';
3
+ import type { AppState } from '../types';
4
+
5
+ // Typed hooks for the wallet store
6
+ export const useWalletDispatch = () => useDispatch<WalletDispatch>();
7
+ export const useWalletSelector: TypedUseSelectorHook<AppState> = useSelector;
8
+
9
+ // Re-export common hooks that will be migrated from existing code
10
+ export * from './accounts';
11
+ export * from './transactions';
12
+ export * from './settings';
13
+ export * from './global';
14
+ export * from './keyrings';
15
+ export * from './ui';
@@ -0,0 +1,7 @@
1
+ import { useWalletSelector } from './index';
2
+
3
+ export const useKeyringsState = () => {
4
+ return useWalletSelector((state) => state.keyrings);
5
+ };
6
+
7
+ // Additional keyrings hooks will be migrated here
@@ -0,0 +1,7 @@
1
+ import { useWalletSelector } from './index';
2
+
3
+ export const useSettingsState = () => {
4
+ return useWalletSelector((state) => state.settings);
5
+ };
6
+
7
+ // Additional settings hooks will be migrated here