Skip to content

Commit

Permalink
Merge pull request #498 from multiversx/return-data-8
Browse files Browse the repository at this point in the history
 Recover return data of smart contract (new implementation)
  • Loading branch information
andreibancioiu authored Oct 8, 2024
2 parents 22f0fe6 + 0ac07f1 commit 6f76303
Show file tree
Hide file tree
Showing 10 changed files with 12,039 additions and 70 deletions.
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.

2 changes: 1 addition & 1 deletion 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.8.0",
"description": "MultiversX SDK for JavaScript and TypeScript",
"author": "MultiversX",
"homepage": "https://multiversx.com",
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;
}
});
23 changes: 20 additions & 3 deletions src/smartcontracts/resultsParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,10 @@ 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,
};
}
Expand Down Expand Up @@ -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
111 changes: 111 additions & 0 deletions src/testdata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Testdata

## Files `transactions.mainnet.json`

Transactions were sampled from the mainnet BigQuery dataset:

```sql
DECLARE
TIMESTAMP_START DATE DEFAULT '2024-09-01';
DECLARE
TIMESTAMP_END DATE DEFAULT '2024-09-03';
-- Contract execute, with success
(
SELECT
`_id`,
'execute_success' `kind`
FROM
`multiversx-blockchain-etl.crypto_multiversx_mainnet_eu.transactions`
WHERE
DATE(`timestamp`) >= TIMESTAMP_START
AND DATE(`timestamp`) <= TIMESTAMP_END
AND `isScCall` = TRUE
AND ARRAY_LENGTH(`esdtValues`) = 0
AND `status` = 'success'
AND RAND() < 0.25
LIMIT
250 )
UNION ALL
-- Contract execute, with error
(
SELECT
`_id`,
'execute_error' `kind`
FROM
`multiversx-blockchain-etl.crypto_multiversx_mainnet_eu.transactions`
WHERE
DATE(`timestamp`) >= TIMESTAMP_START
AND DATE(`timestamp`) <= TIMESTAMP_END
AND `isScCall` = TRUE
AND ARRAY_LENGTH(`esdtValues`) = 0
AND `status` = 'fail'
AND RAND() < 0.25
LIMIT
250 )
UNION ALL
-- Contract transfer & execute, with success
(
SELECT
`_id`,
'transfer_execute_success' `kind`
FROM
`multiversx-blockchain-etl.crypto_multiversx_mainnet_eu.transactions`
WHERE
DATE(`timestamp`) >= TIMESTAMP_START
AND DATE(`timestamp`) <= TIMESTAMP_END
AND `isScCall` = TRUE
AND ARRAY_LENGTH(`esdtValues`) > 0
AND `status` = 'success'
AND RAND() < 0.25
LIMIT
250 )
UNION ALL
-- Contract transfer & execute, with error
(
SELECT
`_id`,
'transfer_execute_error' `kind`
FROM
`multiversx-blockchain-etl.crypto_multiversx_mainnet_eu.transactions`
WHERE
DATE(`timestamp`) >= TIMESTAMP_START
AND DATE(`timestamp`) <= TIMESTAMP_END
AND `isScCall` = TRUE
AND ARRAY_LENGTH(`esdtValues`) > 0
AND `status` = 'fail'
AND RAND() < 0.25
LIMIT
250)
UNION ALL
-- Relayed, with success
(
SELECT
`_id`,
'relayed_success' `kind`
FROM
`multiversx-blockchain-etl.crypto_multiversx_mainnet_eu.transactions`
WHERE
DATE(`timestamp`) >= TIMESTAMP_START
AND DATE(`timestamp`) <= TIMESTAMP_END
AND `isRelayed` = TRUE
AND `status` = 'success'
AND RAND() < 0.25
LIMIT
50)
UNION ALL
-- Relayed, with failure
(
SELECT
`_id`,
'relayed_error' `kind`
FROM
`multiversx-blockchain-etl.crypto_multiversx_mainnet_eu.transactions`
WHERE
DATE(`timestamp`) >= TIMESTAMP_START
AND DATE(`timestamp`) <= TIMESTAMP_END
AND `isRelayed` = TRUE
AND `status` = 'fail'
AND RAND() < 0.25
LIMIT
50)
```
Loading

0 comments on commit 6f76303

Please sign in to comment.