1
1
import type { Account , AccountWithBalance } from '@fuel-wallet/types' ;
2
- import type { BN , TransactionRequest , TransactionSummary } from 'fuels' ;
2
+ import type { TransactionRequest , TransactionSummary } from 'fuels' ;
3
+ import { BN } from 'fuels' ;
3
4
import type { InterpreterFrom , StateFrom } from 'xstate' ;
4
5
import { assign , createMachine } from 'xstate' ;
5
6
import { AccountService } from '~/systems/Account' ;
@@ -32,11 +33,48 @@ type MachineContext = {
32
33
tip ?: BN ;
33
34
gasLimit ?: BN ;
34
35
skipCustomFee ?: boolean ;
36
+ isPrepareOnly ?: boolean ;
37
+ state ?: {
38
+ state ?: 'funded' | 'immutable' ;
39
+ } ;
40
+ providerCache ?: {
41
+ consensusParameterTimestamp ?: string ;
42
+ chain ?: {
43
+ name ?: string ;
44
+ consensusParameters ?: Record < string , unknown > ;
45
+ daHeight ?: string ;
46
+ [ key : string ] : unknown ;
47
+ } ;
48
+ chainInfo ?: {
49
+ name ?: string ;
50
+ consensusParameters ?: Record < string , unknown > ;
51
+ latestBlockHeight ?: string ;
52
+ [ key : string ] : unknown ;
53
+ } ;
54
+ nodeInfo ?: {
55
+ maxDepth ?: string ;
56
+ maxTx ?: string ;
57
+ nodeVersion ?: string ;
58
+ utxoValidation ?: boolean ;
59
+ vmBacktrace ?: boolean ;
60
+ [ key : string ] : unknown ;
61
+ } ;
62
+ } ;
63
+ transactionData ?: {
64
+ latestGasPrice ?: string ;
65
+ estimatedGasPrice ?: string ;
66
+ summary ?: {
67
+ operations ?: unknown [ ] ;
68
+ changes ?: unknown [ ] ;
69
+ [ key : string ] : unknown ;
70
+ } ;
71
+ } ;
35
72
} ;
36
73
response ?: {
37
74
txSummarySimulated ?: TransactionSummary ;
38
75
txSummaryExecuted ?: TransactionSummary ;
39
76
proposedTxRequest ?: TransactionRequest ;
77
+ preparedTransaction ?: TransactionRequest ;
40
78
} ;
41
79
fees : {
42
80
baseFee ?: BN ;
@@ -79,7 +117,14 @@ type MachineServices = {
79
117
} ;
80
118
81
119
type MachineEvents =
82
- | { type : 'START' ; input ?: TxInputs [ 'request' ] }
120
+ | {
121
+ type : 'START' ;
122
+ input ?: TxInputs [ 'request' ] & {
123
+ state ?: MachineContext [ 'input' ] [ 'state' ] ;
124
+ providerCache ?: MachineContext [ 'input' ] [ 'providerCache' ] ;
125
+ transactionData ?: MachineContext [ 'input' ] [ 'transactionData' ] ;
126
+ } ;
127
+ }
83
128
| { type : 'SET_CUSTOM_FEES' ; input : TxInputs [ 'setCustomFees' ] }
84
129
| { type : 'RESET' ; input ?: null }
85
130
| { type : 'APPROVE' ; input ?: null }
@@ -130,6 +175,8 @@ export const transactionRequestMachine = createMachine(
130
175
input : ( ctx : MachineContext ) => ( {
131
176
address : ctx . input . address ,
132
177
account : ctx . input . account ,
178
+ providerCache : ctx . input . providerCache ,
179
+ transactionData : ctx . input . transactionData ,
133
180
} ) ,
134
181
} ,
135
182
onDone : [
@@ -150,7 +197,12 @@ export const transactionRequestMachine = createMachine(
150
197
invoke : {
151
198
src : 'simulateTransaction' ,
152
199
data : {
153
- input : ( ctx : MachineContext ) => ctx . input ,
200
+ input : ( ctx : MachineContext ) => ( {
201
+ ...ctx . input ,
202
+ providerCache : ctx . input . providerCache ,
203
+ transactionData : ctx . input . transactionData ,
204
+ state : ctx . input . state ,
205
+ } ) ,
154
206
} ,
155
207
onDone : [
156
208
{
@@ -267,8 +319,24 @@ export const transactionRequestMachine = createMachine(
267
319
account,
268
320
address,
269
321
fees,
322
+ state,
323
+ providerCache,
324
+ transactionData,
270
325
} = ev . input || { } ;
271
326
327
+ // Log if we received rich data with the transaction
328
+ if ( state || providerCache || transactionData ) {
329
+ console . log (
330
+ '🎁 Rich data received with transaction:' ,
331
+ 'state' ,
332
+ state ,
333
+ 'providerCache' ,
334
+ providerCache ,
335
+ 'transactionData' ,
336
+ transactionData
337
+ ) ;
338
+ }
339
+
272
340
if ( ! providerUrl ) {
273
341
throw new Error ( 'providerUrl is required' ) ;
274
342
}
@@ -292,6 +360,9 @@ export const transactionRequestMachine = createMachine(
292
360
favIconUrl,
293
361
skipCustomFee,
294
362
fees,
363
+ state,
364
+ providerCache,
365
+ transactionData,
295
366
} ;
296
367
} ,
297
368
fees : ( _ctx , ev ) => {
@@ -316,10 +387,12 @@ export const transactionRequestMachine = createMachine(
316
387
} ,
317
388
} ) ,
318
389
assignApprovedTx : assign ( {
319
- response : ( ctx , ev ) => ( {
320
- ...ctx . response ,
321
- txSummaryExecuted : ev . data ,
322
- } ) ,
390
+ response : ( ctx , ev ) => {
391
+ return {
392
+ ...ctx . response ,
393
+ txSummaryExecuted : ev . data ,
394
+ } ;
395
+ } ,
323
396
} ) ,
324
397
assignSimulateResult : assign ( {
325
398
response : ( ctx , ev ) => ( {
@@ -356,7 +429,12 @@ export const transactionRequestMachine = createMachine(
356
429
} ,
357
430
services : {
358
431
prepareInputForSimulateTransaction : FetchMachine . create <
359
- { address ?: string ; account ?: AccountWithBalance } ,
432
+ {
433
+ address ?: string ;
434
+ account ?: AccountWithBalance ;
435
+ providerCache ?: MachineContext [ 'input' ] [ 'providerCache' ] ;
436
+ transactionData ?: MachineContext [ 'input' ] [ 'transactionData' ] ;
437
+ } ,
360
438
{
361
439
estimated : PrepareInputForSimulateTransactionReturn [ 'estimated' ] ;
362
440
account : AccountWithBalance ;
@@ -365,24 +443,68 @@ export const transactionRequestMachine = createMachine(
365
443
showError : false ,
366
444
maxAttempts : 1 ,
367
445
async fetch ( { input } ) {
368
- const [ estimated , acc ] = await Promise . all ( [
369
- TxService . estimateGasLimitAndDefaultTips ( ) ,
370
- input ?. account ||
371
- AccountService . fetchAccount ( {
372
- address : input ?. address as string ,
373
- } ) . then ( async ( _account ) => {
374
- const network = await NetworkService . getSelectedNetwork ( ) ;
375
- return await AccountService . fetchBalance ( {
376
- account : _account ,
377
- providerUrl : network ?. url as string ,
378
- } ) ;
379
- } ) ,
380
- ] ) ;
446
+ let acc = input ?. account ;
447
+ if ( ! acc ) {
448
+ acc = await AccountService . fetchAccount ( {
449
+ address : input ?. address as string ,
450
+ } ) . then ( async ( _account ) => {
451
+ const network = await NetworkService . getSelectedNetwork ( ) ;
452
+ return await AccountService . fetchBalance ( {
453
+ account : _account ,
454
+ providerUrl : network ?. url as string ,
455
+ } ) ;
456
+ } ) ;
457
+ }
458
+
459
+ if ( ! acc ) {
460
+ throw new Error ( 'Could not retrieve account information' ) ;
461
+ }
462
+
463
+ // Check if we have cached gas prices from the SDK - now directly from input
464
+ const hasEstimatedGasPrice =
465
+ ! ! input ?. transactionData ?. estimatedGasPrice ;
466
+ const hasLatestGasPrice = ! ! input ?. transactionData ?. latestGasPrice ;
467
+
468
+ if ( hasEstimatedGasPrice && hasLatestGasPrice ) {
469
+ try {
470
+ const estimatedGasPrice = input ?. transactionData
471
+ ?. estimatedGasPrice as string ;
472
+ const latestGasPrice = input ?. transactionData
473
+ ?. latestGasPrice as string ;
474
+
475
+ // We need maxGasLimit in any case
476
+ const { maxGasLimit } =
477
+ await TxService . estimateGasLimitAndDefaultTips ( ) ;
478
+
479
+ return {
480
+ estimated : {
481
+ // Using constructor to parse strings
482
+ regularTip : new BN ( latestGasPrice ) ,
483
+ fastTip : new BN ( estimatedGasPrice ) ,
484
+ maxGasLimit,
485
+ } ,
486
+ account : acc ,
487
+ } ;
488
+ } catch ( error ) {
489
+ console . warn (
490
+ 'Failed to use cached gas prices, falling back' ,
491
+ error
492
+ ) ;
493
+ // Fall through to regular estimation
494
+ }
495
+ }
496
+
497
+ // Standard implementation if no cached data or any errors
498
+ const estimated = await TxService . estimateGasLimitAndDefaultTips ( ) ;
381
499
return { estimated, account : acc } ;
382
500
} ,
383
501
} ) ,
384
502
simulateTransaction : FetchMachine . create <
385
- TxInputs [ 'simulateTransaction' ] ,
503
+ TxInputs [ 'simulateTransaction' ] & {
504
+ state ?: MachineContext [ 'input' ] [ 'state' ] ;
505
+ providerCache ?: MachineContext [ 'input' ] [ 'providerCache' ] ;
506
+ transactionData ?: MachineContext [ 'input' ] [ 'transactionData' ] ;
507
+ } ,
386
508
SimulateTransactionReturn
387
509
> ( {
388
510
showError : false ,
@@ -391,6 +513,68 @@ export const transactionRequestMachine = createMachine(
391
513
throw new Error ( 'Invalid simulateTransaction input' ) ;
392
514
}
393
515
516
+ console . log ( '🚀 Simulation input:' , input ) ;
517
+
518
+ // Now access rich data directly from input instead of accessing machine context
519
+ const hasTransactionData = ! ! input . transactionData ;
520
+ const hasState = ! ! input . state ;
521
+ const hasProviderCache = ! ! input . providerCache ;
522
+
523
+ // Log for debugging
524
+ if ( hasTransactionData || hasState || hasProviderCache ) {
525
+ console . log ( '🚀 Rich transaction data found in input:' , {
526
+ hasTransactionData,
527
+ hasState,
528
+ hasProviderCache,
529
+ } ) ;
530
+
531
+ // Check if we have cached provider info we can use
532
+ if (
533
+ hasProviderCache &&
534
+ ( input . providerCache ?. chain || input . providerCache ?. chainInfo )
535
+ ) {
536
+ // TODO: Use cached provider info
537
+ }
538
+
539
+ // Check if we can skip simulation because the transaction is already funded
540
+ const isFunded = input . state ?. state === 'funded' ;
541
+ const hasSummary = ! ! input . transactionData ?. summary ;
542
+
543
+ if ( isFunded && hasSummary ) {
544
+ try {
545
+ // Just create a transaction summary from the cached data
546
+ const summary = input . transactionData ?. summary || { } ;
547
+ const txSummary = {
548
+ id : summary . id ,
549
+ gasUsed : summary . gasUsed ,
550
+ fee : new BN ( summary . fee ? summary . fee . toString ( ) : '0' ) ,
551
+ receipts : summary . receipts || [ ] ,
552
+ } as TransactionSummary ;
553
+
554
+ // Return the mocked simulation result
555
+ return {
556
+ txSummary,
557
+ baseFee : input . transactionData ?. estimatedGasPrice
558
+ ? new BN ( input . transactionData . estimatedGasPrice )
559
+ : undefined ,
560
+ proposedTxRequest : input . transactionRequest ,
561
+ } ;
562
+ } catch ( error ) {
563
+ console . warn (
564
+ 'Failed to use cached transaction summary, falling back to simulation' ,
565
+ error
566
+ ) ;
567
+ // Fall through to regular simulation if anything goes wrong
568
+ }
569
+ }
570
+ } else {
571
+ console . log (
572
+ 'No rich data found in input, continuing with simulation'
573
+ ) ;
574
+ }
575
+
576
+ // Standard implementation if we couldn't use cached data
577
+ console . log ( 'Running standard transaction simulation...' ) ;
394
578
const simulatedInfo = await TxService . simulateTransaction ( input ) ;
395
579
return simulatedInfo ;
396
580
} ,
0 commit comments