Skip to content

Commit

Permalink
Merge pull request #510 from multiversx/main
Browse files Browse the repository at this point in the history
Main to feat/unify
  • Loading branch information
danielailie authored Oct 15, 2024
2 parents cf98adc a43c335 commit 56afdaa
Show file tree
Hide file tree
Showing 23 changed files with 12,463 additions and 153 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
4 changes: 2 additions & 2 deletions .github/workflows/test-localnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 40,8 @@ jobs:
run: |
# Start the local testnet with mxpy
mkdir -p ~/localnet && cd ~/localnet
mxpy localnet setup
nohup mxpy localnet start > localnet.log 2>&1 & echo $! > localnet.pid
mxpy localnet setup --configfile=${GITHUB_WORKSPACE}/localnet.toml
nohup mxpy localnet start --configfile=${GITHUB_WORKSPACE}/localnet.toml > localnet.log 2>&1 & echo $! > localnet.pid
sleep 120 # Allow time for the testnet to fully start
# Step 6: Install Node.js and dependencies
Expand Down
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -1,2 1,3 @@
src/testdata/**
src/testutils/**
localnet.toml
41 changes: 41 additions & 0 deletions localnet.toml
Original file line number Diff line number Diff line change
@@ -0,0 1,41 @@
[general]
log_level = "*:DEBUG"
genesis_delay_seconds = 10
rounds_per_epoch = 50
round_duration_milliseconds = 6000

[metashard]
consensus_size = 1
num_observers = 0
num_validators = 1

[shards]
num_shards = 3
consensus_size = 1
num_observers_per_shard = 0
num_validators_per_shard = 1

[networking]
host = "127.0.0.1"
port_seednode = 9999
port_seednode_rest_api = 10000
p2p_id_seednode = "16Uiu2HAkx4QqgXXDdHdUWbLu5kxhd3Uo2hqB2FfCxmxH5Sd7bZFk"
port_proxy = 7950
port_first_observer = 21100
port_first_observer_rest_api = 10100
port_first_validator = 21500
port_first_validator_rest_api = 10200

[software.mx_chain_go]
resolution = "remote"
archive_url = "https://github.com/multiversx/mx-chain-go/archive/refs/heads/master.zip"
archive_download_folder = "~/multiversx-sdk/localnet_software_remote/downloaded/mx-chain-go"
archive_extraction_folder = "~/multiversx-sdk/localnet_software_remote/extracted/mx-chain-go"
local_path = "~/multiversx-sdk/localnet_software_local/mx-chain-go"

[software.mx_chain_proxy_go]
resolution = "remote"
archive_url = "https://github.com/multiversx/mx-chain-proxy-go/archive/refs/heads/master.zip"
archive_download_folder = "~/multiversx-sdk/localnet_software_remote/downloaded/mx-chain-proxy-go"
archive_extraction_folder = "~/multiversx-sdk/localnet_software_remote/extracted/mx-chain-proxy-go"
local_path = "~/multiversx-sdk/localnet_software_local/mx-chain-proxy-go"
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 1,6 @@
{
"name": "@multiversx/sdk-core",
"version": "13.7.0",
"version": "13.9.0",
"description": "MultiversX SDK for JavaScript and TypeScript",
"author": "MultiversX",
"homepage": "https://multiversx.com",
Expand All @@ -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
1 change: 1 addition & 0 deletions src/interfaceOfNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 53,7 @@ export interface IContractResultItem {
data: string;
returnMessage: string;
logs: ITransactionLogs;
previousHash?: string;
}

export interface IContractQueryResponse {
Expand Down
62 changes: 6 additions & 56 deletions src/smartcontracts/resultsParser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,21 1,17 @@
import BigNumber from "bignumber.js";
import { assert } from "chai";
import { Address } from "../address";
import { IAddress } from "../interface";
import {
ContractQueryResponse,
ContractResultItem,
ContractResults,
TransactionEventData,
TransactionEventOnNetwork,
TransactionEventTopic,
TransactionLogsOnNetwork,
TransactionOnNetwork,
TransactionEventData,
} from "../networkProviders";
import BigNumber from "bignumber.js";
import { assert } from "chai";
import * as fs from "fs";
import path from "path";
import { Address } from "../address";
import { IAddress } from "../interface";
import { ITransactionOnNetwork } from "../interfaceOfNetwork";
import { LogLevel, Logger } from "../logger";
import { loadAbiRegistry } from "../testutils";
import { ArgSerializer } from "./argSerializer";
import { ResultsParser } from "./resultsParser";
Expand Down Expand Up @@ -258,10 254,7 @@ describe("test smart contract results parser", () => {

let bundle = parser.parseUntypedOutcome(transaction);
assert.deepEqual(bundle.returnCode, ReturnCode.Ok);
assert.equal(
bundle.returnMessage,
"@too much gas provided for processing: gas provided = 596384500, gas used = 733010",
);
assert.equal(bundle.returnMessage, "ok");
assert.deepEqual(bundle.values, []);
});

Expand Down Expand Up @@ -383,47 376,4 @@ describe("test smart contract results parser", () => {
assert.deepEqual(bundle.b, new BigNumber(43));
assert.deepEqual(bundle.c, new BigNumber(44));
});

// This test should be enabled manually and run against a set of sample transactions.
// 2022-04-03: test ran against ~1800 transactions sampled from devnet.
it.skip("should parse real-world contract outcomes", async () => {
let oldLogLevel = Logger.logLevel;
Logger.setLevel(LogLevel.Trace);

let folder = path.resolve(process.env["SAMPLES"] || "SAMPLES");
let samples = loadRealWorldSamples(folder);

for (const [transaction, _] of samples) {
console.log("Transaction:", transaction.hash.toString());

let bundle = parser.parseUntypedOutcome(transaction);

console.log("Return code:", bundle.returnCode.toString());
console.log("Return message:", bundle.returnMessage);
console.log("Num values:", bundle.values.length);
console.log("=".repeat(80));

assert.include(KnownReturnCodes, bundle.returnCode.valueOf());
}

Logger.setLevel(oldLogLevel);
});

function loadRealWorldSamples(folder: string): [ITransactionOnNetwork, string][] {
let transactionFiles = fs.readdirSync(folder);
let samples: [ITransactionOnNetwork, string][] = [];

for (const file of transactionFiles) {
let txHash = path.basename(file, ".json");
let filePath = path.resolve(folder, file);
let jsonContent: string = fs.readFileSync(filePath, { encoding: "utf8" });
let json = JSON.parse(jsonContent);
let payload = json["data"]["transaction"];
let transaction = TransactionOnNetwork.fromProxyHttpResponse(txHash, payload);

samples.push([transaction, jsonContent]);
}

return samples;
}
});
39 changes: 28 additions & 11 deletions src/smartcontracts/resultsParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 196,7 @@ export class ResultsParser {
throw new ErrCannotParseContractResults(`transaction ${transaction.hash.toString()}`);
}

private parseTransactionMetadata(transaction: ITransactionOnNetwork): TransactionMetadata {
protected parseTransactionMetadata(transaction: ITransactionOnNetwork): TransactionMetadata {
return new TransactionDecoder().getTransactionMetadata({
sender: transaction.sender.bech32(),
receiver: transaction.receiver.bech32(),
Expand All @@ -205,7 205,7 @@ export class ResultsParser {
});
}

private createBundleOnSimpleMoveBalance(transaction: ITransactionOnNetwork): UntypedOutcomeBundle | null {
protected createBundleOnSimpleMoveBalance(transaction: ITransactionOnNetwork): UntypedOutcomeBundle | null {
let noResults = transaction.contractResults.items.length == 0;
let noLogs = transaction.logs.events.length == 0;

Expand All @@ -220,7 220,7 @@ export class ResultsParser {
return null;
}

private createBundleOnInvalidTransaction(transaction: ITransactionOnNetwork): UntypedOutcomeBundle | null {
protected createBundleOnInvalidTransaction(transaction: ITransactionOnNetwork): UntypedOutcomeBundle | null {
if (transaction.status.isInvalid()) {
if (transaction.receipt.data) {
return {
Expand All @@ -236,7 236,7 @@ export class ResultsParser {
return null;
}

private createBundleOnEasilyFoundResultWithReturnData(results: IContractResults): UntypedOutcomeBundle | null {
protected createBundleOnEasilyFoundResultWithReturnData(results: IContractResults): UntypedOutcomeBundle | null {
let resultItemWithReturnData = results.items.find(
(item) => item.nonce.valueOf() != 0 && item.data.startsWith("@"),
);
Expand All @@ -254,7 254,7 @@ export class ResultsParser {
};
}

private createBundleOnSignalError(logs: ITransactionLogs): UntypedOutcomeBundle | null {
protected createBundleOnSignalError(logs: ITransactionLogs): UntypedOutcomeBundle | null {
let eventSignalError = logs.findSingleOrNoneEvent(WellKnownEvents.OnSignalError);
if (!eventSignalError) {
return null;
Expand All @@ -271,7 271,7 @@ export class ResultsParser {
};
}

private createBundleOnTooMuchGasWarning(logs: ITransactionLogs): UntypedOutcomeBundle | null {
protected createBundleOnTooMuchGasWarning(logs: ITransactionLogs): UntypedOutcomeBundle | null {
let eventTooMuchGas = logs.findSingleOrNoneEvent(
WellKnownEvents.OnWriteLog,
(event) =>
Expand All @@ -284,17 284,15 @@ export class ResultsParser {
}

let { returnCode, returnDataParts } = this.sliceDataFieldInParts(eventTooMuchGas.data);
let lastTopic = eventTooMuchGas.getLastTopic();
let returnMessage = lastTopic?.toString() || returnCode.toString();

return {
returnCode: returnCode,
returnMessage: returnMessage,
returnMessage: returnCode.toString(),
values: returnDataParts,
};
}

private createBundleOnWriteLogWhereFirstTopicEqualsAddress(
protected createBundleOnWriteLogWhereFirstTopicEqualsAddress(
logs: ITransactionLogs,
address: IAddress,
): UntypedOutcomeBundle | null {
Expand Down Expand Up @@ -329,7 327,7 @@ export class ResultsParser {
return null;
}

private createBundleWithFallbackHeuristics(
protected createBundleWithFallbackHeuristics(
transaction: ITransactionOnNetwork,
transactionMetadata: TransactionMetadata,
): UntypedOutcomeBundle | null {
Expand All @@ -355,6 353,25 @@ export class ResultsParser {
}
}

// Additional fallback heuristics (alter search constraints):
for (const resultItem of transaction.contractResults.items) {
let writeLogWithReturnData = resultItem.logs.findSingleOrNoneEvent(WellKnownEvents.OnWriteLog, (event) => {
const addressIsContract = event.address.bech32() == contractAddress.toBech32();
return addressIsContract;
});

if (writeLogWithReturnData) {
const { returnCode, returnDataParts } = this.sliceDataFieldInParts(writeLogWithReturnData.data);
const returnMessage = returnCode.toString();

return {
returnCode: returnCode,
returnMessage: returnMessage,
values: returnDataParts,
};
}
}

return null;
}

Expand Down
8 changes: 8 additions & 0 deletions src/smartcontracts/typesystem/abiRegistry.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 203,12 @@ describe("test abi registry", () => {
assert.deepEqual(enumType.variants[1].name, "interrupted");
assert.deepEqual(enumType.variants[1].discriminant, 1);
});

it("should load abi with title for endpoint", async () => {
const registry = await loadAbiRegistry("src/testdata/lottery-esdt.abi.json");

const endpoint = registry.getEndpoint("createLotteryPool");

assert.equal(endpoint.title, "Create lottery pool");
});
});
2 changes: 1 addition & 1 deletion src/smartcontracts/typesystem/abiRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 214,7 @@ function mapEndpoint(endpoint: EndpointDefinition, mapper: TypeMapper): Endpoint
(e) => new EndpointParameterDefinition(e.name, e.description, mapper.mapType(e.type)),
);

return new EndpointDefinition(endpoint.name, newInput, newOutput, endpoint.modifiers);
return new EndpointDefinition(endpoint.name, newInput, newOutput, endpoint.modifiers, endpoint.title);
}

function mapEvent(event: EventDefinition, mapper: TypeMapper): EventDefinition {
Expand Down
7 changes: 6 additions & 1 deletion src/smartcontracts/typesystem/endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 6,7 @@ const DescriptionPlaceholder = "N / A";

export class EndpointDefinition {
readonly name: string;
readonly title: string;
readonly input: EndpointParameterDefinition[] = [];
readonly output: EndpointParameterDefinition[] = [];
readonly modifiers: EndpointModifiers;
Expand All @@ -15,8 16,10 @@ export class EndpointDefinition {
input: EndpointParameterDefinition[],
output: EndpointParameterDefinition[],
modifiers: EndpointModifiers,
title?: string,
) {
this.name = name;
this.title = title || "";
this.input = input || [];
this.output = output || [];
this.modifiers = modifiers;
Expand All @@ -28,6 31,7 @@ export class EndpointDefinition {

static fromJSON(json: {
name: string;
title?: string;
onlyOwner?: boolean;
mutability: string;
payableInTokens: string[];
Expand All @@ -36,6 40,7 @@ export class EndpointDefinition {
}): EndpointDefinition {
json.name = json.name == null ? NamePlaceholder : json.name;
json.onlyOwner = json.onlyOwner || false;
json.title = json.title || "";
json.payableInTokens = json.payableInTokens || [];
json.inputs = json.inputs || [];
json.outputs = json.outputs || [];
Expand All @@ -44,7 49,7 @@ export class EndpointDefinition {
let output = json.outputs.map((param) => EndpointParameterDefinition.fromJSON(param));
let modifiers = new EndpointModifiers(json.mutability, json.payableInTokens, json.onlyOwner);

return new EndpointDefinition(json.name, input, output, modifiers);
return new EndpointDefinition(json.name, input, output, modifiers, json.title);
}
}

Expand Down
Loading

0 comments on commit 56afdaa

Please sign in to comment.