Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
## Unreleased
- [Breaking change: adjustements to transaction awaitening and completion, transaction watcher](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/173)
- [Breaking change: simplify network config / improve design - not a singleton anymore](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/176)
- [Fix / improve results parser (better heuristics)](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/177)

**Breaking changes**
- Removed utility functions: `transaction.awaitExecuted()`, `transaction.awaitPending()`. `TransactionWatcher` should be used directly, instead.
Expand All @@ -17,6 +18,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
- Removed `NetworkConfig.getDefault()` and `NetworkConfig.sync()`. Instead, one should use `let networkConfig = await provider.getNetworkConfig()`.
- Constructor of `Transaction` now requires `chainID`, as well.
- Added `Interaction.withChainID()` - must be used before calling `buildTransaction()`.
- Altered a bit the public interface of `TransactionEvent`, `Receipt` (renamed fields, renamed methods).

## [10.0.0-beta.3]
- [Extract dapp / signing providers to separate repositories](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/170)
Expand Down
29 changes: 29 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"author": "ElrondNetwork",
"license": "GPL-3.0-or-later",
"dependencies": {
"@elrondnetwork/transaction-decoder": "0.1.0",
"abort-controller": "3.0.0",
"axios": "0.24.0",
"bech32": "1.1.4",
Expand Down
9 changes: 9 additions & 0 deletions src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,15 @@ export class ErrInvariantFailed extends Err {
}
}

/**
* Signals an unexpected condition.
*/
export class ErrUnexpectedCondition extends Err {
public constructor(message: string) {
super(`Unexpected condition: [${message}]`);
}
}

/**
* Signals issues with {@link Address} instantiation.
*/
Expand Down
131 changes: 131 additions & 0 deletions src/smartcontracts/resultsParser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as fs from "fs";
import path from "path";
import { assert } from "chai";
import { BigUIntType, BigUIntValue, EndpointDefinition, EndpointModifiers, EndpointParameterDefinition } from "./typesystem";
import { BytesType, BytesValue } from "./typesystem/bytes";
Expand All @@ -7,6 +9,29 @@ import { ResultsParser } from "./resultsParser";
import { TransactionOnNetwork } from "../transactionOnNetwork";
import { SmartContractResultItem, SmartContractResults } from "./smartContractResults";
import { Nonce } from "../nonce";
import { TransactionHash } from "../transaction";
import { TransactionEvent, TransactionEventTopic, TransactionLogs } from "../transactionLogs";
import { Address } from "../address";
import { Logger, LogLevel } from "../logger";

const KnownReturnCodes: string[] = [
ReturnCode.None.valueOf(),
ReturnCode.Ok.valueOf(),
ReturnCode.FunctionNotFound.valueOf(),
ReturnCode.FunctionWrongSignature.valueOf(),
ReturnCode.ContractNotFound.valueOf(),
ReturnCode.UserError.valueOf(),
ReturnCode.OutOfGas.valueOf(),
ReturnCode.AccountCollision.valueOf(),
ReturnCode.OutOfFunds.valueOf(),
ReturnCode.CallStackOverFlow.valueOf(), ReturnCode.ContractInvalid.valueOf(),
ReturnCode.ExecutionFailed.valueOf(),
// Returned by protocol, not by VM:
"insufficient funds",
"operation in account not permitted not the owner of the account",
"sending value to non payable contract",
"invalid receiver address"
];

describe("test smart contract results parser", () => {
let parser = new ResultsParser();
Expand Down Expand Up @@ -57,4 +82,110 @@ describe("test smart contract results parser", () => {
assert.deepEqual(bundle.secondValue, BytesValue.fromHex("abba"));
assert.lengthOf(bundle.values, 2);
});

it("should parse contract outcome, on easily found result with return data", async () => {
let transaction = new TransactionOnNetwork({
results: new SmartContractResults([
new SmartContractResultItem({
nonce: new Nonce(42),
data: "@6f6b@03",
returnMessage: "foobar"
})
])
});

let bundle = parser.parseUntypedOutcome(transaction);
assert.deepEqual(bundle.returnCode, ReturnCode.Ok);
assert.equal(bundle.returnMessage, "foobar");
assert.deepEqual(bundle.values, [Buffer.from("03", "hex")]);
});

it("should parse contract outcome, on signal error", async () => {
let transaction = new TransactionOnNetwork({
logs: new TransactionLogs(
new Address(),
[
new TransactionEvent(
new Address(),
"signalError",
[
new TransactionEventTopic(Buffer.from("something happened").toString("base64"))
],
`@${Buffer.from("user error").toString("hex")}@07`
)
]
)
});

let bundle = parser.parseUntypedOutcome(transaction);
assert.deepEqual(bundle.returnCode, ReturnCode.UserError);
assert.equal(bundle.returnMessage, "something happened");
assert.deepEqual(bundle.values, [Buffer.from("07", "hex")]);
});

it("should parse contract outcome, on too much gas warning", async () => {
let transaction = new TransactionOnNetwork({
logs: new TransactionLogs(
new Address(),
[
new TransactionEvent(
new Address(),
"writeLog",
[
new TransactionEventTopic("QHRvbyBtdWNoIGdhcyBwcm92aWRlZCBmb3IgcHJvY2Vzc2luZzogZ2FzIHByb3ZpZGVkID0gNTk2Mzg0NTAwLCBnYXMgdXNlZCA9IDczMzAxMA==")
],
Buffer.from("QDZmNmI=", "base64").toString()
)
]
)
});

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.deepEqual(bundle.values, []);
});

// 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): [TransactionOnNetwork, string][] {
let transactionFiles = fs.readdirSync(folder);
let samples: [TransactionOnNetwork, string][] = [];

for (const file of transactionFiles) {
let txHash = new TransactionHash(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.fromHttpResponse(txHash, payload);

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

return samples;
}
});
Loading