@unisat/wallet-state 1.0.0 → 1.0.2
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/lib/index.d.mts +934 -0
- package/lib/index.d.ts +934 -0
- package/lib/index.js +2225 -0
- package/lib/index.js.map +1 -0
- package/lib/index.mjs +2124 -0
- package/lib/index.mjs.map +1 -0
- package/package.json +19 -5
- package/src/actions/global.ts +5 -0
- package/src/context/I18nContext.tsx +191 -0
- package/src/context/PriceContext.tsx +81 -0
- package/src/context/WalletContext.tsx +703 -0
- package/src/context/index.ts +3 -0
- package/src/hooks/accounts.ts +23 -21
- package/src/hooks/approval.ts +72 -0
- package/src/hooks/base.ts +6 -0
- package/src/hooks/discovery.ts +29 -0
- package/src/hooks/global.ts +129 -5
- package/src/hooks/i18n.ts +53 -0
- package/src/hooks/index.ts +9 -15
- package/src/hooks/keyrings.ts +14 -5
- package/src/hooks/settings.ts +318 -5
- package/src/hooks/transactions.ts +44 -38
- package/src/hooks/ui.ts +133 -5
- package/src/index.ts +42 -5
- package/src/{slices → reducers}/accounts.ts +12 -12
- package/src/reducers/discovery.ts +73 -0
- package/src/reducers/global.ts +51 -0
- package/src/{slices → reducers}/keyrings.ts +7 -6
- package/src/{slices → reducers}/settings.ts +5 -5
- package/src/{slices → reducers}/transactions.ts +4 -5
- package/src/{slices → reducers}/ui.ts +4 -5
- package/src/updater/accounts.ts +107 -0
- package/src/updater/index.ts +1 -0
- package/src/utils/bitcoin-utils.ts +81 -0
- package/src/utils/eventBus.ts +49 -0
- package/src/utils/i18n.ts +41 -0
- package/src/slices/global.ts +0 -52
- package/src/slices/index.ts +0 -10
- package/src/store/index.ts +0 -43
- package/src/types/index.ts +0 -37
package/src/index.ts
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { load, save } from 'redux-localstorage-simple'
|
|
2
|
+
|
|
3
|
+
import { configureStore } from '@reduxjs/toolkit'
|
|
4
|
+
import { setupListeners } from '@reduxjs/toolkit/query/react'
|
|
5
|
+
|
|
6
|
+
import accounts from './reducers/accounts'
|
|
7
|
+
import discovery from './reducers/discovery'
|
|
8
|
+
import { updateVersion } from './actions/global'
|
|
9
|
+
import global from './reducers/global'
|
|
10
|
+
import keyrings from './reducers/keyrings'
|
|
11
|
+
import settings from './reducers/settings'
|
|
12
|
+
import transactions from './reducers/transactions'
|
|
13
|
+
import ui from './reducers/ui'
|
|
14
|
+
|
|
15
|
+
const PERSISTED_KEYS: string[] = ['ui', 'discovery']
|
|
16
|
+
const store = configureStore({
|
|
17
|
+
reducer: {
|
|
18
|
+
accounts,
|
|
19
|
+
transactions,
|
|
20
|
+
settings,
|
|
21
|
+
global,
|
|
22
|
+
keyrings,
|
|
23
|
+
ui,
|
|
24
|
+
discovery,
|
|
25
|
+
},
|
|
26
|
+
middleware: getDefaultMiddleware =>
|
|
27
|
+
getDefaultMiddleware({ thunk: true }).concat(save({ states: PERSISTED_KEYS, debounce: 1000 })),
|
|
28
|
+
preloadedState: load({ states: PERSISTED_KEYS, disableWarnings: true }),
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
store.dispatch(updateVersion())
|
|
32
|
+
|
|
33
|
+
setupListeners(store.dispatch)
|
|
34
|
+
|
|
35
|
+
export default store
|
|
36
|
+
|
|
37
|
+
export type AppState = ReturnType<typeof store.getState>
|
|
38
|
+
export type AppDispatch = typeof store.dispatch
|
|
39
|
+
|
|
40
|
+
export * from './context'
|
|
41
|
+
export * from './hooks'
|
|
42
|
+
export * from './updater'
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
Inscription,
|
|
7
7
|
InscriptionSummary,
|
|
8
8
|
TxHistoryItem,
|
|
9
|
-
} from '
|
|
10
|
-
import { createSlice } from '@reduxjs/toolkit'
|
|
9
|
+
} from '@unisat/wallet-shared'
|
|
10
|
+
import { createSlice, Slice } from '@reduxjs/toolkit'
|
|
11
11
|
|
|
12
|
-
import { updateVersion } from '../global
|
|
12
|
+
import { updateVersion } from '../actions/global'
|
|
13
13
|
|
|
14
14
|
export interface AccountsState {
|
|
15
15
|
accounts: Account[]
|
|
@@ -86,7 +86,7 @@ export const initialState: AccountsState = {
|
|
|
86
86
|
},
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const slice = createSlice({
|
|
89
|
+
const slice: Slice<AccountsState> = createSlice({
|
|
90
90
|
name: 'accounts',
|
|
91
91
|
initialState,
|
|
92
92
|
reducers: {
|
|
@@ -132,12 +132,12 @@ const slice = createSlice({
|
|
|
132
132
|
pending_btc_amount: '0',
|
|
133
133
|
expired: true,
|
|
134
134
|
}
|
|
135
|
-
state.balanceMap[address]
|
|
136
|
-
state.balanceMap[address]
|
|
137
|
-
state.balanceMap[address]
|
|
138
|
-
state.balanceMap[address]
|
|
139
|
-
state.balanceMap[address]
|
|
140
|
-
state.balanceMap[address]
|
|
135
|
+
state.balanceMap[address]!.amount = amount
|
|
136
|
+
state.balanceMap[address]!.btc_amount = btc_amount
|
|
137
|
+
state.balanceMap[address]!.inscription_amount = inscription_amount
|
|
138
|
+
state.balanceMap[address]!.confirm_btc_amount = confirm_btc_amount
|
|
139
|
+
state.balanceMap[address]!.pending_btc_amount = pending_btc_amount
|
|
140
|
+
state.balanceMap[address]!.expired = false
|
|
141
141
|
},
|
|
142
142
|
setBalanceV2(
|
|
143
143
|
state,
|
|
@@ -244,11 +244,11 @@ const slice = createSlice({
|
|
|
244
244
|
) {
|
|
245
245
|
const account = action.payload
|
|
246
246
|
if (state.current.key === account.key) {
|
|
247
|
-
state.current.alianName = account.alianName
|
|
247
|
+
state.current.alianName = account.alianName!
|
|
248
248
|
}
|
|
249
249
|
state.accounts.forEach(v => {
|
|
250
250
|
if (v.key === account.key) {
|
|
251
|
-
v.alianName = account.alianName
|
|
251
|
+
v.alianName = account.alianName!
|
|
252
252
|
}
|
|
253
253
|
})
|
|
254
254
|
},
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ChainType } from '@unisat/wallet-types'
|
|
2
|
+
import { AppInfo } from '@unisat/wallet-shared'
|
|
3
|
+
import { createSlice, Slice } from '@reduxjs/toolkit'
|
|
4
|
+
|
|
5
|
+
export interface DiscoveryState {
|
|
6
|
+
bannerList: { id: string; img: string; link: string }[]
|
|
7
|
+
appList: { tab: string; items: AppInfo[] }[]
|
|
8
|
+
lastFetchTime: number
|
|
9
|
+
lastFetchChainType: ChainType
|
|
10
|
+
cachedBannerIds: string[]
|
|
11
|
+
hasNewBanner: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const initialState: DiscoveryState = {
|
|
15
|
+
bannerList: [],
|
|
16
|
+
appList: [],
|
|
17
|
+
lastFetchTime: 0,
|
|
18
|
+
lastFetchChainType: ChainType.BITCOIN_MAINNET,
|
|
19
|
+
cachedBannerIds: [],
|
|
20
|
+
hasNewBanner: true,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const slice: Slice<DiscoveryState> = createSlice({
|
|
24
|
+
name: 'discovery',
|
|
25
|
+
initialState,
|
|
26
|
+
reducers: {
|
|
27
|
+
reset(state) {
|
|
28
|
+
return initialState
|
|
29
|
+
},
|
|
30
|
+
setBannerList(
|
|
31
|
+
state,
|
|
32
|
+
action: {
|
|
33
|
+
payload: {
|
|
34
|
+
bannerList: { id: string; img: string; link: string }[]
|
|
35
|
+
chainType: ChainType
|
|
36
|
+
fetchTime: number
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
) {
|
|
40
|
+
const { payload } = action
|
|
41
|
+
const newBannerIds = payload.bannerList.map(banner => banner.id)
|
|
42
|
+
const hasNewBanner = newBannerIds.some(id => !state.cachedBannerIds.includes(id))
|
|
43
|
+
|
|
44
|
+
state.bannerList = payload.bannerList
|
|
45
|
+
state.lastFetchChainType = payload.chainType
|
|
46
|
+
state.lastFetchTime = payload.fetchTime
|
|
47
|
+
state.hasNewBanner = hasNewBanner
|
|
48
|
+
|
|
49
|
+
state.cachedBannerIds = newBannerIds
|
|
50
|
+
},
|
|
51
|
+
setAppList(
|
|
52
|
+
state,
|
|
53
|
+
action: {
|
|
54
|
+
payload: {
|
|
55
|
+
appList: { tab: string; items: AppInfo[] }[]
|
|
56
|
+
chainType: ChainType
|
|
57
|
+
fetchTime: number
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
) {
|
|
61
|
+
const { payload } = action
|
|
62
|
+
state.appList = payload.appList
|
|
63
|
+
state.lastFetchChainType = payload.chainType
|
|
64
|
+
state.lastFetchTime = payload.fetchTime
|
|
65
|
+
},
|
|
66
|
+
clearNewBannerFlag(state) {
|
|
67
|
+
state.hasNewBanner = false
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
export const discoveryActions = slice.actions
|
|
73
|
+
export default slice.reducer
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createSlice, PayloadAction, Slice, SliceCaseReducers } from '@reduxjs/toolkit'
|
|
2
|
+
|
|
3
|
+
import { updateVersion } from '../actions/global'
|
|
4
|
+
|
|
5
|
+
export type TabOption = 'home' | 'discover' | 'settings'
|
|
6
|
+
|
|
7
|
+
export interface GlobalState {
|
|
8
|
+
tab: TabOption
|
|
9
|
+
isUnlocked: boolean
|
|
10
|
+
isReady: boolean
|
|
11
|
+
isBooted: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const initialState: GlobalState = {
|
|
15
|
+
tab: 'home',
|
|
16
|
+
isUnlocked: false,
|
|
17
|
+
isReady: false,
|
|
18
|
+
isBooted: false,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const reducers: SliceCaseReducers<GlobalState> = {
|
|
22
|
+
reset: state => initialState,
|
|
23
|
+
update: (
|
|
24
|
+
state,
|
|
25
|
+
action: PayloadAction<{
|
|
26
|
+
tab?: TabOption
|
|
27
|
+
isUnlocked?: boolean
|
|
28
|
+
isReady?: boolean
|
|
29
|
+
isBooted?: boolean
|
|
30
|
+
}>
|
|
31
|
+
) => {
|
|
32
|
+
const { payload } = action
|
|
33
|
+
state = Object.assign({}, state, payload)
|
|
34
|
+
return state
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const slice = createSlice({
|
|
39
|
+
name: 'global',
|
|
40
|
+
initialState,
|
|
41
|
+
reducers,
|
|
42
|
+
extraReducers: builder => {
|
|
43
|
+
builder.addCase(updateVersion, state => {
|
|
44
|
+
// todo
|
|
45
|
+
})
|
|
46
|
+
},
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export const globalActions = slice.actions as any
|
|
50
|
+
|
|
51
|
+
export default slice.reducer
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Account,
|
|
2
|
-
import {
|
|
1
|
+
import { Account, WalletKeyring } from '@unisat/wallet-shared'
|
|
2
|
+
import { AddressType } from '@unisat/wallet-types'
|
|
3
|
+
import { createSlice, Slice } from '@reduxjs/toolkit'
|
|
3
4
|
|
|
4
|
-
import { updateVersion } from '../global
|
|
5
|
+
import { updateVersion } from '../actions/global'
|
|
5
6
|
|
|
6
7
|
export interface KeyringsState {
|
|
7
8
|
keyrings: WalletKeyring[]
|
|
@@ -23,7 +24,7 @@ export const initialState: KeyringsState = {
|
|
|
23
24
|
current: initialKeyring,
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
const slice = createSlice({
|
|
27
|
+
const slice: Slice<KeyringsState> = createSlice({
|
|
27
28
|
name: 'keyrings',
|
|
28
29
|
initialState,
|
|
29
30
|
reducers: {
|
|
@@ -57,14 +58,14 @@ const slice = createSlice({
|
|
|
57
58
|
|
|
58
59
|
state.current.accounts.forEach(v => {
|
|
59
60
|
if (v.key === account.key) {
|
|
60
|
-
v.alianName = account.alianName
|
|
61
|
+
v.alianName = account.alianName!
|
|
61
62
|
}
|
|
62
63
|
})
|
|
63
64
|
|
|
64
65
|
state.keyrings.forEach(v => {
|
|
65
66
|
v.accounts.forEach(w => {
|
|
66
67
|
if (w.key === account.key) {
|
|
67
|
-
w.alianName = account.alianName
|
|
68
|
+
w.alianName = account.alianName!
|
|
68
69
|
}
|
|
69
70
|
})
|
|
70
71
|
})
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { createSlice } from '@reduxjs/toolkit'
|
|
1
|
+
import { DEFAULT_LOCKTIME_ID, WalletConfig } from '@unisat/wallet-shared'
|
|
2
|
+
import { ChainType, NetworkType } from '@unisat/wallet-types'
|
|
3
|
+
import { createSlice, Slice } from '@reduxjs/toolkit'
|
|
4
4
|
|
|
5
|
-
import { updateVersion } from '../global
|
|
5
|
+
import { updateVersion } from '../actions/global'
|
|
6
6
|
|
|
7
7
|
export interface SettingsState {
|
|
8
8
|
locale: string
|
|
@@ -31,7 +31,7 @@ export const initialState: SettingsState = {
|
|
|
31
31
|
developerMode: false,
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const slice = createSlice({
|
|
34
|
+
const slice: Slice<SettingsState> = createSlice({
|
|
35
35
|
name: 'settings',
|
|
36
36
|
initialState,
|
|
37
37
|
reducers: {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { Inscription } from '
|
|
2
|
-
import { createSlice } from '@reduxjs/toolkit'
|
|
3
|
-
import { UnspentOutput } from '@unisat/tx-helpers/types'
|
|
1
|
+
import { Inscription, UnspentOutput } from '@unisat/wallet-shared'
|
|
2
|
+
import { createSlice, Slice } from '@reduxjs/toolkit'
|
|
4
3
|
|
|
5
|
-
import { updateVersion } from '../global
|
|
4
|
+
import { updateVersion } from '../actions/global'
|
|
6
5
|
|
|
7
6
|
export interface BitcoinTx {
|
|
8
7
|
fromAddress: string
|
|
@@ -138,7 +137,7 @@ export const initialState: TransactionsState = {
|
|
|
138
137
|
assetUtxos_runes: [],
|
|
139
138
|
}
|
|
140
139
|
|
|
141
|
-
const slice = createSlice({
|
|
140
|
+
const slice: Slice<TransactionsState> = createSlice({
|
|
142
141
|
name: 'transactions',
|
|
143
142
|
initialState,
|
|
144
143
|
reducers: {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Inscription } from '
|
|
2
|
-
import { createSlice } from '@reduxjs/toolkit'
|
|
1
|
+
import { Inscription } from '@unisat/wallet-shared'
|
|
2
|
+
import { createSlice, Slice } from '@reduxjs/toolkit'
|
|
3
3
|
|
|
4
|
-
import { updateVersion } from '../global
|
|
4
|
+
import { updateVersion } from '../actions/global'
|
|
5
5
|
|
|
6
6
|
export interface UIState {
|
|
7
7
|
assetTabKey: AssetTabKey
|
|
@@ -66,7 +66,6 @@ export const initialState: UIState = {
|
|
|
66
66
|
toInfo: {
|
|
67
67
|
address: '',
|
|
68
68
|
domain: '',
|
|
69
|
-
inscription: undefined,
|
|
70
69
|
},
|
|
71
70
|
inputAmount: '',
|
|
72
71
|
enableRBF: false,
|
|
@@ -80,7 +79,7 @@ export const initialState: UIState = {
|
|
|
80
79
|
isBalanceHidden: false,
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
const slice = createSlice({
|
|
82
|
+
const slice: Slice<UIState> = createSlice({
|
|
84
83
|
name: 'ui',
|
|
85
84
|
initialState,
|
|
86
85
|
reducers: {
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Account } from '@unisat/wallet-shared'
|
|
4
|
+
import eventBus from '../utils/eventBus'
|
|
5
|
+
|
|
6
|
+
import { useIsUnlocked } from '../hooks/global'
|
|
7
|
+
import { globalActions } from '../reducers/global'
|
|
8
|
+
import { useAppDispatch } from '../hooks/base'
|
|
9
|
+
import { settingsActions } from '../reducers/settings'
|
|
10
|
+
import { useCurrentAccount, useFetchBalanceCallback, useReloadAccounts } from '../hooks/accounts'
|
|
11
|
+
import { accountActions } from '../reducers/accounts'
|
|
12
|
+
import { ChainType } from '@unisat/wallet-types'
|
|
13
|
+
import { useWallet } from '../context/WalletContext'
|
|
14
|
+
|
|
15
|
+
export function AccountUpdater() {
|
|
16
|
+
const dispatch = useAppDispatch()
|
|
17
|
+
const wallet = useWallet()
|
|
18
|
+
const currentAccount = useCurrentAccount()
|
|
19
|
+
const isUnlocked = useIsUnlocked()
|
|
20
|
+
const selfRef = useRef({
|
|
21
|
+
preAccountKey: '',
|
|
22
|
+
loadingBalance: false,
|
|
23
|
+
loadingHistory: false,
|
|
24
|
+
})
|
|
25
|
+
const self = selfRef.current
|
|
26
|
+
|
|
27
|
+
const reloadAccounts = useReloadAccounts()
|
|
28
|
+
const onCurrentChange = useCallback(async () => {
|
|
29
|
+
if (isUnlocked && currentAccount && currentAccount.key != self.preAccountKey) {
|
|
30
|
+
self.preAccountKey = currentAccount.key
|
|
31
|
+
|
|
32
|
+
// setLoading(true);
|
|
33
|
+
|
|
34
|
+
reloadAccounts()
|
|
35
|
+
|
|
36
|
+
// setLoading(false);
|
|
37
|
+
}
|
|
38
|
+
}, [dispatch, currentAccount, wallet, isUnlocked])
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
onCurrentChange()
|
|
42
|
+
}, [currentAccount && currentAccount.key, isUnlocked])
|
|
43
|
+
|
|
44
|
+
const fetchBalance = useFetchBalanceCallback()
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (self.loadingBalance) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
if (!isUnlocked) {
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
self.loadingBalance = true
|
|
53
|
+
fetchBalance().finally(() => {
|
|
54
|
+
self.loadingBalance = false
|
|
55
|
+
})
|
|
56
|
+
}, [fetchBalance, wallet, isUnlocked, self])
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const accountChangeHandler = (account: Account) => {
|
|
60
|
+
if (account && account.address) {
|
|
61
|
+
dispatch((accountActions as any).setCurrent(account))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
eventBus.addEventListener('accountsChanged', accountChangeHandler)
|
|
65
|
+
return () => {
|
|
66
|
+
eventBus.removeEventListener('accountsChanged', accountChangeHandler)
|
|
67
|
+
}
|
|
68
|
+
}, [dispatch])
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const chaintChangeHandler = (params: { type: ChainType }) => {
|
|
72
|
+
dispatch(
|
|
73
|
+
(settingsActions as any).updateSettings({
|
|
74
|
+
chainType: params.type,
|
|
75
|
+
})
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
reloadAccounts()
|
|
79
|
+
}
|
|
80
|
+
eventBus.addEventListener('chainChanged', chaintChangeHandler)
|
|
81
|
+
return () => {
|
|
82
|
+
eventBus.removeEventListener('chainChanged', chaintChangeHandler)
|
|
83
|
+
}
|
|
84
|
+
}, [dispatch])
|
|
85
|
+
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
const lockHandler = () => {
|
|
88
|
+
dispatch(globalActions.update({ isUnlocked: false }))
|
|
89
|
+
}
|
|
90
|
+
eventBus.addEventListener('lock', lockHandler)
|
|
91
|
+
return () => {
|
|
92
|
+
eventBus.removeEventListener('lock', lockHandler)
|
|
93
|
+
}
|
|
94
|
+
}, [dispatch])
|
|
95
|
+
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
const unlockHandler = () => {
|
|
98
|
+
dispatch(globalActions.update({ isUnlocked: true }))
|
|
99
|
+
}
|
|
100
|
+
eventBus.addEventListener('unlock', unlockHandler)
|
|
101
|
+
return () => {
|
|
102
|
+
eventBus.removeEventListener('unlock', unlockHandler)
|
|
103
|
+
}
|
|
104
|
+
}, [dispatch])
|
|
105
|
+
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AccountUpdater } from './accounts'
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AddressType, NetworkType } from '@unisat/wallet-types'
|
|
2
|
+
|
|
3
|
+
export function getAddressType(address: string, networkType?: NetworkType) {
|
|
4
|
+
if (address.startsWith('bc1q') || address.startsWith('tb1q')) {
|
|
5
|
+
return AddressType.P2WPKH
|
|
6
|
+
} else if (address.startsWith('bc1p') || address.startsWith('tb1p')) {
|
|
7
|
+
return AddressType.P2TR
|
|
8
|
+
} else if (address.startsWith('1') || address.startsWith('m') || address.startsWith('n')) {
|
|
9
|
+
return AddressType.P2PKH
|
|
10
|
+
} else if (address.startsWith('3') || address.startsWith('2')) {
|
|
11
|
+
return AddressType.P2SH_P2WPKH
|
|
12
|
+
} else {
|
|
13
|
+
return AddressType.UNKNOWN
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isValidAddress(address: string, networkType?: NetworkType) {
|
|
18
|
+
const addressType = getAddressType(address, networkType)
|
|
19
|
+
if (addressType === AddressType.UNKNOWN) {
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getAddressUtxoDust(address: string) {
|
|
27
|
+
const addressType = getAddressType(address)
|
|
28
|
+
if (addressType === AddressType.P2WPKH) {
|
|
29
|
+
return 294
|
|
30
|
+
} else if (addressType === AddressType.P2TR) {
|
|
31
|
+
return 330
|
|
32
|
+
} else {
|
|
33
|
+
return 546
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function isValidHdPath(path: string): boolean {
|
|
38
|
+
if (!path || typeof path !== 'string') {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// HD path should start with 'm' or 'M'
|
|
43
|
+
if (!path.startsWith('m') && !path.startsWith('M')) {
|
|
44
|
+
return false
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Split by '/' and validate each component
|
|
48
|
+
const components = path.split('/')
|
|
49
|
+
|
|
50
|
+
// First component should be 'm' or 'M'
|
|
51
|
+
if (components[0] !== 'm' && components[0] !== 'M') {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Validate each path component after 'm'
|
|
56
|
+
for (let i = 1; i < components.length; i++) {
|
|
57
|
+
const component = components[i]
|
|
58
|
+
|
|
59
|
+
if (!component) {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check if it's a hardened path (ends with ')
|
|
64
|
+
const isHardened = component.endsWith("'")
|
|
65
|
+
const numberPart = isHardened ? component.slice(0, -1) : component
|
|
66
|
+
|
|
67
|
+
// Check if the number part is a valid integer
|
|
68
|
+
if (!/^\d+$/.test(numberPart)) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const num = parseInt(numberPart, 10)
|
|
73
|
+
|
|
74
|
+
// Check if number is within valid range (0 to 2^31-1)
|
|
75
|
+
if (num < 0 || num >= Math.pow(2, 31)) {
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return true
|
|
81
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
type Listener = (params?: any) => void
|
|
2
|
+
|
|
3
|
+
class EventBus {
|
|
4
|
+
events: Record<string, Listener[]> = {}
|
|
5
|
+
|
|
6
|
+
emit = (type: string, params?: any) => {
|
|
7
|
+
const listeners = this.events[type]
|
|
8
|
+
if (listeners) {
|
|
9
|
+
listeners.forEach(fn => {
|
|
10
|
+
fn(params)
|
|
11
|
+
})
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
once = (type: string, fn: Listener) => {
|
|
16
|
+
const listeners = this.events[type]
|
|
17
|
+
const func = (...params: any[]) => {
|
|
18
|
+
fn(...params)
|
|
19
|
+
this.events[type] = this.events[type]!.filter(item => item !== func)
|
|
20
|
+
}
|
|
21
|
+
if (listeners) {
|
|
22
|
+
this.events[type]!.push(func)
|
|
23
|
+
} else {
|
|
24
|
+
this.events[type] = [func]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
addEventListener = (type: string, fn: Listener) => {
|
|
29
|
+
const listeners = this.events[type]
|
|
30
|
+
if (listeners) {
|
|
31
|
+
this.events[type]!.push(fn)
|
|
32
|
+
} else {
|
|
33
|
+
this.events[type] = [fn]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
removeEventListener = (type: string, fn: Listener) => {
|
|
38
|
+
const listeners = this.events[type]
|
|
39
|
+
if (listeners) {
|
|
40
|
+
this.events[type] = this.events[type]!.filter(item => item !== fn)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
removeAllEventListeners = (type: string) => {
|
|
45
|
+
this.events[type] = []
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default new EventBus()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import i18n from 'i18next'
|
|
2
|
+
import { initReactI18next } from 'react-i18next'
|
|
3
|
+
|
|
4
|
+
export const fetchLocale = async locale => {
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
const res = await fetch(`./_locales/${locale}/messages.json`)
|
|
7
|
+
const data: Record<string, { message: string; description: string }> = await res.json()
|
|
8
|
+
return Object.keys(data).reduce((res, key) => {
|
|
9
|
+
return {
|
|
10
|
+
...res,
|
|
11
|
+
[key.replace(/__/g, ' ')]: data[key].message,
|
|
12
|
+
}
|
|
13
|
+
}, {})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
i18n
|
|
17
|
+
.use(initReactI18next) // passes i18n down to react-i18next
|
|
18
|
+
.init({
|
|
19
|
+
fallbackLng: 'en',
|
|
20
|
+
defaultNS: 'translations',
|
|
21
|
+
interpolation: {
|
|
22
|
+
escapeValue: false, // react already safes from xss
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export const I18N_NS = 'translations'
|
|
27
|
+
|
|
28
|
+
export const addResourceBundle = async (locale: string) => {
|
|
29
|
+
if (i18n.hasResourceBundle(locale, I18N_NS)) return
|
|
30
|
+
const bundle = await fetchLocale(locale)
|
|
31
|
+
|
|
32
|
+
i18n.addResourceBundle(locale, 'translations', bundle)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
addResourceBundle('en')
|
|
36
|
+
|
|
37
|
+
i18n.on('languageChanged', function (lng: string) {
|
|
38
|
+
addResourceBundle(lng)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export default i18n
|
package/src/slices/global.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { createSlice } from '@reduxjs/toolkit'
|
|
2
|
-
|
|
3
|
-
import { updateVersion } from '../global/actions'
|
|
4
|
-
|
|
5
|
-
export type TabOption = 'home' | 'discover' | 'settings'
|
|
6
|
-
|
|
7
|
-
export interface GlobalState {
|
|
8
|
-
tab: TabOption
|
|
9
|
-
isUnlocked: boolean
|
|
10
|
-
isReady: boolean
|
|
11
|
-
isBooted: boolean
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const initialState: GlobalState = {
|
|
15
|
-
tab: 'home',
|
|
16
|
-
isUnlocked: false,
|
|
17
|
-
isReady: false,
|
|
18
|
-
isBooted: false,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const slice = createSlice({
|
|
22
|
-
name: 'global',
|
|
23
|
-
initialState,
|
|
24
|
-
reducers: {
|
|
25
|
-
reset(state) {
|
|
26
|
-
return initialState
|
|
27
|
-
},
|
|
28
|
-
update(
|
|
29
|
-
state,
|
|
30
|
-
action: {
|
|
31
|
-
payload: {
|
|
32
|
-
tab?: TabOption
|
|
33
|
-
isUnlocked?: boolean
|
|
34
|
-
isReady?: boolean
|
|
35
|
-
isBooted?: boolean
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
) {
|
|
39
|
-
const { payload } = action
|
|
40
|
-
state = Object.assign({}, state, payload)
|
|
41
|
-
return state
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
extraReducers: builder => {
|
|
45
|
-
builder.addCase(updateVersion, state => {
|
|
46
|
-
// todo
|
|
47
|
-
})
|
|
48
|
-
},
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
export const globalActions = slice.actions
|
|
52
|
-
export default slice.reducer
|
package/src/slices/index.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// Redux slices exports
|
|
2
|
-
// Individual slice exports will be added as we migrate existing reducers
|
|
3
|
-
|
|
4
|
-
// Export slice creators that will be populated from existing code
|
|
5
|
-
export * from './accounts';
|
|
6
|
-
export * from './transactions';
|
|
7
|
-
export * from './settings';
|
|
8
|
-
export * from './global';
|
|
9
|
-
export * from './keyrings';
|
|
10
|
-
export * from './ui';
|