How to decode the output of the first call and use it to send a transaction

alchemyevmmetamaskpolygonweb3js

I am attempting to call view function “0x…” with argument of value “3…” to a specific address “0x…”
and also send X wei to that address

I am looking to acquire the hash of the successful tx.

Tools/Platforms: Web.js, Alchemy, EVM, Polygon Mumbai

I have issues completing the transaction.
The console log is showing the transaction as successful but there is no update to the blockchain on mumbai.polygonscan.

I have connected metamask wallet (with test MATIC and Eth) to the testnet and coded into my program.

Code:

const { ethers } = require('ethers');

const provider = new ethers.providers.JsonRpcProvider('https://polygon-mumbai.g.alchemy.com/v2/H....');

const contractAddress = '0x...';
const firstFunctionSelector = '0x...';
const secondFunctionSelector = '0x...';

// Define the sender's private key
const senderPrivateKey = '3...'; // Replace 'YOUR_PRIVATE_KEY_HERE' with the actual private key

async function callFunction() {
    try {
        const wallet = new ethers.Wallet(senderPrivateKey, provider);

        // 1st interaction: Call 0x.... function
        const firstCallData = firstFunctionSelector;
        const firstTx = {
            from: wallet.address,
            to: contractAddress,
            data: firstCallData,
        };
        const firstTxResult = await provider.call(firstTx);    
        // Extract the argument and value from the response of the first call
        const argument = firstTxResult.slice(0, 66); // Extract the first 32 bytes (64 characters) as the argument
        const hexValue = firstTxResult.slice(66); // Extract the value

        console.log(firstTxResult)
        console.log(argument)
        console.log(hexValue)
        



        // 2nd interaction: Use output from first call to send transaction
        // const secondTx = {
        //     to: contractAddress,
        //     data: secondFunctionSelector + argument.slice(2), // Remove '0x' prefix from the argument
        //     value: ethers.BigNumber.from(hexValue), // Convert hexadecimal string to BigNumber
        //     gasLimit: 500000,
        //     chainId: 80001, // Assuming Mumbai testnet
        // };

        // // const txResponse = await wallet.sendTransaction(secondTx);
        // console.log('Transaction Hash:', txResponse.hash);
    } catch (error) {
        console.error('Error calling the function:', error);
    }
}

callFunction();

Output:

node call-view-function.js
0x…

0x0000000000000000000000000000000000000000000000000000000000000020

00…

I need to decode this data, and use the result to send the transaction in the next step

Please assist with the code to achieve this.

Best Answer

Here's how you can do it, by decoding the response of the first transaction, and then fetching the necessary details from the same using regex. Then, using that info, send the second transaction.

const { ethers } = require('ethers');

const provider = new ethers.providers.JsonRpcProvider('YOUR_JSON_RPC_URL');

const contractAddress = '0x...';
const firstFunctionSelector = '0x...';

// Define the sender's private key
const senderPrivateKey = 'YOUR_PRIVATE_KEY'; // Replace 'YOUR_PRIVATE_KEY_HERE' with the actual private key

function findInfoFromDecodedTxResponse(inputString) {
    // Regular expressions to extract function signature, argument type, argument value, value, and wei
    const signatureRegex = /function signature '(\w+)'/;
    const argumentTypeRegex = /providing a '(\w+)' argument/;
    const argumentValueRegex = /value: (\d+)/;
    const weiRegex = /wei: (\d+)/;

    // Extracting function signature, argument type, argument value, value, and wei
    const signatureMatch = inputString.match(signatureRegex);
    const argumentTypeMatch = inputString.match(argumentTypeRegex);
    const argumentValueMatch = inputString.match(argumentValueRegex);
    const weiMatch = inputString.match(weiRegex);

    if (signatureMatch && argumentTypeMatch && argumentValueMatch && weiMatch) {
        const functionSignature = signatureMatch[1];
        const argumentType = argumentTypeMatch[1];
        const argumentValue = argumentValueMatch[1];
        const wei = weiMatch[1];

        return {
            functionSignature: functionSignature,
            argumentType: argumentType,
            argumentValue: argumentValue,
            wei: wei
        };
    } else {
        return {
            error: "Some information not found in the decoded tx response."
        };
    }
}

async function callFunction() {
    try {
        const wallet = new ethers.Wallet(senderPrivateKey, provider);

        // 1st interaction: Call 0x.... function
        const firstCallData = firstFunctionSelector;
        const firstTx = {
            from: wallet.address,
            to: contractAddress,
            data: firstCallData,
        };
        const firstTxResult = await provider.call(firstTx);
        // Extract the argument and value from the response of the first call

        // Remove '0x' prefix
        const hexData = firstTxResult.slice(2);

        // Extract the offset (first 32 bytes)
        const offset = ethers.BigNumber.from('0x' + hexData.slice(0, 64)).toNumber();

        // Interpret the offset to determine the start of the actual data
        const dataStart = 2 + (offset * 2); // Multiply by 2 because each byte is represented by 2 characters in hexadecimal

        // Extract the actual data and strip leading zeroes
        const data = '0x' + hexData.slice(dataStart).replace(/^0+/, '');

        // Convert hexadecimal data to bytes
        const dataBytes = ethers.utils.arrayify(data);

        // Convert bytes to a readable string
        const decodedData = ethers.utils.toUtf8String(dataBytes, ethers.utils.Utf8ErrorFuncs.ignore);

        console.log(decodedData);

        const secondTxInfo = findInfoFromDecodedTxResponse(decodedData);

        console.log(secondTxInfo);
        // 2nd interaction: Use output from first call to send transaction
        if (!secondTxInfo.error) {
            const secondFunctionSelector = secondTxInfo.functionSignature;
            const secondFunctionArgValue = BigInt(secondTxInfo.argumentValue).toString();
            const secondFunctionCallData = secondFunctionSelector + ethers.utils.defaultAbiCoder.encode([secondTxInfo.argumentType], [secondFunctionArgValue]).slice(2);
            const secondFunctionWeiValue = secondTxInfo.wei
            const secondTx = {
                from: wallet.address,
                to: contractAddress,
                data: secondFunctionCallData,
                value: secondFunctionWeiValue
            };

            // console.log(secondTx)

            wallet.sendTransaction(secondTx).then(async (signedTx) => {
                // Send the signed transaction
                const txReceipt = await signedTx.wait();
                console.log("Transaction hash:", txReceipt.transactionHash);
            })
        }
        else {
            console.log(secondTxInfo.error)
        }


    } catch (error) {
        console.error('Error calling the function:', error);
    }
}

callFunction();

P.S., Some details, such as contract addresses, function signatures, and transaction hashes, have been removed from both the question and answer due to privacy concerns.

Related Topic