multiversx / mx-sdk-js-core

MultiversX SDK for interacting with the MultiversX blockchain (in general) and Smart Contracts (in particular).
https://multiversx.github.io/mx-sdk-js-core/
Other
58 stars 37 forks source link

Adjust / improve ResultsParser to properly handle outcome of relayed transactions #305

Open andreibancioiu opened 1 year ago

andreibancioiu commented 1 year ago

Issue raised by @MWFIAE - parsing the outcome of a relayed transaction would result into errors such as:

ErrCannotParseContractResults [Error]: cannot parse contract results
andreibancioiu commented 1 year ago

The following contribution of @MWFIAE can serve as a starting point for adjusting the ResultsParser of sdk-core:


/**
 * Custom ResultsParser that is needed until elrond correctly implements relayedTransactions.
 *
 * @export
 * @class KocResultParser
 * @extends {ResultsParser}
 */
export class KocResultParser extends ResultsParser {
    /**
     * Create a bundle with data from relayed transactions.
     *
     * @protected
     * @param {ITransactionOnNetwork} transaction Transaction to parse
     * @param {TransactionMetadata} transactionMetadata Metadata of the transaction
     * @return {(UntypedOutcomeBundle | null)}
     * @memberof KocResultParser
     */
    protected createBundleWithCustomHeuristics(
        transaction: ITransactionOnNetwork,
        transactionMetadata: TransactionMetadata
    ): UntypedOutcomeBundle | null {
        const address = transactionMetadata.sender;
        const hexAddress = new Address(address).hex();

        let eventWriteLogWhereTopicIsSender = transaction.logs.findSingleOrNoneEvent("writeLog", event => {
            debugger;
            return event.findFirstOrNoneTopic(topic => topic.hex() == hexAddress) != undefined;
        });

        if (!eventWriteLogWhereTopicIsSender) {
            for (const result of transaction.contractResults.items) {
                eventWriteLogWhereTopicIsSender = result.logs.findSingleOrNoneEvent(
                    "writeLog",
                    event => event.findFirstOrNoneTopic(topic => topic.hex() == hexAddress) != undefined
                );
                if (!!eventWriteLogWhereTopicIsSender) break;
            }
        }

        if (!eventWriteLogWhereTopicIsSender) {
            return null;
        }

        const { returnCode, returnDataParts } = this.sliceDataFieldInPartsCustom(eventWriteLogWhereTopicIsSender.data);
        const returnMessage = returnCode.toString();

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

    /**
     * Copy of the sliceDataFieldInParts method. Needed until elrond fixes the access modifier.
     *
     * @private
     * @param {string} data The data to split
     * @return {{ returnCode: ReturnCode; returnDataParts: Buffer[] }}
     * @memberof KocResultParser
     */
    private sliceDataFieldInPartsCustom(data: string): { returnCode: ReturnCode; returnDataParts: Buffer[] } {
        // By default, skip the first part, which is usually empty (e.g. "[empty]@6f6b")
        let startingIndex = 1;

        // Before trying to parse the hex strings, cut the unwanted parts of the data field, in case of token transfers:
        if (data.startsWith("ESDTTransfer")) {
            // Skip "ESDTTransfer" (1), token identifier (2), amount (3)
            startingIndex = 3;
        } else {
            // TODO: Upon gathering more transaction samples, fix for other kinds of transfers, as well (future PR, as needed).
        }

        const parts = new ArgSerializer().stringToBuffers(data);
        const returnCodePart = parts[startingIndex] || Buffer.from([]);
        const returnDataParts = parts.slice(startingIndex + 1);

        if (returnCodePart.length == 0) {
            throw new ErrCannotParseContractResults("no return code");
        }

        const returnCode = ReturnCode.fromBuffer(returnCodePart);
        return { returnCode, returnDataParts };
    }
}
andreibancioiu commented 1 year ago

Normally, the "completion detection" issues should be resolved once sdk-core's TransactionWatcher is updated to be compatible with sdk-network-provider's transaction status adjustment: https://github.com/multiversx/mx-sdk-js-network-providers/pull/41.

We will apply that as a non-breaking change (v12).

Related to: https://github.com/multiversx/mx-chain-proxy-go/pull/386.

Later edit: removed unrelated notes (it was a confusion on my side).