@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.
@@ -0,0 +1,574 @@
1
+ import { useCallback, useMemo } from 'react'
2
+
3
+ import { RawTxInfo, ToAddressInfo } from '@/shared/types'
4
+ import { useTools } from '@/ui/components/ActionComponent'
5
+ import { useI18n } from '@/ui/hooks/useI18n'
6
+ import { useBTCUnit } from '@/ui/state/settings/hooks'
7
+ import { satoshisToBTC, sleep, useWallet } from '@/ui/utils'
8
+ import { UnspentOutput } from '@unisat/tx-helpers/types'
9
+
10
+ import { AppState } from '..'
11
+ import { useAccountAddress, useCurrentAccount } from '../accounts/hooks'
12
+ import { accountActions } from '../accounts/reducer'
13
+ import { useAppDispatch, useAppSelector } from '../hooks'
14
+ import { transactionsActions } from './reducer'
15
+
16
+ export function useTransactionsState(): AppState['transactions'] {
17
+ return useAppSelector(state => state.transactions)
18
+ }
19
+
20
+ export function useBitcoinTx() {
21
+ const transactionsState = useTransactionsState()
22
+ return transactionsState.bitcoinTx
23
+ }
24
+
25
+ export function usePrepareSendBTCCallback() {
26
+ const dispatch = useAppDispatch()
27
+ const wallet = useWallet()
28
+ const fromAddress = useAccountAddress()
29
+ const utxos = useUtxos()
30
+ const fetchUtxos = useFetchUtxosCallback()
31
+ const account = useCurrentAccount()
32
+ const btcUnit = useBTCUnit()
33
+ const { t } = useI18n()
34
+ return useCallback(
35
+ async ({
36
+ toAddressInfo,
37
+ toAmount,
38
+ feeRate,
39
+ enableRBF,
40
+ memo,
41
+ memos,
42
+ disableAutoAdjust,
43
+ }: {
44
+ toAddressInfo: ToAddressInfo
45
+ toAmount: number
46
+ feeRate?: number
47
+ enableRBF: boolean
48
+ memo?: string
49
+ memos?: string[]
50
+ disableAutoAdjust?: boolean
51
+ }) => {
52
+ let _utxos: UnspentOutput[] = utxos
53
+ if (_utxos.length === 0) {
54
+ _utxos = await fetchUtxos()
55
+ }
56
+ const safeBalance = _utxos
57
+ .filter(v => v.inscriptions.length == 0)
58
+ .reduce((pre, cur) => pre + cur.satoshis, 0)
59
+ if (safeBalance < toAmount) {
60
+ throw new Error(t('insufficient_balance'))
61
+ }
62
+
63
+ if (!feeRate) {
64
+ const summary = await wallet.getFeeSummary()
65
+ feeRate = summary.list[1].feeRate
66
+ }
67
+ let res: {
68
+ psbtHex: string
69
+ rawtx: string
70
+ fee: number
71
+ }
72
+
73
+ if (safeBalance === toAmount && !disableAutoAdjust) {
74
+ res = await wallet.sendAllBTC({
75
+ to: toAddressInfo.address,
76
+ btcUtxos: _utxos,
77
+ enableRBF,
78
+ feeRate,
79
+ })
80
+ } else {
81
+ res = await wallet.sendBTC({
82
+ to: toAddressInfo.address,
83
+ amount: toAmount,
84
+ btcUtxos: _utxos,
85
+ enableRBF,
86
+ feeRate,
87
+ memo,
88
+ memos,
89
+ })
90
+ }
91
+
92
+ dispatch(
93
+ transactionsActions.updateBitcoinTx({
94
+ rawtx: res.rawtx,
95
+ psbtHex: res.psbtHex,
96
+ fromAddress,
97
+ feeRate,
98
+ enableRBF,
99
+ })
100
+ )
101
+ const rawTxInfo: RawTxInfo = {
102
+ psbtHex: res.psbtHex,
103
+ rawtx: res.rawtx,
104
+ toAddressInfo,
105
+ fee: res.fee,
106
+ }
107
+ return rawTxInfo
108
+ },
109
+ [dispatch, wallet, fromAddress, utxos, fetchUtxos]
110
+ )
111
+ }
112
+
113
+ export function usePrepareSendBypassHeadOffsetsCallback() {
114
+ const dispatch = useAppDispatch()
115
+ const wallet = useWallet()
116
+ const fromAddress = useAccountAddress()
117
+ const account = useCurrentAccount()
118
+ const btcUnit = useBTCUnit()
119
+ return useCallback(
120
+ async ({
121
+ toAddressInfo,
122
+ toAmount,
123
+ feeRate,
124
+ }: {
125
+ toAddressInfo: ToAddressInfo
126
+ toAmount: number
127
+ feeRate: number
128
+ }) => {
129
+ const res = await wallet.sendCoinBypassHeadOffsets(
130
+ [
131
+ {
132
+ address: toAddressInfo.address,
133
+ satoshis: toAmount,
134
+ },
135
+ ],
136
+ feeRate
137
+ )
138
+
139
+ dispatch(
140
+ transactionsActions.updateBitcoinTx({
141
+ rawtx: res.rawtx,
142
+ psbtHex: res.psbtHex,
143
+ fromAddress,
144
+ feeRate,
145
+ })
146
+ )
147
+ const rawTxInfo: RawTxInfo = {
148
+ psbtHex: res.psbtHex,
149
+ rawtx: res.rawtx,
150
+ toAddressInfo,
151
+ fee: res.fee,
152
+ }
153
+ return rawTxInfo
154
+ },
155
+ [dispatch, wallet, fromAddress]
156
+ )
157
+ }
158
+
159
+ export function usePushBitcoinTxCallback() {
160
+ const dispatch = useAppDispatch()
161
+ const wallet = useWallet()
162
+ const tools = useTools()
163
+ return useCallback(
164
+ async (rawtx: string) => {
165
+ const ret = {
166
+ success: false,
167
+ txid: '',
168
+ error: '',
169
+ }
170
+ try {
171
+ tools.showLoading(true)
172
+ const txid = await wallet.pushTx(rawtx)
173
+ await sleep(3) // Wait for transaction synchronization
174
+ tools.showLoading(false)
175
+ dispatch(transactionsActions.updateBitcoinTx({ txid }))
176
+ dispatch(accountActions.expireBalance())
177
+ setTimeout(() => {
178
+ dispatch(accountActions.expireBalance())
179
+ }, 2000)
180
+ setTimeout(() => {
181
+ dispatch(accountActions.expireBalance())
182
+ }, 5000)
183
+
184
+ ret.success = true
185
+ ret.txid = txid
186
+ } catch (e) {
187
+ ret.error = (e as Error).message
188
+ tools.showLoading(false)
189
+ }
190
+
191
+ return ret
192
+ },
193
+ [dispatch, wallet]
194
+ )
195
+ }
196
+
197
+ export function useOrdinalsTx() {
198
+ const transactionsState = useTransactionsState()
199
+ return transactionsState.ordinalsTx
200
+ }
201
+
202
+ export function usePrepareSendOrdinalsInscriptionCallback() {
203
+ const dispatch = useAppDispatch()
204
+ const wallet = useWallet()
205
+ const fromAddress = useAccountAddress()
206
+ const utxos = useUtxos()
207
+ const fetchUtxos = useFetchUtxosCallback()
208
+ const account = useCurrentAccount()
209
+ return useCallback(
210
+ async ({
211
+ toAddressInfo,
212
+ inscriptionId,
213
+ feeRate,
214
+ outputValue,
215
+ enableRBF,
216
+ }: {
217
+ toAddressInfo: ToAddressInfo
218
+ inscriptionId: string
219
+ feeRate?: number
220
+ outputValue?: number
221
+ enableRBF: boolean
222
+ }) => {
223
+ if (!feeRate) {
224
+ const summary = await wallet.getFeeSummary()
225
+ feeRate = summary.list[1].feeRate
226
+ }
227
+
228
+ let btcUtxos = utxos
229
+ if (btcUtxos.length === 0) {
230
+ btcUtxos = await fetchUtxos()
231
+ }
232
+
233
+ const res = await wallet.sendOrdinalsInscription({
234
+ to: toAddressInfo.address,
235
+ inscriptionId,
236
+ feeRate,
237
+ outputValue,
238
+ enableRBF,
239
+ btcUtxos,
240
+ })
241
+ dispatch(
242
+ transactionsActions.updateOrdinalsTx({
243
+ rawtx: res.rawtx,
244
+ psbtHex: res.psbtHex,
245
+ fromAddress,
246
+ // inscription,
247
+ feeRate,
248
+ outputValue,
249
+ enableRBF,
250
+ })
251
+ )
252
+ const rawTxInfo: RawTxInfo = {
253
+ psbtHex: res.psbtHex,
254
+ rawtx: res.rawtx,
255
+ toAddressInfo,
256
+ }
257
+ return rawTxInfo
258
+ },
259
+ [dispatch, wallet, fromAddress, utxos]
260
+ )
261
+ }
262
+
263
+ export function usePrepareSendOrdinalsInscriptionsCallback() {
264
+ const dispatch = useAppDispatch()
265
+ const wallet = useWallet()
266
+ const fromAddress = useAccountAddress()
267
+ const fetchUtxos = useFetchUtxosCallback()
268
+ const utxos = useUtxos()
269
+ const account = useCurrentAccount()
270
+ return useCallback(
271
+ async ({
272
+ toAddressInfo,
273
+ inscriptionIds,
274
+ feeRate,
275
+ enableRBF,
276
+ }: {
277
+ toAddressInfo: ToAddressInfo
278
+ inscriptionIds: string[]
279
+ feeRate?: number
280
+ enableRBF: boolean
281
+ }) => {
282
+ if (!feeRate) {
283
+ const summary = await wallet.getFeeSummary()
284
+ feeRate = summary.list[1].feeRate
285
+ }
286
+
287
+ let btcUtxos = utxos
288
+ if (btcUtxos.length === 0) {
289
+ btcUtxos = await fetchUtxos()
290
+ }
291
+ const res = await wallet.sendOrdinalsInscriptions({
292
+ to: toAddressInfo.address,
293
+ inscriptionIds,
294
+ feeRate,
295
+ enableRBF,
296
+ btcUtxos,
297
+ })
298
+ dispatch(
299
+ transactionsActions.updateOrdinalsTx({
300
+ rawtx: res.rawtx,
301
+ psbtHex: res.psbtHex,
302
+ fromAddress,
303
+ feeRate,
304
+ enableRBF,
305
+ })
306
+ )
307
+ const rawTxInfo: RawTxInfo = {
308
+ psbtHex: res.psbtHex,
309
+ rawtx: res.rawtx,
310
+ toAddressInfo,
311
+ }
312
+ return rawTxInfo
313
+ },
314
+ [dispatch, wallet, fromAddress, utxos]
315
+ )
316
+ }
317
+
318
+ export function useCreateSplitTxCallback() {
319
+ const dispatch = useAppDispatch()
320
+ const wallet = useWallet()
321
+ const fromAddress = useAccountAddress()
322
+ const utxos = useUtxos()
323
+ const fetchUtxos = useFetchUtxosCallback()
324
+ const account = useCurrentAccount()
325
+ return useCallback(
326
+ async ({
327
+ inscriptionId,
328
+ feeRate,
329
+ outputValue,
330
+ enableRBF,
331
+ }: {
332
+ inscriptionId: string
333
+ feeRate: number
334
+ outputValue: number
335
+ enableRBF: boolean
336
+ }) => {
337
+ let btcUtxos = utxos
338
+ if (btcUtxos.length === 0) {
339
+ btcUtxos = await fetchUtxos()
340
+ }
341
+
342
+ const res = await wallet.splitOrdinalsInscription({
343
+ inscriptionId,
344
+ feeRate,
345
+ outputValue,
346
+ enableRBF,
347
+ btcUtxos,
348
+ })
349
+ dispatch(
350
+ transactionsActions.updateOrdinalsTx({
351
+ rawtx: res.rawtx,
352
+ psbtHex: res.psbtHex,
353
+ fromAddress,
354
+ // inscription,
355
+ enableRBF,
356
+ feeRate,
357
+ outputValue,
358
+ })
359
+ )
360
+ const rawTxInfo: RawTxInfo = {
361
+ psbtHex: res.psbtHex,
362
+ rawtx: res.rawtx,
363
+ toAddressInfo: {
364
+ address: fromAddress,
365
+ },
366
+ }
367
+ return { rawTxInfo, splitedCount: res.splitedCount }
368
+ },
369
+ [dispatch, wallet, fromAddress, utxos]
370
+ )
371
+ }
372
+
373
+ export function usePushOrdinalsTxCallback() {
374
+ const dispatch = useAppDispatch()
375
+ const wallet = useWallet()
376
+ const tools = useTools()
377
+ return useCallback(
378
+ async (rawtx: string) => {
379
+ const ret = {
380
+ success: false,
381
+ txid: '',
382
+ error: '',
383
+ }
384
+ try {
385
+ tools.showLoading(true)
386
+ const txid = await wallet.pushTx(rawtx)
387
+ await sleep(3) // Wait for transaction synchronization
388
+ tools.showLoading(false)
389
+ dispatch(transactionsActions.updateOrdinalsTx({ txid }))
390
+
391
+ dispatch(accountActions.expireBalance())
392
+ setTimeout(() => {
393
+ dispatch(accountActions.expireBalance())
394
+ }, 2000)
395
+ setTimeout(() => {
396
+ dispatch(accountActions.expireBalance())
397
+ }, 5000)
398
+
399
+ ret.success = true
400
+ ret.txid = txid
401
+ } catch (e) {
402
+ console.log(e)
403
+ ret.error = (e as Error).message
404
+ tools.showLoading(false)
405
+ }
406
+
407
+ return ret
408
+ },
409
+ [dispatch, wallet]
410
+ )
411
+ }
412
+
413
+ export function useUtxos() {
414
+ const transactionsState = useTransactionsState()
415
+ return transactionsState.utxos
416
+ }
417
+
418
+ export function useFetchUtxosCallback() {
419
+ const dispatch = useAppDispatch()
420
+ const wallet = useWallet()
421
+ const account = useCurrentAccount()
422
+ return useCallback(async () => {
423
+ const data = await wallet.getBTCUtxos()
424
+ dispatch(transactionsActions.setUtxos(data))
425
+ return data
426
+ }, [wallet, account])
427
+ }
428
+
429
+ export function useSpendUnavailableUtxos() {
430
+ const transactionsState = useTransactionsState()
431
+ return transactionsState.spendUnavailableUtxos
432
+ }
433
+
434
+ export function useSetSpendUnavailableUtxosCallback() {
435
+ const dispatch = useAppDispatch()
436
+ return useCallback(
437
+ (utxos: UnspentOutput[]) => {
438
+ dispatch(transactionsActions.setSpendUnavailableUtxos(utxos))
439
+ },
440
+ [dispatch]
441
+ )
442
+ }
443
+
444
+ export function useSafeBalance() {
445
+ const utxos = useUtxos()
446
+ return useMemo(() => {
447
+ const satoshis = utxos
448
+ .filter(v => v.inscriptions.length === 0)
449
+ .reduce((pre, cur) => pre + cur.satoshis, 0)
450
+ return satoshisToBTC(satoshis)
451
+ }, [utxos])
452
+ }
453
+
454
+ export function useAssetUtxosRunes() {
455
+ const transactionsState = useTransactionsState()
456
+ return transactionsState.assetUtxos_runes
457
+ }
458
+
459
+ export function useFetchAssetUtxosRunesCallback() {
460
+ const dispatch = useAppDispatch()
461
+ const wallet = useWallet()
462
+ const account = useCurrentAccount()
463
+ return useCallback(
464
+ async (rune: string) => {
465
+ const data = await wallet.getAssetUtxosRunes(rune)
466
+ dispatch(transactionsActions.setAssetUtxosRunes(data))
467
+ return data
468
+ },
469
+ [wallet, account]
470
+ )
471
+ }
472
+
473
+ export function usePrepareSendRunesCallback() {
474
+ const dispatch = useAppDispatch()
475
+ const wallet = useWallet()
476
+ const fromAddress = useAccountAddress()
477
+ const utxos = useUtxos()
478
+ const fetchUtxos = useFetchUtxosCallback()
479
+ const assetUtxosRunes = useAssetUtxosRunes()
480
+ const fetchAssetUtxosRunes = useFetchAssetUtxosRunesCallback()
481
+ const account = useCurrentAccount()
482
+ return useCallback(
483
+ async ({
484
+ toAddressInfo,
485
+ runeid,
486
+ runeAmount,
487
+ outputValue,
488
+ feeRate,
489
+ enableRBF,
490
+ }: {
491
+ toAddressInfo: ToAddressInfo
492
+ runeid: string
493
+ runeAmount: string
494
+ outputValue?: number
495
+ feeRate: number
496
+ enableRBF: boolean
497
+ }) => {
498
+ if (!feeRate) {
499
+ const summary = await wallet.getFeeSummary()
500
+ feeRate = summary.list[1].feeRate
501
+ }
502
+
503
+ let btcUtxos = utxos
504
+ if (btcUtxos.length === 0) {
505
+ btcUtxos = await fetchUtxos()
506
+ }
507
+
508
+ let assetUtxos = assetUtxosRunes
509
+ if (assetUtxos.length == 0) {
510
+ assetUtxos = await fetchAssetUtxosRunes(runeid)
511
+ }
512
+
513
+ const res = await wallet.sendRunes({
514
+ to: toAddressInfo.address,
515
+ runeid,
516
+ runeAmount,
517
+ outputValue,
518
+ feeRate,
519
+ enableRBF,
520
+ btcUtxos,
521
+ assetUtxos,
522
+ })
523
+
524
+ dispatch(
525
+ transactionsActions.updateRunesTx({
526
+ rawtx: res.rawtx,
527
+ psbtHex: res.psbtHex,
528
+ fromAddress,
529
+ feeRate,
530
+ enableRBF,
531
+ runeid,
532
+ runeAmount,
533
+ outputValue,
534
+ })
535
+ )
536
+ const rawTxInfo: RawTxInfo = {
537
+ psbtHex: res.psbtHex,
538
+ rawtx: res.rawtx,
539
+ toAddressInfo,
540
+ }
541
+ return rawTxInfo
542
+ },
543
+ [dispatch, wallet, fromAddress, utxos, assetUtxosRunes, fetchAssetUtxosRunes, account]
544
+ )
545
+ }
546
+
547
+ export function useRunesTx() {
548
+ const transactionsState = useTransactionsState()
549
+ return transactionsState.runesTx
550
+ }
551
+
552
+ export function usePrepareSendAlkanesCallback() {
553
+ const wallet = useWallet()
554
+ const account = useCurrentAccount()
555
+ const callback = useCallback(
556
+ async (
557
+ toAddressInfo: ToAddressInfo,
558
+ alkaneid: string,
559
+ amount: string,
560
+ feeRate: number,
561
+ enableRBF = false
562
+ ) => {
563
+ return await wallet.sendAlkanes({
564
+ to: toAddressInfo.address,
565
+ alkaneid,
566
+ amount,
567
+ feeRate,
568
+ enableRBF,
569
+ })
570
+ },
571
+ [wallet, account]
572
+ )
573
+ return callback
574
+ }
@@ -0,0 +1,7 @@
1
+ import { useWalletSelector } from './index';
2
+
3
+ export const useUIState = () => {
4
+ return useWalletSelector((state) => state.ui);
5
+ };
6
+
7
+ // Additional UI hooks will be migrated here
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ // State management exports
2
+ export * from './store';
3
+ export * from './slices';
4
+ export * from './hooks';
5
+ export * from './types';