From 4e3c9339b391441ee7c03299742325b74d4cc5e7 Mon Sep 17 00:00:00 2001 From: Negar Date: Thu, 21 Sep 2023 19:29:21 +1000 Subject: [PATCH] fix: adding simulation on failed transactions (#133) After a failed transaction, using simulate to extract more information about what went wrong on debug mode. --- docs/capabilities/app-client.md | 4 +- docs/code/modules/index.md | 54 ++- src/__snapshots__/app-deploy.spec.ts.snap | 6 +- src/transaction.spec.ts | 31 ++ src/transaction.ts | 49 ++- .../__snapshots__/app-client.spec.ts.snap | 359 +++++++++++++++++- src/types/app-client.spec.ts | 66 +--- 7 files changed, 478 insertions(+), 91 deletions(-) diff --git a/docs/capabilities/app-client.md b/docs/capabilities/app-client.md index 1a77e82e..06b90153 100644 --- a/docs/capabilities/app-client.md +++ b/docs/capabilities/app-client.md @@ -165,7 +165,9 @@ If you want to go a step further and automatically issue a [dry run transaction] algokit.Config.configure({ debug: true }) ``` -If you do that then the exception will have the `traces` property within the underlying exception will have key information from the dry run within it and this will get populated into the `led.traces` property of the thrown error. +> ⚠️ **Note:** The "dry run" feature has been deprecated and is now replaced by the "simulation" feature. Please refer to the [Simulation Documentation](https://algorand.github.io/js-algorand-sdk/classes/modelsv2.SimulateTransactionResult.html) for more details. + +If you do that then the exception will have the `traces` property within the underlying exception will have key information from the simulation within it and this will get populated into the `led.traces` property of the thrown error. ## `AppClientCallParams` diff --git a/docs/code/modules/index.md b/docs/code/modules/index.md index 2ad4d427..75620da3 100644 --- a/docs/code/modules/index.md +++ b/docs/code/modules/index.md @@ -73,6 +73,7 @@ - [mnemonicAccountFromEnvironment](index.md#mnemonicaccountfromenvironment) - [multisigAccount](index.md#multisigaccount) - [performAtomicTransactionComposerDryrun](index.md#performatomictransactioncomposerdryrun) +- [performAtomicTransactionComposerSimulate](index.md#performatomictransactioncomposersimulate) - [performTemplateSubstitution](index.md#performtemplatesubstitution) - [performTemplateSubstitutionAndCompile](index.md#performtemplatesubstitutionandcompile) - [randomAccount](index.md#randomaccount) @@ -172,7 +173,7 @@ the estimated rate. #### Defined in -[src/transaction.ts:397](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L397) +[src/transaction.ts:426](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L426) ___ @@ -228,7 +229,7 @@ Allows for control of fees on a `Transaction` or `SuggestedParams` object #### Defined in -[src/transaction.ts:420](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L420) +[src/transaction.ts:449](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L449) ___ @@ -343,7 +344,7 @@ the transaction note ready for inclusion in a transaction #### Defined in -[src/transaction.ts:38](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L38) +[src/transaction.ts:39](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L39) ___ @@ -1270,7 +1271,7 @@ The array of transactions with signers #### Defined in -[src/transaction.ts:452](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L452) +[src/transaction.ts:481](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L481) ___ @@ -1519,7 +1520,7 @@ The public address #### Defined in -[src/transaction.ts:59](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L59) +[src/transaction.ts:60](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L60) ___ @@ -1544,7 +1545,7 @@ A transaction signer #### Defined in -[src/transaction.ts:69](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L69) +[src/transaction.ts:70](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L70) ___ @@ -1569,7 +1570,7 @@ The suggested transaction parameters #### Defined in -[src/transaction.ts:443](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L443) +[src/transaction.ts:472](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L472) ___ @@ -1595,7 +1596,7 @@ A TransactionWithSigner object. #### Defined in -[src/transaction.ts:82](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L82) +[src/transaction.ts:83](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L83) ___ @@ -1906,7 +1907,32 @@ The dryrun result #### Defined in -[src/transaction.ts:277](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L277) +[src/transaction.ts:278](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L278) + +___ + +### performAtomicTransactionComposerSimulate + +▸ **performAtomicTransactionComposerSimulate**(`atc`, `algod`): `Promise`<`SimulateResponse`\> + +Performs a simulation of the transactions loaded into the given AtomicTransactionComposer. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `atc` | `AtomicTransactionComposer` | The AtomicTransactionComposer with transaction(s) loaded. | +| `algod` | `default` | An Algod client to perform the simulation. | + +#### Returns + +`Promise`<`SimulateResponse`\> + +The simulation result, which includes various details about how the transactions would be processed. + +#### Defined in + +[src/transaction.ts:293](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L293) ___ @@ -2089,7 +2115,7 @@ An object with transaction IDs, transactions, group transaction ID (`groupTransa #### Defined in -[src/transaction.ts:189](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L189) +[src/transaction.ts:190](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L190) ___ @@ -2114,7 +2140,7 @@ An object with transaction IDs, transactions, group transaction ID (`groupTransa #### Defined in -[src/transaction.ts:295](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L295) +[src/transaction.ts:324](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L324) ___ @@ -2142,7 +2168,7 @@ An object with transaction (`transaction`) and (if `skipWaiting` is `false` or ` #### Defined in -[src/transaction.ts:145](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L145) +[src/transaction.ts:146](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L146) ___ @@ -2167,7 +2193,7 @@ The signed transaction as a `Uint8Array` #### Defined in -[src/transaction.ts:125](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L125) +[src/transaction.ts:126](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L126) ___ @@ -2319,4 +2345,4 @@ Throws an error if the transaction is not confirmed or rejected in the next `tim #### Defined in -[src/transaction.ts:340](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L340) +[src/transaction.ts:369](https://github.com/algorandfoundation/algokit-utils-ts/blob/main/src/transaction.ts#L369) diff --git a/src/__snapshots__/app-deploy.spec.ts.snap b/src/__snapshots__/app-deploy.spec.ts.snap index f8a7ccab..bac24302 100644 --- a/src/__snapshots__/app-deploy.spec.ts.snap +++ b/src/__snapshots__/app-deploy.spec.ts.snap @@ -24,7 +24,8 @@ INFO: Existing app test found by creator ACCOUNT_1, with app id APP_1 and versio INFO: Detected a TEAL update in app APP_1 for creator ACCOUNT_1 WARN: App is not deletable and onUpdate=ReplaceApp, will attempt to create new app and delete old app, delete will most likely fail INFO: Deploying a new test app for ACCOUNT_1; deploying app with version 2.0. -WARN: Deleting existing test app with id APP_1 from ACCOUNT_1 account." +WARN: Deleting existing test app with id APP_1 from ACCOUNT_1 account. +INFO: Received error executing Atomic Transaction Composer, for more information enable the debug flag" `; exports[`deploy-app Deploy failure for replacement of schema broken app fails if onSchemaBreak = Fail 1`] = ` @@ -72,7 +73,8 @@ INFO: Existing app test found by creator ACCOUNT_1, with app id APP_1 and versio WARN: Detected a breaking app schema change in app APP_1: | [{"from":{"global":{"numUint":3,"numByteSlice":2,"attribute_map":{"numUint":"num-uint","numByteSlice":"num-byte-slice"}},"local":{"numUint":2,"numByteSlice":2,"attribute_map":{"numUint":"num-uint","numByteSlice":"num-byte-slice"}}},"to":{"global":{"numUint":3,"numByteSlice":3,"attribute_map":{"numUint":"num-uint","numByteSlice":"num-byte-slice"}},"local":{"numUint":2,"numByteSlice":2,"attribute_map":{"numUint":"num-uint","numByteSlice":"num-byte-slice"}}}}] INFO: App is not deletable but onSchemaBreak=ReplaceApp, will attempt to delete app, delete will most likely fail INFO: Deploying a new test app for ACCOUNT_1; deploying app with version 2.0. -WARN: Deleting existing test app with id APP_1 from ACCOUNT_1 account." +WARN: Deleting existing test app with id APP_1 from ACCOUNT_1 account. +INFO: Received error executing Atomic Transaction Composer, for more information enable the debug flag" `; exports[`deploy-app Deploy update to immutable updated app fails 1`] = ` diff --git a/src/transaction.spec.ts b/src/transaction.spec.ts index 320bebb5..a73eb721 100644 --- a/src/transaction.spec.ts +++ b/src/transaction.spec.ts @@ -291,6 +291,37 @@ describe('transaction', () => { expect((e as Error).message).toEqual(`Transaction ${txn.txID()} not confirmed after 5 rounds`) } }) + + test('Transaction fails in debug mode, error is enriched using simulate', async () => { + const { algod, testAccount } = localnet.context + const txn1 = await getTestTransaction(1) + const txn2 = await getTestTransaction(9999999999999) // This will fail due to fee being too high + + try { + await algokit.sendGroupOfTransactions( + { + transactions: [ + { + transaction: txn1, + signer: testAccount, + }, + { + transaction: txn2, + signer: testAccount, + }, + ], + }, + algod, + ) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + expect(e.traces[0].message).toEqual( + `transaction ${txn2.txID()}: overspend (account ${ + testAccount.addr + }, data {AccountBaseData:{Status:Offline MicroAlgos:{Raw:9998000} RewardsBase:0 RewardedMicroAlgos:{Raw:0} AuthAddr:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ TotalAppSchema:{_struct:{} NumUint:0 NumByteSlice:0} TotalExtraAppPages:0 TotalAppParams:0 TotalAppLocalStates:0 TotalAssetParams:0 TotalAssets:0 TotalBoxes:0 TotalBoxBytes:0} VotingData:{VoteID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] SelectionID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] StateProofID:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] VoteFirstValid:0 VoteLastValid:0 VoteKeyDilution:0}}, tried to spend {9999999999999})`, + ) + } + }) }) describe('transaction node encoder', () => { diff --git a/src/transaction.ts b/src/transaction.ts index 3c60e332..8e847e4f 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -1,6 +1,7 @@ import algosdk, { Algodv2, AtomicTransactionComposer, + EncodedSignedTransaction, modelsv2, SuggestedParams, Transaction, @@ -245,25 +246,25 @@ export const sendAtomicTransactionComposer = async function (atcSend: AtomicTran } as SendAtomicTransactionComposerResults // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { + Config.logger.info('Received error executing Atomic Transaction Composer, for more information enable the debug flag') if (Config.debug && typeof e === 'object') { e.traces = [] Config.logger.debug( - 'Received error executing Atomic Transaction Composer and debug flag enabled; attempting dry run to get more information', + 'Received error executing Atomic Transaction Composer and debug flag enabled; attempting simulation to get more information', ) - const dryrun = await performAtomicTransactionComposerDryrun(atc, algod) - - for (const txn of dryrun.txns) { - if (txn.appCallRejected()) { + const simulate = await performAtomicTransactionComposerSimulate(atc, algod) + if (simulate.txnGroups[0].failedAt) { + for (const txn of simulate.txnGroups[0].txnResults) { e.traces.push({ - trace: txn.appTrace(), - cost: txn.cost, - logs: txn.logs, - messages: txn.appCallMessages, + trace: txn.execTrace?.get_obj_for_encoding(), + appBudget: txn.appBudgetConsumed, + logicSigBudget: txn.logicSigBudgetConsumed, + logs: txn.txnResult.logs, + message: simulate.txnGroups[0].failureMessage, }) } } } - throw e } } @@ -283,6 +284,34 @@ export async function performAtomicTransactionComposerDryrun(atc: AtomicTransact return new algosdk.DryrunResult(await algod.dryrun(dryrun).do()) } +/** + * Performs a simulation of the transactions loaded into the given AtomicTransactionComposer. + * @param atc The AtomicTransactionComposer with transaction(s) loaded. + * @param algod An Algod client to perform the simulation. + * @returns The simulation result, which includes various details about how the transactions would be processed. + */ +export async function performAtomicTransactionComposerSimulate(atc: AtomicTransactionComposer, algod: Algodv2) { + const unsignedTransactionsSigners = atc.buildGroup() + const decodedSignedTransactions = unsignedTransactionsSigners.map((ts) => algosdk.encodeUnsignedSimulateTransaction(ts.txn)) + + const simulateRequest = new modelsv2.SimulateRequest({ + allowEmptySignatures: true, + allowMoreLogging: true, + execTraceConfig: new modelsv2.SimulateTraceConfig({ + enable: true, + scratchChange: true, + stackChange: true, + }), + txnGroups: [ + new modelsv2.SimulateRequestTransactionGroup({ + txns: decodedSignedTransactions.map((txn) => algosdk.decodeObj(txn)) as EncodedSignedTransaction[], + }), + ], + }) + const simulateResult = await algod.simulateTransactions(simulateRequest).do() + return simulateResult +} + /** * Signs and sends a group of [up to 16](https://developer.algorand.org/docs/get-details/atomic_transfers/#create-transactions) transactions to the chain * diff --git a/src/types/__snapshots__/app-client.spec.ts.snap b/src/types/__snapshots__/app-client.spec.ts.snap index 62c2795a..0065767a 100644 --- a/src/types/__snapshots__/app-client.spec.ts.snap +++ b/src/types/__snapshots__/app-client.spec.ts.snap @@ -1,9 +1,366 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`application-client Errors Display nice error messages when there is a logic error 3`] = ` +"{ + "trace": { + "approval-program-trace": [ + { + "pc": 1 + }, + { + "pc": 9 + }, + { + "pc": 17, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ] + }, + { + "pc": 19, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 20, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 21, + "stack-pop-count": 1 + }, + { + "pc": 24, + "stack-additions": [ + { + "type": 1, + "bytes": "RNDaDQ==" + } + ] + }, + { + "pc": 27, + "stack-additions": [ + { + "type": 1, + "bytes": "8X6ApQ==" + } + ] + }, + { + "pc": 33, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 34, + "stack-pop-count": 1 + }, + { + "pc": 37, + "stack-additions": [ + { + "type": 1, + "bytes": "RNDaDQ==" + } + ] + }, + { + "pc": 40, + "stack-additions": [ + { + "type": 1, + "bytes": "CpKoHg==" + } + ] + }, + { + "pc": 46, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 47, + "stack-pop-count": 1 + }, + { + "pc": 50, + "stack-additions": [ + { + "type": 1, + "bytes": "RNDaDQ==" + } + ] + }, + { + "pc": 53, + "stack-additions": [ + { + "type": 1, + "bytes": "rXVgLA==" + } + ] + }, + { + "pc": 59, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 60, + "stack-pop-count": 1 + }, + { + "pc": 63, + "stack-additions": [ + { + "type": 1, + "bytes": "RNDaDQ==" + } + ] + }, + { + "pc": 66, + "stack-additions": [ + { + "type": 1, + "bytes": "pM+N6g==" + } + ] + }, + { + "pc": 72, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 73, + "stack-pop-count": 1 + }, + { + "pc": 76, + "stack-additions": [ + { + "type": 1, + "bytes": "RNDaDQ==" + } + ] + }, + { + "pc": 79, + "stack-additions": [ + { + "type": 1, + "bytes": "zsKDSg==" + } + ] + }, + { + "pc": 85, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 86, + "stack-pop-count": 1 + }, + { + "pc": 89, + "stack-additions": [ + { + "type": 1, + "bytes": "RNDaDQ==" + } + ] + }, + { + "pc": 92, + "stack-additions": [ + { + "type": 1, + "bytes": "pLSiMA==" + } + ] + }, + { + "pc": 98, + "stack-additions": [ + { + "type": 2 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 99, + "stack-pop-count": 1 + }, + { + "pc": 102, + "stack-additions": [ + { + "type": 1, + "bytes": "RNDaDQ==" + } + ] + }, + { + "pc": 105, + "stack-additions": [ + { + "type": 1, + "bytes": "RNDaDQ==" + } + ] + }, + { + "pc": 111, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 112, + "stack-pop-count": 1 + }, + { + "pc": 341, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 343, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 344, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 345, + "stack-additions": [ + { + "type": 2, + "uint": "APP_ID" + } + ] + }, + { + "pc": 347, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 348, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 349, + "stack-additions": [ + { + "type": 2, + "uint": 1 + } + ], + "stack-pop-count": 2 + }, + { + "pc": 350, + "stack-pop-count": 1 + }, + { + "pc": 351 + }, + { + "pc": 1321 + }, + { + "pc": 1324 + }, + { + "pc": 881 + }, + { + "pc": 884, + "stack-additions": [ + { + "type": 2 + } + ] + }, + { + "pc": 885, + "stack-pop-count": 1 + } + ] + }, + "appBudget": 48, + "message": "transaction {TX_ID}: logic eval error: assert failed pc=885. Details: pc=885, opcodes=proto 0 0; intc_0 // 0; assert" +}" +`; + exports[`application-client Errors Display nice error messages when there is a logic error 4`] = ` "INFO: Idempotently deploying app "TestingApp" from creator ACCOUNT_1 using 1498 bytes of teal code and 4 bytes of teal code INFO: App TestingApp not found in apps created by ACCOUNT_1; deploying app with version 1.0. INFO: Sent transaction ID TXID_1 appl from ACCOUNT_1 DEBUG: Created app APP_1 from creator ACCOUNT_1 -DEBUG: Received error executing Atomic Transaction Composer and debug flag enabled; attempting dry run to get more information" +INFO: Received error executing Atomic Transaction Composer, for more information enable the debug flag +DEBUG: Received error executing Atomic Transaction Composer and debug flag enabled; attempting simulation to get more information" `; diff --git a/src/types/app-client.spec.ts b/src/types/app-client.spec.ts index 9ac413d1..d974a366 100644 --- a/src/types/app-client.spec.ts +++ b/src/types/app-client.spec.ts @@ -630,69 +630,9 @@ describe('application-client', () => { create_8:" `) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - e.led.traces[0].trace = e.led.traces[0].trace!.replace(new RegExp(`${app.appId}([\\],])`, 'g'), '{APP_ID}$1') - expect(e.led.traces[0]).toMatchInlineSnapshot(` - { - "cost": undefined, - "logs": undefined, - "messages": [ - "ApprovalProgram", - "REJECT", - "logic eval error: assert failed pc=885. Details: pc=885, opcodes=proto 0 0; intc_0 // 0; assert", - ], - "trace": "pc# |ln# |source |scratch |stack - 1 |1 |intcblock 0 1 10 5 1 1 | |[] - 9 |2 |bytecblock 0x 0x151f7c75 | |[] - 17 |3 |txn NumAppArgs | |[] - 19 |4 |intc_0 // 0 | |[1] - 20 |5 |== | |[1, 0] - 21 |6 |bnz label1 | |[0] - 24 |7 |txna ApplicationArgs 0 | |[] - 27 |8 |pushbytes 0xf17e80a5 // 0xf17e... | |[0x44d0da0d] - 33 |9 |== | |[0x44d0da0d, 0xf17e80a5] - 34 |10 |bnz label2 | |[0] - 37 |11 |txna ApplicationArgs 0 | |[] - 40 |12 |pushbytes 0x0a92a81e // 0x0a92... | |[0x44d0da0d] - 46 |13 |== | |[0x44d0da0d, 0x0a92a81e] - 47 |14 |bnz label3 | |[0] - 50 |15 |txna ApplicationArgs 0 | |[] - 53 |16 |pushbytes 0xad75602c // 0xad75... | |[0x44d0da0d] - 59 |17 |== | |[0x44d0da0d, 0xad75602c] - 60 |18 |bnz label4 | |[0] - 63 |19 |txna ApplicationArgs 0 | |[] - 66 |20 |pushbytes 0xa4cf8dea // 0xa4cf... | |[0x44d0da0d] - 72 |21 |== | |[0x44d0da0d, 0xa4cf8dea] - 73 |22 |bnz label5 | |[0] - 76 |23 |txna ApplicationArgs 0 | |[] - 79 |24 |pushbytes 0xcec2834a // 0xcec2... | |[0x44d0da0d] - 85 |25 |== | |[0x44d0da0d, 0xcec2834a] - 86 |26 |bnz label6 | |[0] - 89 |27 |txna ApplicationArgs 0 | |[] - 92 |28 |pushbytes 0xa4b4a230 // 0xa4b4... | |[0x44d0da0d] - 98 |29 |== | |[0x44d0da0d, 0xa4b4a230] - 99 |30 |bnz label7 | |[0] - 102 |31 |txna ApplicationArgs 0 | |[] - 105 |32 |pushbytes 0x44d0da0d // 0x44d0... | |[0x44d0da0d] - 111 |33 |== | |[0x44d0da0d, 0x44d0da0d] - 112 |34 |bnz label8 | |[1] - 341 |165 |txn OnCompletion | |[] - 343 |166 |intc_0 // 0 | |[0] - 344 |167 |== | |[0, 0] - 345 |168 |txn ApplicationID | |[1] - 347 |169 |intc_0 // 0 | |[1, {APP_ID}] - 348 |170 |!= | |[1, {APP_ID}, 0] - 349 |171 |&& | |[1, 1] - 350 |172 |assert | |[1] - 351 |173 |callsub label25 | |[] - 1321 |702 |proto 0 0 | |[] - 1324 |703 |callsub label50 | |[] - 881 |450 |proto 0 0 | |[] - 884 |451 |intc_0 // 0 | |[] - 885 |452 |assert | |[0] - 885 |452 |!! assert failed pc=885 !! | |[0] - ", - } - `) + const originalData = JSON.stringify(e.led.traces[0], null, 2).replace(/transaction [A-Z0-9]{52}/, 'transaction {TX_ID}') + const updatedData = originalData.replace(/("pc": 345[\s\S]+?stack-additions[\s\S]+?uint": )\d+/g, '$1"APP_ID"') + expect(updatedData).toMatchSnapshot() } expect(