2

Update

Since I'm not able to achieve this using the approach in this question, I created my own library to do the same thing (link). It doesn't rely on go-ethereum package but use the normal net/http package to do JSON RPC request.

I still love to know what I did wrong in my approach below.


Definitions:

  • owner = public variable in contract with address type
  • contract = smart-contract that has owner

This is the curl request to get the owner of a contract. I managed to get the owner. (JSON RPC docs)

curl localhost:8545 -X POST \
--header 'Content-type: application/json' \
--data '{"jsonrpc":"2.0", "method":"eth_call", "params":[{"to": "0x_MY_CONTRACT_ADDRESS", "data": "0x8da5cb5b"}, "latest"], "id":1}'

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

But when I try to replicate it in Golang (code below), I got json: cannot unmarshal string into Go value of type main.response error. (go-ethereum code that I use)

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/ethereum/go-ethereum/rpc"
)

func main() {
    client, err := rpc.DialHTTP(os.Getenv("RPC_SERVER"))
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    type request struct {
        To   string `json:"to"`
        Data string `json:"data"`
    }

    type response struct {
        Result string
    }

    req := request{"0x_MY_CONTRACT_ADDRESS", "0x8da5cb5b"}
    var resp response
    if err := client.Call(&resp, "eth_call", req, "latest"); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%v\n", resp)
}

What did I miss here?

Expected result:

Address in string format. E.g. 0x3ab17372b25154400738C04B04f755321bB5a94b

P/S — I'm aware of abigen and I know it's better and easier to do this using abigen. But I'm trying to solve this specific issue without using abigen method.

Zulhilmi Zainudin
  • 9,017
  • 12
  • 62
  • 98

4 Answers4

7

You can solve the problem best using the go-ethereum/ethclient:

package main

import (
    "context"
    "log"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
)

func main() {
    client, _ := ethclient.Dial("https://mainnet.infura.io")
    defer client.Close()

    contractAddr := common.HexToAddress("0xCc13Fc627EFfd6E35D2D2706Ea3C4D7396c610ea")
    callMsg := ethereum.CallMsg{
        To:   &contractAddr,
        Data: common.FromHex("0x8da5cb5b"),
    }

    res, err := client.CallContract(context.Background(), callMsg, nil)
    if err != nil {
        log.Fatalf("Error calling contract: %v", err)
    }
    log.Printf("Owner: %s", common.BytesToAddress(res).Hex())
}
user10595796
  • 134
  • 9
1

If you look at the client library code, you'll see that the JSON RPC response object is already disassembled and either an error is returned on failure, or the actual result parsed: https://github.com/ethereum/go-ethereum/blob/master/rpc/client.go#L277

The parser however already unwrapped the containing "result" field. Your type still wants to do an additional unwrap:

type response struct {
    Result string
}

Drop the outer struct, simply pass a string pointer to the client.Call's first parameter.

0

Your response struct doesn't show the data that the json of the response has

try this

type response struct {
    Jsonrpc string `json:"jsonrpc"`
    ID      int    `json:"id"`
    Result  string `json:"result"`
}
Vorsprung
  • 32,923
  • 5
  • 39
  • 63
0

json: cannot unmarshal string into Go value of type main.response error. I got similar type error when i was unmarshaling a response. It was because the response was actually json string, i mean it had Quotation " as first character. So to be sure you also encountered the same problem, please printf("%v",resp.Result) before unmarshaling in here https://github.com/ethereum/go-ethereum/blob/1ff152f3a43e4adf030ac61eb5d8da345554fc5a/rpc/client.go#L278.

nightfury1204
  • 4,364
  • 19
  • 22