Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
andreibancioiu opened this issue Jun 21, 2023 · 2 comments
Assignees
Labels
task Task

Comments

@andreibancioiu
Copy link
Contributor

andreibancioiu commented Jun 21, 2023

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

ErrCannotParseContractResults [Error]: cannot parse contract results
@andreibancioiu andreibancioiu added the task Task label Jun 21, 2023
@andreibancioiu andreibancioiu self-assigned this Jun 21, 2023
@andreibancioiu
Copy link
Contributor Author

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
Copy link
Contributor Author

andreibancioiu commented Jul 21, 2023

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: multiversx/mx-sdk-js-network-providers#41.

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

Related to: multiversx/mx-chain-proxy-go#386.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
task Task
Projects
None yet
Development

No branches or pull requests

1 participant