2

I am writing a CLI in Go that should faciliate users to be able to make contract function calls from it. This entails generating contract ABIs and providing input parameters to the function arguments but requires knowledge of the argument type to be able to construct a transaction payload correctly. This payload becomes the data component of an Ethereum transaction to make a function call.

My CLI prompts for input which then needs to be converted to a specific type for that parameter as defined by its ABI.

// Compile solidity contract given a path to file
contract, err := compiler.CompileSolidity("", contractPath)
if err != nil {
    log.Fatal("ERROR failed to compile contract:", err)
}

path := strings.Split(contractPath, "/")
contractName := path[len(path)-1]

// Resulting compiled contract instance
compiledContract = contract[contractPath+":"+strings.Replace(contractName, ".sol", "", -1)]

// Retrieve the ABI definition as a JSON string from the compiled contract
abiStr, err := json.Marshal(compiledContract.Info.AbiDefinition)
if err != nil {
    log.Fatal("ERROR marshalling abi to string", err)
}

// Go format ABI from JSON
abiContract, err := abi.JSON(strings.NewReader(string(abiStr)))
if err != nil {
    log.Fatal("ERROR reading contract ABI ", err)
}

inputParameters := abi.Methods[methodName].Inputs
// Gets input parameters to named method as strings
args := getArguments(inputParameters)

payload, err := abiContract.Pack(methodName, args...)
if err != nil {
    log.Fatal("ERROR packing the method name for the contract call: ", err)
}

As shown above, a contract instance is compiled and its generated ABI used to discern the method metadata. For a given methodName, a Method contains Inputs as type Arguments which contain information for each input argument including parameter name and type. This code is here.

Above, args is an array of string that have been input as parameters to the methodName function call but need to be casted to the correct type before abiContract.Pack where it panics due to type mismatch of provided parameters vs the expected type as defined by the ABI.

Given a variable of type string and another variable of type abi.Type how can I perform a type conversion to convert the string variable into the type defined by the abi.Type variable?

0

abi.Pack rest arguments must be of type interface{} so create an []interface{} to hold your values in order to spread them:

args := []string{"foo"}
var iargs []interface{}
for _, arg := range args {
    iargs = append(iargs, args)
}

packed, err := abiContract.Pack(methodName, iargs...)
if err != nil {
    panic(err)
}

fmt.Println(packed)

Here's a full example demonstrating how to use Pack and Unpack:

Store.sol (example smart contract)

pragma solidity ^0.4.24;

contract Store {
  mapping (bytes32 => bytes32) public items;

  function setItem(bytes32 key, bytes32 value) external {
    items[key] = value;
  }
}

main.go (full example code)

package main

import (
    "context"
    "fmt"
    "log"
    "strings"

    ethereum "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/accounts/abi"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"

    store "./contracts" // where the abigen compiled contract is
)

func main() {
    client, err := ethclient.Dial("https://rinkeby.infura.io")
    if err != nil {
        log.Fatal(err)
    }

    abiContract, err := abi.JSON(strings.NewReader(string(store.StoreABI)))
    if err != nil {
        panic(err)
    }

    var key [32]byte
    copy(key[:], []byte("foo"))
    iargs := []interface{}{key}

    packed, err := abiContract.Pack("items", iargs...)
    if err != nil {
        panic(err)
    }

    fmt.Println(packed) // [72 243 67 243 102 111 111 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

    contractAddress := common.HexToAddress("0x147b8eb97fd247d06c4006d269c90c1908fb5d54")

    msg := ethereum.CallMsg{
        To:   &contractAddress,
        Data: packed,
    }

    output, err := client.CallContract(context.Background(), msg, nil)
    if err != nil {
        panic(err)
    }

    var item [32]byte
    err = abiContract.Unpack(&item, "items", output)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(item[:])) // "bar"
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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