@unisat/wallet-state 1.0.5 → 1.1.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.
Files changed (99) hide show
  1. package/LICENSE +0 -5
  2. package/lib/index.d.mts +2012 -200
  3. package/lib/index.d.ts +2012 -200
  4. package/lib/index.js +6951 -570
  5. package/lib/index.js.map +1 -1
  6. package/lib/index.mjs +6754 -517
  7. package/lib/index.mjs.map +1 -1
  8. package/lib/types/index.d.mts +1 -1
  9. package/lib/types/index.d.ts +1 -1
  10. package/package.json +19 -15
  11. package/src/.DS_Store +0 -0
  12. package/src/context/ApprovalContext.tsx +27 -0
  13. package/src/context/DeviceContext.tsx +36 -0
  14. package/src/context/I18nContext.tsx +14 -172
  15. package/src/context/NavigationContext.tsx +305 -0
  16. package/src/context/PriceContext.tsx +2 -2
  17. package/src/context/StorageContext.tsx +393 -0
  18. package/src/context/ToolsContext.tsx +50 -0
  19. package/src/context/WalletContext.tsx +119 -108
  20. package/src/context/index.ts +17 -2
  21. package/src/hooks/accounts.ts +11 -5
  22. package/src/hooks/browser.ts +11 -0
  23. package/src/hooks/global.ts +170 -7
  24. package/src/hooks/index.ts +1 -2
  25. package/src/hooks/settings.ts +28 -37
  26. package/src/hooks/transactions.ts +28 -155
  27. package/src/hooks/ui.ts +232 -36
  28. package/src/index.ts +33 -24
  29. package/src/reducers/accounts.ts +19 -2
  30. package/src/reducers/browser.ts +223 -0
  31. package/src/reducers/global.ts +67 -1
  32. package/src/reducers/index.ts +1 -0
  33. package/src/reducers/transactions.ts +0 -9
  34. package/src/reducers/ui.ts +127 -8
  35. package/src/types/index.ts +1 -1
  36. package/src/ui-hooks/index.ts +107 -0
  37. package/src/ui-hooks/useActionOverviewSectionLogic.ts +150 -0
  38. package/src/ui-hooks/useAddressTypeScreenLogic.ts +160 -0
  39. package/src/ui-hooks/useAlkanesBalanceCardLogic.ts +41 -0
  40. package/src/ui-hooks/useAlkanesCollectionListLogic.ts +68 -0
  41. package/src/ui-hooks/useAlkanesListLogic.ts +69 -0
  42. package/src/ui-hooks/useAlkanesNFTListLogic.ts +42 -0
  43. package/src/ui-hooks/useAlkanesNFTScreenLogic.ts +45 -0
  44. package/src/ui-hooks/useAlkanesTokenScreenLogic.ts +130 -0
  45. package/src/ui-hooks/useAmountInputLogic.ts +80 -0
  46. package/src/ui-hooks/useAnnouncementCardLogic.ts +91 -0
  47. package/src/ui-hooks/useBRC20BalanceCardLogic.ts +115 -0
  48. package/src/ui-hooks/useBRC20InscribeTransferLogic.ts +398 -0
  49. package/src/ui-hooks/useBRC20ListLogic.ts +75 -0
  50. package/src/ui-hooks/useBRC20ProgListLogic.ts +77 -0
  51. package/src/ui-hooks/useBRC20SendScreenLogic.ts +411 -0
  52. package/src/ui-hooks/useBRC20SingleStepScreenLogic.ts +208 -0
  53. package/src/ui-hooks/useBRC20TokenScreenLogic.ts +469 -0
  54. package/src/ui-hooks/useBalanceCardLogic.ts +164 -0
  55. package/src/ui-hooks/useBtcDisplayLogic.ts +16 -0
  56. package/src/ui-hooks/useCAT20BalanceCardLogic.ts +35 -0
  57. package/src/ui-hooks/useCAT20ListLogic.ts +83 -0
  58. package/src/ui-hooks/useCAT20TokenScreenLogic.ts +122 -0
  59. package/src/ui-hooks/useCAT721ListLogic.ts +68 -0
  60. package/src/ui-hooks/useCAT721NFTScreenLogic.ts +37 -0
  61. package/src/ui-hooks/useCreatePasswordScreenLogic.ts +92 -0
  62. package/src/ui-hooks/useCreateWalletLogicImportWordsStep.ts +299 -0
  63. package/src/ui-hooks/useEditAccountNameScreenLogic.ts +71 -0
  64. package/src/ui-hooks/useEditContactScreenLogic.ts +162 -0
  65. package/src/ui-hooks/useEditWalletNameScreenLogic.ts +58 -0
  66. package/src/ui-hooks/useExportMnemonicsScreenLogic.ts +75 -0
  67. package/src/ui-hooks/useExportPrivateKeyScreenLogic.ts +64 -0
  68. package/src/ui-hooks/useFeeRateBarLogic.ts +303 -0
  69. package/src/ui-hooks/useInfiniteList.ts +85 -0
  70. package/src/ui-hooks/useInscriptionListLogic.ts +68 -0
  71. package/src/ui-hooks/useLockTimePageLogic.ts +43 -0
  72. package/src/ui-hooks/useNotificationsLogic.ts +115 -0
  73. package/src/ui-hooks/useOrdinalsInscriptionScreenLogic.ts +130 -0
  74. package/src/ui-hooks/useRunesBalanceCardLogic.ts +44 -0
  75. package/src/ui-hooks/useRunesListLogic.ts +74 -0
  76. package/src/ui-hooks/useRunesTokenScreenLogic.ts +149 -0
  77. package/src/ui-hooks/useSecurityCardLogic.ts +0 -0
  78. package/src/ui-hooks/useSendAlkanesNFTScreenLogic.ts +138 -0
  79. package/src/ui-hooks/useSendAlkanesScreenLogic.ts +192 -0
  80. package/src/ui-hooks/useSendCAT20ScreenLogic.ts +297 -0
  81. package/src/ui-hooks/useSendCAT721ScreenLogic.ts +205 -0
  82. package/src/ui-hooks/useSendOrdinalsInscriptionScreenLogic.ts +137 -0
  83. package/src/ui-hooks/useSendRunesScreenLogic.ts +172 -0
  84. package/src/ui-hooks/useSettingsTabScreenLogic.ts +211 -0
  85. package/src/ui-hooks/useSignMessageLogic.ts +302 -0
  86. package/src/ui-hooks/useSignPsbtLogic.ts +517 -0
  87. package/src/ui-hooks/useSplitOrdinalsInscriptionScreenLogic.ts +95 -0
  88. package/src/ui-hooks/useTxConfirmScreenLogic.ts +47 -0
  89. package/src/ui-hooks/useTxCreateScreenLogic.ts +161 -0
  90. package/src/ui-hooks/useTxFailScreenLogic.ts +26 -0
  91. package/src/ui-hooks/useTxSuccessScreenLogic.ts +33 -0
  92. package/src/updater/accounts.ts +11 -11
  93. package/src/utils/bitcoin-utils.ts +17 -8
  94. package/src/utils/eventBus.ts +2 -1
  95. package/src/utils/password-utils.ts +78 -0
  96. package/src/utils/ui-utils.ts +28 -0
  97. package/src/hooks/approval.ts +0 -72
  98. package/src/hooks/i18n.ts +0 -53
  99. package/src/utils/i18n.ts +0 -41
