2

I am trying to write a script to call the following method:

    /// @notice Creates a proposal
    /// @param _targets The target addresses to call
    /// @param _values The ETH values of each call
    /// @param _calldatas The calldata of each call
    /// @param _description The proposal description
    function propose(
        address[] memory _targets,
        uint256[] memory _values,
        bytes[] memory _calldatas,
        string memory _description
    ) external returns (bytes32)

the third argument requires an array of bytes representing calldata from a function of the _targets argument. The target function I am trying to call for the calldata requires the following:

  function addProperties(
        string[] calldata _names,
        ItemParam[] calldata _items,
        IPFSGroup calldata _ipfsGroup
    )

how can I format calldata for the target function addProperties() and use as an argument in the first function propose()?

2 Answers 2

0

solidity-gencall

This is a nodejs tool I maintain that will take a ABI file and load a menu of functions it can encode.

npm i -g solidity-gencall && solgen --version

solgen will recognize your function's and provide a menu for you, e.g.

$ solgen
  gencall shell utility
? Path to artifact › build/contracts/YourContractABI.json

Example use

? Select Function

WETH()
router()
permitAndCall(address,uint256,bytes4,bytes,bytes4,bytes)

Configuration File

.env
MNEMONIC=your-mneomic-phrase
RPC_ENDPOINT=your json rpc provider endpoint

There is also Foundry's cast tool

Foundry Cast

Cast[^1] is a CLI tool for making RPC calls to Ethereum Virtual Machine (EVM) compatible blockchains. Using cast, we can make contract calls, query data, and handle encoding and decoding.

Check the WETH9 balance of an address. The signature of the function is balanceOf(address)[^2]. To find the ID of this function (its selector), we must perform a hash to it, and then take the first four bytes:

$ cast keccak "balanceOf(address)"| cut -b 1-10
0x70a08231

To pass this to an address, we must append it to the function selector, add a then left pad it up to 32 digits since addresses take 32 bytes in function call data:

0x70a08231000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266

Next, we execute eth_call JSON-RPC method to make the call. Notice that this doesn’t require sending a transaction–this endpoint is used to read data from contracts.

$ params='{"from":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266","to":"0xe7f1725e7734ce288f8367e1bb143e90bb3f0512","data":"0x70a08231000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266"}'

Example RPC Request (data omitted)

$ curl -X POST -H 'Content-Type: application/json' \
  --data '{"id":1,"jsonrpc":"2.0","method":"eth_call","params":['"$params"',"latest"]}' \
  http://127.0.0.1:8545

Response from RPC Endpoint:

{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000011153ce5e56cf880000"}

Ethereum nodes return results as raw bytes, to parse them we need to know the type of a returned value. In the case of balanceOf function, the type of a returned value is uint256. Using cast, we can convert it to a decimal number and then convert it to ethers-js:

$ cast --to-dec 0x00000000000000000000000000000000000000000000011153ce5e56cf880000| cast --from-wei
> 5042.000000000000000000

Footnotes

[^1]: Foundry is a rust re-write of dapptools. Cast is a rust implementation of Dapptools' Seth utility.

[^2]: As defined in ERC-20, https://eips.ethereum.org/EIPS/eip-20

Foundry Cast example sourced from: https://uniswapv3book.com/docs/milestone_1/deployment/

Foundry Docs: https://book.getfoundry.sh/

Foundry-Rs repo: https://github.com/foundry-rs/foundry

0

Here's a starting point.

In solidity:

bytes cdata = abi.encodeWithSignature("transfer(address,uint)", to, 1 ether);

In ethers.js:

let ABI = ["function transfer(address to, uint amount)"];
const iface = new ethers.utils.Interface(ABI);
const cdata = iface.encodeFunctionData("transfer", [to, parseEther("1.0")]);

Of course, if you already have a contract factory C then you can just use its interface, i.e. C.interface instead of creating a new one as above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.