Skip to content

Commit

Permalink
Merge pull request #497 from multiversx/outcome-08
Browse files Browse the repository at this point in the history
In outcome parsers, handle both "TransactionOnNetwork" and "TransactionOutcome"
  • Loading branch information
andreibancioiu authored Oct 8, 2024
2 parents 52f0efb + 8983762 commit 22f0fe6
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 78 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 8 additions & 0 deletions src/converters/transactionsConverter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
21 changes: 19 additions & 2 deletions src/testutils/networkProviders.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { ApiNetworkProvider, ProxyNetworkProvider } from "../networkProviders";
import { IAddress } from "../interface";
import {
IAccountOnNetwork,
Expand All @@ -7,6 +6,7 @@ import {
ITransactionOnNetwork,
ITransactionStatus,
} from "../interfaceOfNetwork";
import { ApiNetworkProvider, ProxyNetworkProvider } from "../networkProviders";
import { Query } from "../smartcontracts/query";
import { Transaction } from "../transaction";

Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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") {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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: {
Expand All @@ -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 {
Expand All @@ -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;
Expand All @@ -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 {
Expand All @@ -91,7 +148,7 @@ export class SmartContractTransactionsOutcomeParser {
};
}

const functionName = options.function || directCallOutcome.function;
functionName = functionName || directCallOutcome.function;

if (!functionName) {
throw new Err(
Expand All @@ -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.");
}
}
Loading

0 comments on commit 22f0fe6

Please sign in to comment.