@@ -0,0 +1,517 @@
1
+ import { numUtils } from '@unisat/base-utils'
2
+ import { KeyringType, ToSignInput } from '@unisat/keyring-service/types'
3
+ import {
4
+ ApprovalSession,
5
+ ContractResult,
6
+ DecodedPsbt,
7
+ LocalPsbtSummary,
8
+ SignedData,
9
+ SignState,
10
+ TickPriceItem,
11
+ ToSignData,
12
+ WebsiteResult,
13
+ } from '@unisat/wallet-shared'
14
+ import logger from 'loglevel'
15
+ import { useEffect, useMemo, useRef, useState } from 'react'
16
+ import { useApproval, useI18n, useTools, useWallet } from 'src/context'
17
+ import { useCurrentAccount } from 'src/hooks'
18
+ import { useAsyncEffect } from 'src/utils/ui-utils'
19
+
20
+ interface InscriptionInfo {
21
+ id: string
22
+ isSent: boolean
23
+ }
24
+
25
+ export interface TxInfo {
26
+ changedBalance: number
27
+ changedInscriptions: InscriptionInfo[]
28
+ rawtx: string
29
+ psbtHex: string
30
+ toSignInputs: ToSignInput[]
31
+ txError: string
32
+ decodedPsbt: DecodedPsbt
33
+ contractResults: ContractResult[]
34
+ }
35
+
36
+ export const initTxInfo: TxInfo = {
37
+ changedBalance: 0,
38
+ changedInscriptions: [],
39
+ rawtx: '',
40
+ psbtHex: '',
41
+ toSignInputs: [],
42
+ txError: '',
43
+ decodedPsbt: {
44
+ inputInfos: [],
45
+ outputInfos: [],
46
+ fee: 0,
47
+ feeRate: 0,
48
+ risks: [],
49
+ features: {
50
+ rbf: false,
51
+ },
52
+ inscriptions: {},
53
+ isScammer: false,
54
+ shouldWarnFeeRate: false,
55
+ recommendedFeeRate: 1,
56
+ isCompleted: false,
57
+ },
58
+ contractResults: [],
59
+ }
60
+
61
+ export interface SignPsbtProps {
62
+ header?: React.ReactNode
63
+ params: {
64
+ data: {
65
+ toSignDatas: ToSignData[]
66
+ }
67
+ session?: ApprovalSession
68
+ }
69
+ handleCancel?: () => void
70
+ handleConfirm?: (signedDatas: SignedData[]) => void
71
+ fromApproval?: boolean
72
+ }
73
+
74
+ export function useSignPsbtLogic(props: SignPsbtProps) {
75
+ const {
76
+ params: {
77
+ data: { toSignDatas },
78
+ session,
79
+ },
80
+ handleCancel,
81
+ handleConfirm,
82
+ } = props
83
+ const { resolveApproval, rejectApproval } = useApproval()
84
+
85
+ const [loading, setLoading] = useState(false)
86
+ const [isPsbtSigning, setIsPsbtSigning] = useState(false)
87
+ const [isPsbtRiskPopoverVisible, setIsPsbtRiskPopoverVisible] = useState(false)
88
+ const [isKeystoneSigning, setIsKeystoneSigning] = useState(false)
89
+ const [isColdWalletSigning, setIsColdWalletSigning] = useState(false)
90
+
91
+ const [contractPopoverData, setContractPopoverData] = useState(undefined)
92
+
93
+ const wallet = useWallet()
94
+ const tools = useTools()
95
+ const currentAccount = useCurrentAccount()
96
+ const keyringType = currentAccount.type
97
+
98
+ const { t } = useI18n()
99
+
100
+ const [disclaimerVisible, setDisclaimerVisible] = useState(false)
101
+ const [localPsbtSummary, setLocalPsbtSummary] = useState<LocalPsbtSummary | null>(null)
102
+
103
+ const [signingTxIndex, setSigningTxIndex] = useState(toSignDatas.length > 1 ? -1 : 0)
104
+ const [signedStates, setSignedStates] = useState<SignState[]>(
105
+ toSignDatas.map(() => SignState.PENDING)
106
+ )
107
+
108
+ const [websiteResult, setWebsiteResult] = useState<WebsiteResult>({
109
+ isScammer: false,
110
+ warning: '',
111
+ allowQuickMultiSign: false,
112
+ })
113
+
114
+ const isMultipleViewMode = toSignDatas.length > 1
115
+
116
+ const [allowQuickMultiSign, setAllowQuickMultiSign] = useState(false)
117
+
118
+ //
119
+
120
+ useEffect(() => {
121
+ if (toSignDatas.length <= 1) {
122
+ return
123
+ }
124
+
125
+ if (
126
+ keyringType === KeyringType.KeystoneKeyring ||
127
+ keyringType === KeyringType.ColdWalletKeyring
128
+ ) {
129
+ return
130
+ }
131
+
132
+ setAllowQuickMultiSign(websiteResult.allowQuickMultiSign && toSignDatas.length > 1)
133
+ }, [websiteResult, keyringType])
134
+
135
+ useAsyncEffect(async () => {
136
+ const website = session?.origin
137
+ if (website) {
138
+ const result = await wallet.checkWebsite(website)
139
+ setWebsiteResult(result)
140
+
141
+ // Also check local user-granted trust
142
+ if (!result.allowQuickMultiSign && toSignDatas.length > 1) {
143
+ const sites = await wallet.getConnectedSites()
144
+ const site = sites.find(s => s.origin === website)
145
+ if (site?.allowQuickMultiSign) {
146
+ setAllowQuickMultiSign(true)
147
+ }
148
+ }
149
+ }
150
+ }, [])
151
+
152
+ const currentToSignData = toSignDatas[signingTxIndex]
153
+
154
+ const signedDatas = useRef<SignedData[]>([])
155
+
156
+ const decodedPsbtDatas = useRef<DecodedPsbt[]>([])
157
+ useAsyncEffect(async () => {
158
+ if (decodedPsbtDatas.current[signingTxIndex]) {
159
+ return
160
+ }
161
+ if (!currentToSignData) {
162
+ return
163
+ }
164
+ try {
165
+ setLoading(true)
166
+
167
+ const decodedPsbt = await wallet.decodePsbt(currentToSignData.psbtHex, session?.origin || '')
168
+ decodedPsbtDatas.current[signingTxIndex] = decodedPsbt
169
+ } catch (e) {
170
+ logger.error('Failed to decode PSBT data:', e)
171
+ } finally {
172
+ setLoading(false)
173
+ }
174
+ }, [signingTxIndex, currentToSignData])
175
+
176
+ const currentDecodedPsbt = decodedPsbtDatas.current[signingTxIndex]
177
+
178
+ const [brc20PriceMap, setBrc20PriceMap] = useState<Record<string, TickPriceItem>>()
179
+ const [runesPriceMap, setRunesPriceMap] = useState<Record<string, TickPriceItem>>()
180
+
181
+ useEffect(() => {
182
+ if (!currentDecodedPsbt?.inputInfos) return
183
+
184
+ const runesMap = {}
185
+ const brc20Map = {}
186
+
187
+ // collect asset information
188
+ currentDecodedPsbt.inputInfos.forEach(v => {
189
+ if (v.runes) {
190
+ v.runes.forEach(w => {
191
+ runesMap[w.spacedRune] = true
192
+ })
193
+ }
194
+
195
+ if (v.inscriptions) {
196
+ v.inscriptions.forEach(w => {
197
+ const inscription = currentDecodedPsbt.inscriptions[w.inscriptionId]
198
+ if (inscription && inscription.brc20) {
199
+ brc20Map[inscription.brc20.tick] = true
200
+ }
201
+ })
202
+ }
203
+ })
204
+
205
+ // get asset price
206
+ if (Object.keys(runesMap).length > 0) {
207
+ wallet
208
+ .getRunesPrice(Object.keys(runesMap))
209
+ .then(setRunesPriceMap)
210
+ .catch(e => tools.toastError(e.message))
211
+ }
212
+
213
+ if (Object.keys(brc20Map).length > 0) {
214
+ wallet
215
+ .getBrc20sPrice(Object.keys(brc20Map))
216
+ .then(setBrc20PriceMap)
217
+ .catch(e => tools.toastError(e.message))
218
+ }
219
+ }, [currentDecodedPsbt])
220
+
221
+ const networkFee = currentDecodedPsbt ? numUtils.satoshisToAmount(currentDecodedPsbt.fee) : '0'
222
+ const isValid = currentDecodedPsbt ? currentDecodedPsbt.inputInfos.length > 0 : false
223
+ const hasRisk = currentDecodedPsbt ? currentDecodedPsbt.risks.length > 0 : false
224
+
225
+ let showFeeSection = false
226
+ if (currentDecodedPsbt && currentDecodedPsbt.isCompleted) {
227
+ showFeeSection = true
228
+ }
229
+
230
+ let isScammer = false
231
+ if (currentDecodedPsbt && currentDecodedPsbt.isScammer == true) {
232
+ isScammer = true
233
+ }
234
+ if (websiteResult && websiteResult.isScammer == true) {
235
+ isScammer = true
236
+ }
237
+
238
+ const isAllSigned = signedStates.every(state => state === SignState.SUCCESS)
239
+ const signedCount = signedStates.filter(state => state === SignState.SUCCESS).length
240
+
241
+ const defaultHandleCancel = () => rejectApproval()
242
+ const actualHandleCancel = handleCancel || defaultHandleCancel
243
+
244
+ const multiSignList = useMemo(() => {
245
+ return toSignDatas.map((data, index) => {
246
+ const signState = signedStates[index]
247
+ let buttonText = 'View'
248
+ if (signState == SignState.PENDING) {
249
+ buttonText = t('view')
250
+ } else if (signState == SignState.SUCCESS) {
251
+ buttonText = t('signed')
252
+ } else if (signState == SignState.FAILED) {
253
+ buttonText = t('reject')
254
+ }
255
+
256
+ let buttonPreset = 'primary'
257
+ if (signState === SignState.SUCCESS) {
258
+ buttonPreset = 'approval'
259
+ } else if (signState === SignState.FAILED) {
260
+ buttonPreset = 'danger'
261
+ }
262
+
263
+ const psbtInfo = localPsbtSummary?.items?.[index]
264
+
265
+ const onClick = () => {
266
+ setSigningTxIndex(index)
267
+ }
268
+ return {
269
+ index,
270
+ buttonText,
271
+ buttonPreset,
272
+ psbtInfo,
273
+ onClick,
274
+ }
275
+ })
276
+ }, [toSignDatas, signedStates, localPsbtSummary, t])
277
+
278
+ // action
279
+ const onClickBack = () => {
280
+ if (isMultipleViewMode && signingTxIndex != -1) {
281
+ // back to multi sign view
282
+ setSigningTxIndex(-1)
283
+ setIsPsbtRiskPopoverVisible(false)
284
+ return
285
+ }
286
+ actualHandleCancel()
287
+ }
288
+
289
+ const onClickSign = () => {
290
+ if (currentDecodedPsbt?.risks?.length > 0) {
291
+ setIsPsbtRiskPopoverVisible(true)
292
+ return
293
+ }
294
+ onNextStep()
295
+ }
296
+
297
+ useAsyncEffect(async () => {
298
+ if (toSignDatas.length <= 1) return
299
+ try {
300
+ const summary = await wallet.analyzeLocalPsbts(toSignDatas)
301
+ setLocalPsbtSummary(summary)
302
+ } catch (e) {
303
+ logger.error('analyzeLocalPsbts failed:', e)
304
+ }
305
+ }, [])
306
+
307
+ const onTryMultiSign = async () => {
308
+ setDisclaimerVisible(true)
309
+ }
310
+
311
+ const onTrustSite = async () => {
312
+ const origin = session?.origin
313
+ if (!origin) return
314
+ await wallet.updateConnectSite(origin, { allowQuickMultiSign: true })
315
+ setAllowQuickMultiSign(true)
316
+ }
317
+
318
+ const onQuickMultiSign = async () => {
319
+ // Run analysis if not yet done (user clicked before async summary completed)
320
+
321
+ try {
322
+ setIsPsbtSigning(true)
323
+ // Yield to the JS event loop so the loading UI can render before heavy signing work begins
324
+ await new Promise<void>(resolve => setTimeout(resolve, 50))
325
+ const summary = localPsbtSummary ?? (await wallet.analyzeLocalPsbts(toSignDatas))
326
+ if (!localPsbtSummary) setLocalPsbtSummary(summary)
327
+
328
+ if (summary.parseErrorCount > 0) {
329
+ tools.toastError(t('failed_to_parse_psbt'))
330
+ return
331
+ }
332
+ if (summary.hasSighashNone) {
333
+ tools.toastError(t('sighash_none_detected'))
334
+ return
335
+ }
336
+ if (summary.hasAssets) {
337
+ tools.toastError(t('quick_sign_assets_detected'))
338
+ return
339
+ }
340
+
341
+ for (let i = 0; i < toSignDatas.length; i++) {
342
+ try {
343
+ const toSignData = toSignDatas[i]
344
+ const signedData = await wallet.signPsbtV2(toSignData)
345
+ onSignedData(signedData, i)
346
+ } catch (e) {
347
+ signedStates[i] = SignState.FAILED
348
+ setSignedStates([...signedStates])
349
+ tools.toastError(`Signing failed for PSBT ${i}: ${e.message}`)
350
+ }
351
+ }
352
+ } catch (e) {
353
+ } finally {
354
+ setIsPsbtSigning(false)
355
+ setDisclaimerVisible(false)
356
+ }
357
+ }
358
+
359
+ const localSign = async () => {
360
+ try {
361
+ const toSignData = toSignDatas[signingTxIndex]
362
+ const signedData = await wallet.signPsbtV2(toSignData)
363
+ onSignedData(signedData, signingTxIndex)
364
+ } catch (e) {
365
+ logger.error('Local signing failed:', e)
366
+ if (e.message.includes('Can not sign for input')) {
367
+ tools.toastError(t('sign_psbt_input_error'))
368
+ } else {
369
+ tools.toastError(e.message)
370
+ }
371
+ signedStates[signingTxIndex] = SignState.FAILED
372
+ setSignedStates([...signedStates])
373
+ }
374
+ }
375
+
376
+ const onNextStep = () => {
377
+ if (keyringType === KeyringType.KeystoneKeyring) {
378
+ setIsKeystoneSigning(true)
379
+ } else if (keyringType === KeyringType.ColdWalletKeyring) {
380
+ setIsColdWalletSigning(true)
381
+ } else {
382
+ localSign()
383
+ }
384
+ }
385
+
386
+ const onSignedData = (data: SignedData, signingTxIndex: number) => {
387
+ signedStates[signingTxIndex] = SignState.SUCCESS
388
+ setSignedStates([...signedStates])
389
+
390
+ signedDatas.current[signingTxIndex] = data
391
+
392
+ // single mode
393
+ if (!isMultipleViewMode) {
394
+ if (handleConfirm) {
395
+ handleConfirm(signedDatas.current)
396
+ } else {
397
+ resolveApproval(signedDatas.current)
398
+ }
399
+ return
400
+ }
401
+
402
+ // multiple mode
403
+ const isAllSigned = signedStates.every(state => state === SignState.SUCCESS)
404
+ if (isAllSigned) {
405
+ if (handleConfirm) {
406
+ handleConfirm(signedDatas.current)
407
+ } else {
408
+ resolveApproval(signedDatas.current)
409
+ }
410
+ return
411
+ } else {
412
+ setSigningTxIndex(-1)
413
+ setIsPsbtRiskPopoverVisible(false)
414
+ return
415
+ }
416
+ }
417
+
418
+ // keystone
419
+ const onKeystoneSigningSuccess = (data: SignedData) => {
420
+ setIsKeystoneSigning(false)
421
+ onSignedData(data, signingTxIndex)
422
+ }
423
+
424
+ const onKeystoneSigningBack = () => {
425
+ setIsKeystoneSigning(false)
426
+ }
427
+
428
+ // coldwallet
429
+ const onColdWalletSigningSuccess = (data: { psbtHex: string; rawtx: string }) => {
430
+ setIsColdWalletSigning(false)
431
+ onSignedData(data, signingTxIndex)
432
+ }
433
+
434
+ const onColdWalletSigningBack = () => {
435
+ setIsColdWalletSigning(false)
436
+ }
437
+
438
+ // risk popover
439
+ const onRiskPopoverConfirm = () => {
440
+ setIsPsbtRiskPopoverVisible(false)
441
+ onNextStep()
442
+ }
443
+
444
+ const onRiskPopoverClose = () => {
445
+ setIsPsbtRiskPopoverVisible(false)
446
+ }
447
+
448
+ // disclaimer modal
449
+ const onDisclaimerModalClose = () => {
450
+ setDisclaimerVisible(false)
451
+ }
452
+
453
+ const showMultiSignView = isMultipleViewMode && signingTxIndex == -1
454
+
455
+ const isWaitingData = showMultiSignView == false && (!currentDecodedPsbt || !currentToSignData)
456
+
457
+ const showLoading = isWaitingData || loading
458
+
459
+ const backButtonText = isMultipleViewMode ? t('back') : t('reject')
460
+
461
+ return {
462
+ isPsbtRiskPopoverVisible,
463
+ contractPopoverData,
464
+ setContractPopoverData,
465
+ t,
466
+ brc20PriceMap,
467
+ runesPriceMap,
468
+ session,
469
+ isPsbtSigning,
470
+
471
+ // page state
472
+ isKeystoneSigning,
473
+ isColdWalletSigning,
474
+ showMultiSignView,
475
+ showLoading,
476
+
477
+ disclaimerVisible,
478
+ localPsbtSummary,
479
+
480
+ // data
481
+ toSignDatas,
482
+ currentToSignData,
483
+ currentDecodedPsbt,
484
+
485
+ // state
486
+ networkFee,
487
+ isValid,
488
+ hasRisk,
489
+ showFeeSection,
490
+ isScammer,
491
+ allowQuickMultiSign,
492
+
493
+ // multiple sign state
494
+ isAllSigned,
495
+ signedCount,
496
+ multiSignList,
497
+
498
+ // actions
499
+ backButtonText,
500
+ onClickBack,
501
+ onClickSign,
502
+ onQuickMultiSign,
503
+ onTryMultiSign,
504
+ onTrustSite,
505
+
506
+ onKeystoneSigningSuccess,
507
+ onKeystoneSigningBack,
508
+
509
+ onColdWalletSigningSuccess,
510
+ onColdWalletSigningBack,
511
+
512
+ onRiskPopoverConfirm,
513
+ onRiskPopoverClose,
514
+
515
+ onDisclaimerModalClose,
516
+ }
517
+ }
@@ -0,0 +1,95 @@
1
+ import { Inscription } from '@unisat/wallet-shared'
2
+ import { useEffect, useState } from 'react'
3
+ import { useI18n, useNavigation, useWallet } from 'src/context'
4
+ import {
5
+ useCreateSplitTxCallback,
6
+ useCurrentAccount,
7
+ useFeeRateBar,
8
+ useOrdinalsTx,
9
+ } from 'src/hooks'
10
+ import { getAddressUtxoDust } from 'src/utils/bitcoin-utils'
11
+
12
+ export function useSplitOrdinalsInscriptionScreenLogic() {
13
+ const nav = useNavigation()
14
+ const props = nav.getRouteState<'SplitOrdinalsInscriptionScreen'>()
15
+
16
+ const inscription = props.inscription
17
+
18
+ const [disabled, setDisabled] = useState(true)
19
+
20
+ const ordinalsTx = useOrdinalsTx()
21
+
22
+ const [error, setError] = useState('')
23
+ const createSplitTx = useCreateSplitTxCallback()
24
+
25
+ const defaultOutputValue = props.inscription ? props.inscription.outputValue : 10000
26
+ const { t } = useI18n()
27
+ const account = useCurrentAccount()
28
+ const minOutputValue = getAddressUtxoDust(account.address)
29
+ const [outputValue, setOutputValue] = useState(defaultOutputValue)
30
+
31
+ const { feeRate } = useFeeRateBar()
32
+
33
+ const [inscriptions, setInscriptions] = useState<Inscription[]>([])
34
+
35
+ const [splitedCount, setSplitedCount] = useState(0)
36
+ const wallet = useWallet()
37
+ useEffect(() => {
38
+ wallet.getInscriptionUtxoDetail(props.inscription.inscriptionId).then(v => {
39
+ setInscriptions(v.inscriptions)
40
+ })
41
+ }, [])
42
+
43
+ useEffect(() => {
44
+ setDisabled(true)
45
+ setError('')
46
+ setSplitedCount(0)
47
+
48
+ if (feeRate <= 0) {
49
+ setError(t('invalid_fee_rate'))
50
+ return
51
+ }
52
+
53
+ if (!outputValue) {
54
+ return
55
+ }
56
+
57
+ if (outputValue < minOutputValue) {
58
+ setError(`${t('output_value_must_be_at_least')} ${minOutputValue}`)
59
+ return
60
+ }
61
+
62
+ setDisabled(false)
63
+ }, [feeRate, outputValue])
64
+
65
+ const onClickBack = () => {
66
+ nav.goBack()
67
+ }
68
+
69
+ const onOutputValueChange = (value: number) => {
70
+ setOutputValue(value)
71
+ }
72
+
73
+ const onClickNext = () => {
74
+ createSplitTx({ inscriptionId: inscription.inscriptionId, feeRate, outputValue })
75
+ .then(toSignData => {
76
+ nav.navigate('TxConfirmScreen', { toSignData })
77
+ })
78
+ .catch(e => {
79
+ console.log(e)
80
+ setError(e.message)
81
+ })
82
+ }
83
+
84
+ return {
85
+ t,
86
+ inscriptions,
87
+ minOutputValue,
88
+ splitedCount,
89
+ error,
90
+ disabled,
91
+ onOutputValueChange,
92
+ onClickBack,
93
+ onClickNext,
94
+ }
95
+ }
@@ -0,0 +1,47 @@
1
+ import { SignPsbtParams, SignPsbtResult } from '@unisat/wallet-shared'
2
+ import { useI18n, useNavigation } from 'src/context'
3
+ import { usePushBitcoinTxCallback } from 'src/hooks'
4
+
5
+ export function useTxConfirmScreenLogic() {
6
+ const { t } = useI18n()
7
+ const nav = useNavigation()
8
+ const { toSignData } = nav.getRouteState<'TxConfirmScreen'>()
9
+ const pushBitcoinTx = usePushBitcoinTxCallback()
10
+
11
+ const signPsbtParams: SignPsbtParams = {
12
+ data: {
13
+ toSignDatas: [toSignData],
14
+ },
15
+ }
16
+
17
+ const onClickBack = () => {
18
+ nav.goBack()
19
+ }
20
+
21
+ const onClickCancel = () => {
22
+ nav.goBack()
23
+ }
24
+
25
+ const onClickConfirm = async (signedDatas: SignPsbtResult) => {
26
+ try {
27
+ const { success, txid, error } = await pushBitcoinTx(signedDatas[0].psbtHex)
28
+ if (success) {
29
+ nav.navigate('TxSuccessScreen', { txid })
30
+ } else {
31
+ throw new Error(error)
32
+ }
33
+ } catch (e) {
34
+ nav.navigate('TxFailScreen', { error: (e as any).message })
35
+ }
36
+ }
37
+
38
+ return {
39
+ // data
40
+ signPsbtParams,
41
+
42
+ // actions
43
+ onClickBack,
44
+ onClickCancel,
45
+ onClickConfirm,
46
+ }
47
+ }