@@ -361,53 +361,86 @@ export class AssembledTransaction<T> {
361361 } ) ;
362362 }
363363
364- static fromJSON < T > (
365- options : Omit < AssembledTransactionOptions < T > , "args" > ,
366- {
367- tx,
368- simulationResult,
369- simulationTransactionData,
370- } : {
371- tx : XDR_BASE64 ;
372- simulationResult : {
373- auth : XDR_BASE64 [ ] ;
374- retval : XDR_BASE64 ;
375- } ;
376- simulationTransactionData : XDR_BASE64 ;
377- } ,
378- ) : AssembledTransaction < T > {
379- const txn = new AssembledTransaction ( options ) ;
380- txn . built = TransactionBuilder . fromXDR ( tx , options . networkPassphrase ) as Tx ;
381-
382- if ( txn . built . operations . length !== 1 ) {
364+ /**
365+ * Validate that a built transaction is a single invokeContract operation
366+ * targeting the expected contract, and return the parsed InvokeContractArgs.
367+ */
368+ private static validateInvokeContractOp (
369+ built : Tx ,
370+ expectedContractId : string ,
371+ ) : xdr . InvokeContractArgs {
372+ if ( built . operations . length !== 1 ) {
383373 throw new Error (
384374 "Transaction envelope must contain exactly one operation." ,
385375 ) ;
386376 }
387377
388- const operation = txn . built . operations [ 0 ] as Operation . InvokeHostFunction ;
378+ const operation = built . operations [ 0 ] as Operation . InvokeHostFunction ;
379+
380+ if ( ! operation ?. func ?. switch ) {
381+ throw new Error (
382+ "Transaction envelope does not contain an invokeHostFunction operation." ,
383+ ) ;
384+ }
389385
390- if ( ! operation ? .func ?. value || typeof operation . func . value !== "function " ) {
386+ if ( operation . func . switch ( ) . name !== "hostFunctionTypeInvokeContract " ) {
391387 throw new Error (
392- "Could not extract the method from the transaction envelope ." ,
388+ "Transaction envelope does not contain an invokeContract host function ." ,
393389 ) ;
394390 }
395391
396392 const invokeContractArgs = operation . func . value ( ) as xdr . InvokeContractArgs ;
397393
398- if ( ! invokeContractArgs ?. functionName ) {
394+ if (
395+ ! invokeContractArgs ?. contractAddress ||
396+ ! invokeContractArgs ?. functionName
397+ ) {
399398 throw new Error (
400- "Could not extract the method name from the transaction envelope." ,
399+ "Could not extract contract address or method name from the transaction envelope." ,
401400 ) ;
402401 }
403402
404403 const xdrContractId = Address . fromScAddress (
405404 invokeContractArgs . contractAddress ( ) ,
406405 ) . toString ( ) ;
407406
408- if ( xdrContractId !== options . contractId ) {
407+ if ( xdrContractId !== expectedContractId ) {
408+ throw new Error (
409+ `Transaction envelope targets contract ${ xdrContractId } , but this Client is configured for ${ expectedContractId } .` ,
410+ ) ;
411+ }
412+
413+ return invokeContractArgs ;
414+ }
415+
416+ static fromJSON < T > (
417+ options : Omit < AssembledTransactionOptions < T > , "args" > ,
418+ {
419+ tx,
420+ simulationResult,
421+ simulationTransactionData,
422+ } : {
423+ tx : XDR_BASE64 ;
424+ simulationResult : {
425+ auth : XDR_BASE64 [ ] ;
426+ retval : XDR_BASE64 ;
427+ } ;
428+ simulationTransactionData : XDR_BASE64 ;
429+ } ,
430+ ) : AssembledTransaction < T > {
431+ const txn = new AssembledTransaction ( options ) ;
432+ txn . built = TransactionBuilder . fromXDR ( tx , options . networkPassphrase ) as Tx ;
433+
434+ const invokeContractArgs = AssembledTransaction . validateInvokeContractOp (
435+ txn . built ,
436+ options . contractId ,
437+ ) ;
438+
439+ const xdrMethod = invokeContractArgs . functionName ( ) . toString ( "utf-8" ) ;
440+
441+ if ( xdrMethod !== options . method ) {
409442 throw new Error (
410- `Transaction envelope targets contract ${ xdrContractId } , but this Client is configured for ${ options . contractId } .` ,
443+ `Transaction envelope calls method ' ${ xdrMethod } ' , but the provided method is ' ${ options . method } ' .` ,
411444 ) ;
412445 }
413446
@@ -453,34 +486,10 @@ export class AssembledTransaction<T> {
453486 options . networkPassphrase ,
454487 ) as Tx ;
455488
456- if ( built . operations . length !== 1 ) {
457- throw new Error (
458- "Transaction envelope must contain exactly one operation." ,
459- ) ;
460- }
461-
462- const operation = built . operations [ 0 ] as Operation . InvokeHostFunction ;
463- if ( ! operation ?. func ?. value || typeof operation . func . value !== "function" ) {
464- throw new Error (
465- "Could not extract the method from the transaction envelope." ,
466- ) ;
467- }
468- const invokeContractArgs = operation . func . value ( ) as xdr . InvokeContractArgs ;
469- if ( ! invokeContractArgs ?. functionName ) {
470- throw new Error (
471- "Could not extract the method name from the transaction envelope." ,
472- ) ;
473- }
474-
475- const xdrContractId = Address . fromScAddress (
476- invokeContractArgs . contractAddress ( ) ,
477- ) . toString ( ) ;
478-
479- if ( xdrContractId !== options . contractId ) {
480- throw new Error (
481- `Transaction envelope targets contract ${ xdrContractId } , but this Client is configured for ${ options . contractId } .` ,
482- ) ;
483- }
489+ const invokeContractArgs = AssembledTransaction . validateInvokeContractOp (
490+ built ,
491+ options . contractId ,
492+ ) ;
484493
485494 const method = invokeContractArgs . functionName ( ) . toString ( "utf-8" ) ;
486495 const txn = new AssembledTransaction ( {
0 commit comments