[Ethereum] How to generate solidity encode function signature from Javascript

ethereumjsjavascriptremixsolidityweb3js

What I am trying to achieve is like , i should be able to call a set of solidity functions dynamically based on some criteria. i'm using call() method to achieve the same.

(bool success, bytes memory result) =APContract.call(abi.encodeWithSignature(string(data), "hello"));

data variable contain the function call string "testCall(string)". instead of encoding from the contract I should be able to encode the same with javascript (because of some argument generation logic). so that i can call the function like APContract.call(data). Is there any method that help to achieve the same?

Best Answer

Not sure if I totally understood your question but you can take a look at the Solidity Documentation Contract ABI Specification: https://docs.soliditylang.org/en/latest/abi-spec.html#examples

To see how to encode the parameters on your own. It is basically a big concatenated strings set of function name hash and function arguments.

You can also take a look at this lib which performs "custom contract calls" (so not relying on any ABI): https://github.com/ERC725Alliance/erc725.js/blob/main/src/lib/utils.ts

Manual way

import Web3 from 'web3';

const web3 = new Web3();

const methodId = web3.utils.keccak256('testCall(string)').substr(0, 10); //0xc7cee1b7

// In this function call, the first and only parameter uses a dynamic type: string
// https://docs.soliditylang.org/en/latest/abi-spec.html#use-of-dynamic-types

// we use the offset in bytes to the start of their data area, measured from the start of the value encoding
// offset to start of data part of second parameter, 1*32 bytes, exactly the size of the head part

const stringArgument = web3.utils.padLeft(web3.utils.toHex(1*32), 64).replace('0x', '');
// 0000000000000000000000000000000000000000000000000000000000000020


const stringValue = 'TODO' // This one is a bit tricky, the docs explain it well: https://docs.soliditylang.org/en/latest/abi-spec.html#use-of-dynamic-types

const call = methodId + stringArgument + stringValue;

ABI way

You can always construct your own ABI dict, then web3 will handle the above conversion for you:

const contract = new web3.eth.Contract(
        [
            {
                name: 'testCall',
                inputs: [
                    { type: 'bytes32', name: '_hash', internalType: 'bytes32' },
                ],
            },
        ],
        contractAddress,
    );

const answer = await contract.methods
            .testCall(messageHash)
            .call();

ABI utils way

Use the helpers fonctions from: https://web3js.readthedocs.io/en/v1.3.0/web3-eth-abi.html to build what you need.

web3.eth.abi.encodeParameter('string', 'Hello')

Edit: you can also check web3 source code