From 475b8a4e2ca9b27b2e77df0191ced9b515f57064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Tue, 8 Oct 2024 13:49:10 +0300 Subject: [PATCH 1/2] In outcome parsers, handle both TransactionOnNetwork and TransactionOutcome. --- .github/workflows/build.yml | 3 +- package.json | 7 +- src/converters/transactionsConverter.ts | 8 + src/testutils/networkProviders.ts | 21 ++- ...> tokenOperationsFactory.test.net.spec.ts} | 0 .../delegationTransactionsOutcomeParser.ts | 23 ++- ...tTransactionsOutcomeParser.dev.net.spec.ts | 40 +++++ .../smartContractTransactionsOutcomeParser.ts | 94 ++++++++-- ...okenManagementTransactionsOutcomeParser.ts | 163 ++++++++++++------ 9 files changed, 281 insertions(+), 78 deletions(-) rename src/tokenOperations/{tokenOperationsFactory.testnet.spec.ts => tokenOperationsFactory.test.net.spec.ts} (100%) create mode 100644 src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.dev.net.spec.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 82323997d..5881ae129 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,4 +27,5 @@ jobs: - run: npm run lint - run: npm run compile - run: npm install esmify && npm run compile-browser - - run: npm test + - run: npm run tests-unit + - run: npm run tests-devnet diff --git a/package.json b/package.json index 1fe34dd72..94358aaaf 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,11 @@ ], "scripts": { "test": "npm run tests-unit", - "tests-unit": "mocha $(find . -name '*.spec.ts' ! -name '*.local.net.spec.*' ! -name '*.devnet.spec.*' ! -name '*.testnet.spec.*')", + "tests-unit": "mocha $(find . -name '*.spec.ts' ! -name '*.local.net.spec.*' ! -name '*.test.net.spec.*' ! -name '*.dev.net.spec.*' ! -name '*.main.net.spec.*')", "tests-localnet": "mocha $(find . -name '*.local.net.spec.ts')", - "tests-devnet": "mocha $(find . -name '*.devnet.spec.ts')", - "tests-testnet": "mocha $(find . -name '*.testnet.spec.ts')", + "tests-testnet": "mocha $(find . -name '*.test.net.spec.ts')", + "tests-devnet": "mocha $(find . -name '*.dev.net.spec.ts')", + "tests-mainnet": "mocha $(find . -name '*.main.net.spec.ts')", "compile-browser": "tsc -p tsconfig.json && browserify out/index.js -o out-browser/sdk-core.js --standalone multiversxSdkCore -p esmify", "compile": "tsc -p tsconfig.json", "compile-proto": "npx pbjs -t static-module -w default -o src/proto/compiled.js src/proto/transaction.proto", diff --git a/src/converters/transactionsConverter.ts b/src/converters/transactionsConverter.ts index c6001c5d6..d94cb11fa 100644 --- a/src/converters/transactionsConverter.ts +++ b/src/converters/transactionsConverter.ts @@ -79,6 +79,14 @@ export class TransactionsConverter { return Buffer.from(value || "", "hex"); } + /** + * @deprecated Where {@link TransactionOutcome} was needed (throughout the SDK), pass the {@link ITransactionOnNetwork} object instead. + * + * Summarizes the outcome of a transaction on the network, and maps it to the "standard" resources (according to the sdk-specs). + * + * In the future, this converter function will become obsolete, + * as the impedance mismatch between the network components and the "core" components will be reduced. + */ public transactionOnNetworkToOutcome(transactionOnNetwork: ITransactionOnNetwork): TransactionOutcome { // In the future, this will not be needed because the transaction, as returned from the API, // will hold the data corresponding to the direct smart contract call outcome (in case of smart contract calls). diff --git a/src/testutils/networkProviders.ts b/src/testutils/networkProviders.ts index 3b27e3eda..67ed2a4d1 100644 --- a/src/testutils/networkProviders.ts +++ b/src/testutils/networkProviders.ts @@ -1,4 +1,3 @@ -import { ApiNetworkProvider, ProxyNetworkProvider } from "../networkProviders"; import { IAddress } from "../interface"; import { IAccountOnNetwork, @@ -7,6 +6,7 @@ import { ITransactionOnNetwork, ITransactionStatus, } from "../interfaceOfNetwork"; +import { ApiNetworkProvider, ProxyNetworkProvider } from "../networkProviders"; import { Query } from "../smartcontracts/query"; import { Transaction } from "../transaction"; @@ -15,7 +15,24 @@ export function createLocalnetProvider(): INetworkProvider { } export function createTestnetProvider(): INetworkProvider { - return new ApiNetworkProvider("https://testnet-api.multiversx.com", { timeout: 5000 }); + return new ApiNetworkProvider("https://testnet-api.multiversx.com", { + timeout: 5000, + clientName: "mx-sdk-js-core/tests", + }); +} + +export function createDevnetProvider(): INetworkProvider { + return new ProxyNetworkProvider("https://devnet-gateway.multiversx.com", { + timeout: 5000, + clientName: "mx-sdk-js-core/tests", + }); +} + +export function createMainnetProvider(): INetworkProvider { + return new ProxyNetworkProvider("https://gateway.multiversx.com", { + timeout: 10000, + clientName: "mx-sdk-js-core/tests", + }); } export interface INetworkProvider { diff --git a/src/tokenOperations/tokenOperationsFactory.testnet.spec.ts b/src/tokenOperations/tokenOperationsFactory.test.net.spec.ts similarity index 100% rename from src/tokenOperations/tokenOperationsFactory.testnet.spec.ts rename to src/tokenOperations/tokenOperationsFactory.test.net.spec.ts diff --git a/src/transactionsOutcomeParsers/delegationTransactionsOutcomeParser.ts b/src/transactionsOutcomeParsers/delegationTransactionsOutcomeParser.ts index 305949058..b7e9a0a03 100644 --- a/src/transactionsOutcomeParsers/delegationTransactionsOutcomeParser.ts +++ b/src/transactionsOutcomeParsers/delegationTransactionsOutcomeParser.ts @@ -1,18 +1,35 @@ import { Address } from "../address"; +import { TransactionsConverter } from "../converters/transactionsConverter"; import { ErrParseTransactionOutcome } from "../errors"; +import { ITransactionOnNetwork } from "../interfaceOfNetwork"; import { TransactionEvent, TransactionOutcome, findEventsByIdentifier } from "./resources"; export class DelegationTransactionsOutcomeParser { constructor() {} - parseCreateNewDelegationContract(transactionOutcome: TransactionOutcome): { contractAddress: string }[] { - this.ensureNoError(transactionOutcome.logs.events); + parseCreateNewDelegationContract( + transaction: TransactionOutcome | ITransactionOnNetwork, + ): { contractAddress: string }[] { + transaction = this.ensureTransactionOutcome(transaction); - const events = findEventsByIdentifier(transactionOutcome, "SCDeploy"); + this.ensureNoError(transaction.logs.events); + + const events = findEventsByIdentifier(transaction, "SCDeploy"); return events.map((event) => ({ contractAddress: this.extractContractAddress(event) })); } + /** + * Temporary workaround, until "TransactionOnNetwork" completely replaces "TransactionOutcome". + */ + private ensureTransactionOutcome(transaction: TransactionOutcome | ITransactionOnNetwork): TransactionOutcome { + if ("hash" in transaction) { + return new TransactionsConverter().transactionOnNetworkToOutcome(transaction); + } + + return transaction; + } + private ensureNoError(transactionEvents: TransactionEvent[]) { for (const event of transactionEvents) { if (event.identifier == "signalError") { diff --git a/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.dev.net.spec.ts b/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.dev.net.spec.ts new file mode 100644 index 000000000..6eb10aa4f --- /dev/null +++ b/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.dev.net.spec.ts @@ -0,0 +1,40 @@ +import { assert } from "chai"; +import { TransactionsConverter } from "../converters/transactionsConverter"; +import { createDevnetProvider } from "../testutils/networkProviders"; +import { SmartContractTransactionsOutcomeParser } from "./smartContractTransactionsOutcomeParser"; + +describe("test smart contract transactions outcome parser on devnet", () => { + const networkProvider = createDevnetProvider(); + const parser = new SmartContractTransactionsOutcomeParser(); + const transactionsConverter = new TransactionsConverter(); + + it("should parse outcome of deploy transactions (1)", async () => { + const transactionHash = "5d2ff2af8eb3fe7f2acb7e29c0436854b4c6c44de02878b6afff582888024a55"; + const transactionOnNetwork = await networkProvider.getTransaction(transactionHash); + const transactionOutcome = transactionsConverter.transactionOnNetworkToOutcome(transactionOnNetwork); + const parsedGivenTransactionOnNetwork = parser.parseDeploy({ transactionOnNetwork }); + const parsedGivenTransactionOutcome = parser.parseDeploy({ transactionOutcome }); + + assert.deepEqual(parsedGivenTransactionOnNetwork, parsedGivenTransactionOutcome); + assert.equal(parsedGivenTransactionOnNetwork.returnCode, "ok"); + assert.deepEqual(parsedGivenTransactionOnNetwork.contracts, [ + { + address: "erd1qqqqqqqqqqqqqpgqpayq2es08gq8798xhnpr0kzgn7495qt5q6uqd7lpwf", + ownerAddress: "erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6", + codeHash: Buffer.from("c876625ec34a04445cfd99067777ebe488afdbc6899cd958f4c1d36107ca02d9", "hex"), + }, + ]); + }); + + it("should parse outcome of deploy transactions (2)", async () => { + const transactionHash = "76683e926dad142fc9651afca208487f2a80d327fc87e5c876eec9d028196352"; + const transactionOnNetwork = await networkProvider.getTransaction(transactionHash); + const transactionOutcome = transactionsConverter.transactionOnNetworkToOutcome(transactionOnNetwork); + const parsedGivenTransactionOnNetwork = parser.parseDeploy({ transactionOnNetwork }); + const parsedGivenTransactionOutcome = parser.parseDeploy({ transactionOutcome }); + + assert.deepEqual(parsedGivenTransactionOnNetwork, parsedGivenTransactionOutcome); + assert.equal(parsedGivenTransactionOnNetwork.returnCode, "execution failed"); + assert.lengthOf(parsedGivenTransactionOnNetwork.contracts, 0); + }); +}); diff --git a/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.ts b/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.ts index 7c240504c..fe0777ef8 100644 --- a/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.ts +++ b/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.ts @@ -1,7 +1,14 @@ import { Address } from "../address"; import { Err } from "../errors"; +import { ITransactionOnNetwork } from "../interfaceOfNetwork"; import { EndpointDefinition, ResultsParser, ReturnCode, Type, UntypedOutcomeBundle } from "../smartcontracts"; -import { TransactionEvent, TransactionOutcome, findEventsByIdentifier } from "./resources"; +import { TransactionOutcome, findEventsByIdentifier } from "./resources"; + +enum Events { + SCDeploy = "SCDeploy", + SignalError = "signalError", + WriteLog = "writeLog", +} interface IAbi { getEndpoint(name: string): EndpointDefinition; @@ -28,15 +35,31 @@ export class SmartContractTransactionsOutcomeParser { constructor(options?: { abi?: IAbi; legacyResultsParser?: ILegacyResultsParser }) { this.abi = options?.abi; - - // Prior v13, we've advertised that people can override the "ResultsParser" to alter it's behavior in case of exotic flows. - // Now, since the new "SmartContractTransactionsOutcomeParser" (still) depends on the legacy "ResultsParser", - // at least until "return data parts of direct outcome of contract call" are included on API & Proxy responses (on GET transaction), - // we have to allow the same level of customization (for exotic flows). this.legacyResultsParser = options?.legacyResultsParser || new ResultsParser(); } - parseDeploy(options: { transactionOutcome: TransactionOutcome }): { + parseDeploy( + options: { transactionOutcome: TransactionOutcome } | { transactionOnNetwork: ITransactionOnNetwork }, + ): { + returnCode: string; + returnMessage: string; + contracts: { + address: string; + ownerAddress: string; + codeHash: Uint8Array; + }[]; + } { + if ("transactionOutcome" in options) { + return this.parseDeployGivenTransactionOutcome(options.transactionOutcome); + } + + return this.parseDeployGivenTransactionOnNetwork(options.transactionOnNetwork); + } + + /** + * Legacy approach. + */ + protected parseDeployGivenTransactionOutcome(transactionOutcome: TransactionOutcome): { returnCode: string; returnMessage: string; contracts: { @@ -45,8 +68,8 @@ export class SmartContractTransactionsOutcomeParser { codeHash: Uint8Array; }[]; } { - const directCallOutcome = options.transactionOutcome.directSmartContractCallOutcome; - const events = findEventsByIdentifier(options.transactionOutcome, "SCDeploy"); + const directCallOutcome = transactionOutcome.directSmartContractCallOutcome; + const events = findEventsByIdentifier(transactionOutcome, Events.SCDeploy); const contracts = events.map((event) => this.parseScDeployEvent(event)); return { @@ -56,7 +79,19 @@ export class SmartContractTransactionsOutcomeParser { }; } - private parseScDeployEvent(event: TransactionEvent): { + protected parseDeployGivenTransactionOnNetwork(_transactionOnNetwork: ITransactionOnNetwork): { + returnCode: string; + returnMessage: string; + contracts: { + address: string; + ownerAddress: string; + codeHash: Uint8Array; + }[]; + } { + throw new Error("Not implemented."); + } + + private parseScDeployEvent(event: { topics: Uint8Array[] }): { address: string; ownerAddress: string; codeHash: Uint8Array; @@ -76,12 +111,34 @@ export class SmartContractTransactionsOutcomeParser { }; } - parseExecute(options: { transactionOutcome: TransactionOutcome; function?: string }): { + parseExecute( + options: + | { transactionOutcome: TransactionOutcome; function?: string } + | { transactionOnNetwork: ITransactionOnNetwork; function?: string }, + ): { values: any[]; returnCode: string; returnMessage: string; } { - const directCallOutcome = options.transactionOutcome.directSmartContractCallOutcome; + if ("transactionOutcome" in options) { + return this.parseExecuteGivenTransactionOutcome(options.transactionOutcome, options.function); + } + + return this.parseExecuteGivenTransactionOnNetwork(options.transactionOnNetwork, options.function); + } + + /** + * Legacy approach. + */ + protected parseExecuteGivenTransactionOutcome( + transactionOutcome: TransactionOutcome, + functionName?: string, + ): { + values: any[]; + returnCode: string; + returnMessage: string; + } { + const directCallOutcome = transactionOutcome.directSmartContractCallOutcome; if (!this.abi) { return { @@ -91,7 +148,7 @@ export class SmartContractTransactionsOutcomeParser { }; } - const functionName = options.function || directCallOutcome.function; + functionName = functionName || directCallOutcome.function; if (!functionName) { throw new Err( @@ -115,4 +172,15 @@ export class SmartContractTransactionsOutcomeParser { returnMessage: legacyTypedBundle.returnMessage, }; } + + protected parseExecuteGivenTransactionOnNetwork( + _transactionOnNetwork: ITransactionOnNetwork, + _functionName?: string, + ): { + values: any[]; + returnCode: string; + returnMessage: string; + } { + throw new Error("Not implemented."); + } } diff --git a/src/transactionsOutcomeParsers/tokenManagementTransactionsOutcomeParser.ts b/src/transactionsOutcomeParsers/tokenManagementTransactionsOutcomeParser.ts index 0702cba6c..fcf78d585 100644 --- a/src/transactionsOutcomeParsers/tokenManagementTransactionsOutcomeParser.ts +++ b/src/transactionsOutcomeParsers/tokenManagementTransactionsOutcomeParser.ts @@ -1,45 +1,57 @@ import { Address } from "../address"; +import { TransactionsConverter } from "../converters/transactionsConverter"; import { ErrParseTransactionOutcome } from "../errors"; +import { ITransactionOnNetwork } from "../interfaceOfNetwork"; import { bufferToBigInt } from "../smartcontracts/codec/utils"; import { TransactionEvent, TransactionOutcome, findEventsByIdentifier } from "./resources"; export class TokenManagementTransactionsOutcomeParser { constructor() {} - parseIssueFungible(transactionOutcome: TransactionOutcome): { tokenIdentifier: string }[] { - this.ensureNoError(transactionOutcome.logs.events); + parseIssueFungible(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string }[] { + transaction = this.ensureTransactionOutcome(transaction); - const events = findEventsByIdentifier(transactionOutcome, "issue"); + this.ensureNoError(transaction.logs.events); + + const events = findEventsByIdentifier(transaction, "issue"); return events.map((event) => ({ tokenIdentifier: this.extractTokenIdentifier(event) })); } - parseIssueNonFungible(transactionOutcome: TransactionOutcome): { tokenIdentifier: string }[] { - this.ensureNoError(transactionOutcome.logs.events); + parseIssueNonFungible(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string }[] { + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "issueNonFungible"); + const events = findEventsByIdentifier(transaction, "issueNonFungible"); return events.map((event) => ({ tokenIdentifier: this.extractTokenIdentifier(event) })); } - parseIssueSemiFungible(transactionOutcome: TransactionOutcome): { tokenIdentifier: string }[] { - this.ensureNoError(transactionOutcome.logs.events); + parseIssueSemiFungible(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string }[] { + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "issueSemiFungible"); + const events = findEventsByIdentifier(transaction, "issueSemiFungible"); return events.map((event) => ({ tokenIdentifier: this.extractTokenIdentifier(event) })); } - parseRegisterMetaEsdt(transactionOutcome: TransactionOutcome): { tokenIdentifier: string }[] { - this.ensureNoError(transactionOutcome.logs.events); + parseRegisterMetaEsdt(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string }[] { + transaction = this.ensureTransactionOutcome(transaction); - const events = findEventsByIdentifier(transactionOutcome, "registerMetaESDT"); + this.ensureNoError(transaction.logs.events); + + const events = findEventsByIdentifier(transaction, "registerMetaESDT"); return events.map((event) => ({ tokenIdentifier: this.extractTokenIdentifier(event) })); } parseRegisterAndSetAllRoles( - transactionOutcome: TransactionOutcome, + transaction: TransactionOutcome | ITransactionOnNetwork, ): { tokenIdentifier: string; roles: string[] }[] { - this.ensureNoError(transactionOutcome.logs.events); - const registerEvents = findEventsByIdentifier(transactionOutcome, "registerAndSetAllRoles"); - const setRoleEvents = findEventsByIdentifier(transactionOutcome, "ESDTSetRole"); + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); + const registerEvents = findEventsByIdentifier(transaction, "registerAndSetAllRoles"); + const setRoleEvents = findEventsByIdentifier(transaction, "ESDTSetRole"); if (registerEvents.length !== setRoleEvents.length) { throw new ErrParseTransactionOutcome( @@ -55,22 +67,28 @@ export class TokenManagementTransactionsOutcomeParser { }); } - parseSetBurnRoleGlobally(transactionOutcome: TransactionOutcome) { - this.ensureNoError(transactionOutcome.logs.events); + parseSetBurnRoleGlobally(transaction: TransactionOutcome | ITransactionOnNetwork) { + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); } - parseUnsetBurnRoleGlobally(transactionOutcome: TransactionOutcome) { - this.ensureNoError(transactionOutcome.logs.events); + parseUnsetBurnRoleGlobally(transaction: TransactionOutcome | ITransactionOnNetwork) { + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); } - parseSetSpecialRole(transactionOutcome: TransactionOutcome): { + parseSetSpecialRole(transaction: TransactionOutcome | ITransactionOnNetwork): { userAddress: string; tokenIdentifier: string; roles: string[]; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "ESDTSetRole"); + const events = findEventsByIdentifier(transaction, "ESDTSetRole"); return events.map((event) => this.getOutputForSetSpecialRoleEvent(event)); } @@ -87,14 +105,16 @@ export class TokenManagementTransactionsOutcomeParser { return { userAddress: userAddress, tokenIdentifier: tokenIdentifier, roles: roles }; } - parseNftCreate(transactionOutcome: TransactionOutcome): { + parseNftCreate(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string; nonce: bigint; initialQuantity: bigint; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "ESDTNFTCreate"); + const events = findEventsByIdentifier(transaction, "ESDTNFTCreate"); return events.map((event) => this.getOutputForNftCreateEvent(event)); } @@ -110,15 +130,17 @@ export class TokenManagementTransactionsOutcomeParser { return { tokenIdentifier: tokenIdentifier, nonce: nonce, initialQuantity: amount }; } - parseLocalMint(transactionOutcome: TransactionOutcome): { + parseLocalMint(transaction: TransactionOutcome | ITransactionOnNetwork): { userAddress: string; tokenIdentifier: string; nonce: bigint; mintedSupply: bigint; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); - const events = findEventsByIdentifier(transactionOutcome, "ESDTLocalMint"); + this.ensureNoError(transaction.logs.events); + + const events = findEventsByIdentifier(transaction, "ESDTLocalMint"); return events.map((event) => this.getOutputForLocalMintEvent(event)); } @@ -141,15 +163,17 @@ export class TokenManagementTransactionsOutcomeParser { }; } - parseLocalBurn(transactionOutcome: TransactionOutcome): { + parseLocalBurn(transaction: TransactionOutcome | ITransactionOnNetwork): { userAddress: string; tokenIdentifier: string; nonce: bigint; burntSupply: bigint; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "ESDTLocalBurn"); + const events = findEventsByIdentifier(transaction, "ESDTLocalBurn"); return events.map((event) => this.getOutputForLocalBurnEvent(event)); } @@ -172,29 +196,35 @@ export class TokenManagementTransactionsOutcomeParser { }; } - parsePause(transactionOutcome: TransactionOutcome): { tokenIdentifier: string }[] { - this.ensureNoError(transactionOutcome.logs.events); + parsePause(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string }[] { + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "ESDTPause"); + const events = findEventsByIdentifier(transaction, "ESDTPause"); return events.map((event) => ({ tokenIdentifier: this.extractTokenIdentifier(event) })); } - parseUnpause(transactionOutcome: TransactionOutcome): { tokenIdentifier: string }[] { - this.ensureNoError(transactionOutcome.logs.events); + parseUnpause(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string }[] { + transaction = this.ensureTransactionOutcome(transaction); - const events = findEventsByIdentifier(transactionOutcome, "ESDTUnPause"); + this.ensureNoError(transaction.logs.events); + + const events = findEventsByIdentifier(transaction, "ESDTUnPause"); return events.map((event) => ({ tokenIdentifier: this.extractTokenIdentifier(event) })); } - parseFreeze(transactionOutcome: TransactionOutcome): { + parseFreeze(transaction: TransactionOutcome | ITransactionOnNetwork): { userAddress: string; tokenIdentifier: string; nonce: bigint; balance: bigint; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "ESDTFreeze"); + const events = findEventsByIdentifier(transaction, "ESDTFreeze"); return events.map((event) => this.getOutputForFreezeEvent(event)); } @@ -217,15 +247,17 @@ export class TokenManagementTransactionsOutcomeParser { }; } - parseUnfreeze(transactionOutcome: TransactionOutcome): { + parseUnfreeze(transaction: TransactionOutcome | ITransactionOnNetwork): { userAddress: string; tokenIdentifier: string; nonce: bigint; balance: bigint; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "ESDTUnFreeze"); + const events = findEventsByIdentifier(transaction, "ESDTUnFreeze"); return events.map((event) => this.getOutputForUnfreezeEvent(event)); } @@ -248,15 +280,17 @@ export class TokenManagementTransactionsOutcomeParser { }; } - parseWipe(transactionOutcome: TransactionOutcome): { + parseWipe(transaction: TransactionOutcome | ITransactionOnNetwork): { userAddress: string; tokenIdentifier: string; nonce: bigint; balance: bigint; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); - const events = findEventsByIdentifier(transactionOutcome, "ESDTWipe"); + this.ensureNoError(transaction.logs.events); + + const events = findEventsByIdentifier(transaction, "ESDTWipe"); return events.map((event) => this.getOutputForWipeEvent(event)); } @@ -279,14 +313,16 @@ export class TokenManagementTransactionsOutcomeParser { }; } - parseUpdateAttributes(transactionOutcome: TransactionOutcome): { + parseUpdateAttributes(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string; nonce: bigint; attributes: Uint8Array; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "ESDTNFTUpdateAttributes"); + const events = findEventsByIdentifier(transaction, "ESDTNFTUpdateAttributes"); return events.map((event) => this.getOutputForUpdateAttributesEvent(event)); } @@ -306,14 +342,16 @@ export class TokenManagementTransactionsOutcomeParser { }; } - parseAddQuantity(transactionOutcome: TransactionOutcome): { + parseAddQuantity(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string; nonce: bigint; addedQuantity: bigint; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); + + this.ensureNoError(transaction.logs.events); - const events = findEventsByIdentifier(transactionOutcome, "ESDTNFTAddQuantity"); + const events = findEventsByIdentifier(transaction, "ESDTNFTAddQuantity"); return events.map((event) => this.getOutputForAddQuantityEvent(event)); } @@ -333,14 +371,16 @@ export class TokenManagementTransactionsOutcomeParser { }; } - parseBurnQuantity(transactionOutcome: TransactionOutcome): { + parseBurnQuantity(transaction: TransactionOutcome | ITransactionOnNetwork): { tokenIdentifier: string; nonce: bigint; burntQuantity: bigint; }[] { - this.ensureNoError(transactionOutcome.logs.events); + transaction = this.ensureTransactionOutcome(transaction); - const events = findEventsByIdentifier(transactionOutcome, "ESDTNFTBurn"); + this.ensureNoError(transaction.logs.events); + + const events = findEventsByIdentifier(transaction, "ESDTNFTBurn"); return events.map((event) => this.getOutputForBurnQuantityEvent(event)); } @@ -360,6 +400,17 @@ export class TokenManagementTransactionsOutcomeParser { }; } + /** + * Temporary workaround, until "TransactionOnNetwork" completely replaces "TransactionOutcome". + */ + private ensureTransactionOutcome(transaction: TransactionOutcome | ITransactionOnNetwork): TransactionOutcome { + if ("hash" in transaction) { + return new TransactionsConverter().transactionOnNetworkToOutcome(transaction); + } + + return transaction; + } + private ensureNoError(transactionEvents: TransactionEvent[]) { for (const event of transactionEvents) { if (event.identifier == "signalError") { From 8983762e444298047b89f3a89aecbd3edcbafcd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Tue, 8 Oct 2024 13:56:24 +0300 Subject: [PATCH 2/2] Remove tests file, will be added in next PR. --- ...tTransactionsOutcomeParser.dev.net.spec.ts | 40 ------------------- 1 file changed, 40 deletions(-) delete mode 100644 src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.dev.net.spec.ts diff --git a/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.dev.net.spec.ts b/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.dev.net.spec.ts deleted file mode 100644 index 6eb10aa4f..000000000 --- a/src/transactionsOutcomeParsers/smartContractTransactionsOutcomeParser.dev.net.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { assert } from "chai"; -import { TransactionsConverter } from "../converters/transactionsConverter"; -import { createDevnetProvider } from "../testutils/networkProviders"; -import { SmartContractTransactionsOutcomeParser } from "./smartContractTransactionsOutcomeParser"; - -describe("test smart contract transactions outcome parser on devnet", () => { - const networkProvider = createDevnetProvider(); - const parser = new SmartContractTransactionsOutcomeParser(); - const transactionsConverter = new TransactionsConverter(); - - it("should parse outcome of deploy transactions (1)", async () => { - const transactionHash = "5d2ff2af8eb3fe7f2acb7e29c0436854b4c6c44de02878b6afff582888024a55"; - const transactionOnNetwork = await networkProvider.getTransaction(transactionHash); - const transactionOutcome = transactionsConverter.transactionOnNetworkToOutcome(transactionOnNetwork); - const parsedGivenTransactionOnNetwork = parser.parseDeploy({ transactionOnNetwork }); - const parsedGivenTransactionOutcome = parser.parseDeploy({ transactionOutcome }); - - assert.deepEqual(parsedGivenTransactionOnNetwork, parsedGivenTransactionOutcome); - assert.equal(parsedGivenTransactionOnNetwork.returnCode, "ok"); - assert.deepEqual(parsedGivenTransactionOnNetwork.contracts, [ - { - address: "erd1qqqqqqqqqqqqqpgqpayq2es08gq8798xhnpr0kzgn7495qt5q6uqd7lpwf", - ownerAddress: "erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6", - codeHash: Buffer.from("c876625ec34a04445cfd99067777ebe488afdbc6899cd958f4c1d36107ca02d9", "hex"), - }, - ]); - }); - - it("should parse outcome of deploy transactions (2)", async () => { - const transactionHash = "76683e926dad142fc9651afca208487f2a80d327fc87e5c876eec9d028196352"; - const transactionOnNetwork = await networkProvider.getTransaction(transactionHash); - const transactionOutcome = transactionsConverter.transactionOnNetworkToOutcome(transactionOnNetwork); - const parsedGivenTransactionOnNetwork = parser.parseDeploy({ transactionOnNetwork }); - const parsedGivenTransactionOutcome = parser.parseDeploy({ transactionOutcome }); - - assert.deepEqual(parsedGivenTransactionOnNetwork, parsedGivenTransactionOutcome); - assert.equal(parsedGivenTransactionOnNetwork.returnCode, "execution failed"); - assert.lengthOf(parsedGivenTransactionOnNetwork.contracts, 0); - }); -});