1

I'm working on moving a node project of ours over to a go project and I've run into an issue:

web3.eth.defaultAccount = web3.eth.accounts[0];

In node using web3 this is all I had to do for every transaction to go out as that address. No private key, no passphrase. However in go I'm running in to an issue when building TransactOpts for my contract calls:

opts := bind.TransactOpts{
    Context:  context.Background(),
    From:     fromAddress,
    GasLimit: big.NewInt(1000000),
    GasPrice: big.NewInt(0),
}

Here I specify the From but since I don't specify the Signer property I get an error no signer to authorize the transaction with and sure enough this page marks the Signer field as mandatory.

How, from go, should I be making my transactional contract calls? Ideally without private keys and passphrases being accessed.

EDIT: As I looked more into how Web3 does what it does, I realized that it was part of our process to leave that address unlocked. It was part of our script to mine to unlock that address until further notice. If Node still requires the address to be unlocked then I see less of an issue having to unlock it in Go. Unless I find a better way, I'll be using the keystore object to unlock an account and construct a transactor.

1 Answer 1

2

You don't need to load a private key for making smart contract read-only calls, but since you're trying to send a transaction, here's a full example of how you'd do it using the go-ethereum package to generate and sign the transaction using the smart contract bindings generated by the abigen tool.

Store.sol

pragma solidity ^0.4.24;

contract Store {
  event ItemSet(bytes32 key, bytes32 value);

  string public version;
  mapping (bytes32 => bytes32) public items;

  constructor(string _version) public {
    version = _version;
  }

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

Compilation

solc --abi Store.sol | awk '/JSON ABI/{x=1;next}x' > Store.abi
solc --bin Store.sol | awk '/Binary:/{x=1;next}x' > Store.bin
abigen --bin=Store.bin --abi=Store.abi --pkg=store --out=Store.go

main.go

package main

import (
    "fmt"
    "log"

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

    store "./contracts" // for demo
)

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

    privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19")
    if err != nil {
        log.Fatal(err)
    }

    publicKey := privateKey.Public()
    publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
    if !ok {
        log.Fatal("error casting public key to ECDSA")
    }

    fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
    nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
    if err != nil {
        log.Fatal(err)
    }

    gasPrice, err := client.SuggestGasPrice(context.Background())
    if err != nil {
        log.Fatal(err)
    }

    auth := bind.NewKeyedTransactor(privateKey)
    auth.Nonce = big.NewInt(int64(nonce))
    auth.Value = big.NewInt(0)     // in wei
    auth.GasLimit = uint64(300000) // in units
    auth.GasPrice = gasPrice

    address := common.HexToAddress("0x147B8eb97fD247D06C4006D269c90C1908Fb5D54")
    instance, err := store.NewStore(address, client)
    if err != nil {
        log.Fatal(err)
    }

    key := [32]byte{}
    value := [32]byte{}
    copy(key[:], []byte("foo"))
    copy(value[:], []byte("bar"))

    tx, err := instance.SetItem(auth, key, value)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("tx sent: %s", tx.Hash().Hex()) // tx sent: 0x8d490e535678e9a24360e955d75b27ad307bdfb97a1dca51d0f3035dcee3e870

    result, err := instance.Items(nil, key)
    if err != nil {
        log.Fatal(err)
    }

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

Check out the Ethereum Development with Go guide book for more examples.

Your Answer

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

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