logoDocumentation

Interact from Golang App

Interact with a smart contract from your Golang-based application.

abigen is a tool provided by the Go Ethereum (Geth) client that generates Go bindings for Solidity smart contracts. Developers would need to use Abigen when they want to interact with a smart contract written in Solidity from a Go programming language application. It enables developers to easily call functions and access data from Solidity contracts in a Go application. This tutorial demonstrates how to compile a solidity contract into Golang to deploy and call contracts programmatically.

How to Build

Download the solidity compiler from solc-bin.

Copy the appropriate compiler into your current path. ~/bin/ is a common path in most Linux distributions.

cp linux-amd64/solc-linux-amd64-v0.8.9+commit.e5eed63a ~/bin

Ensure solc is properly installed by checking its version.

solc --version

Clone Coreth and Build Abigen.

git clone [email protected]:ava-labs/coreth.git
cd coreth/
go build -o abigen cmd/abigen/*.go
cp abigen ~/bin

Compile a contract.

abigen --sol counter.sol --pkg main --out counter.go

This will produce counter.go suitable to interact with contract.

Example Code

Setup the connection to avalanchego, then deploy, call, and fetch values from the contract.

Abigen offers more features for complicated contracts, the following is provided as an example to get started using the basic contract

package main
 
import (
	"context"
	"log"
	"math/big"
	"strings"
	"time"
 
	"github.com/ava-labs/avalanchego/utils/constants"
	"github.com/ava-labs/avalanchego/utils/formatting"
	"github.com/ava-labs/coreth/accounts/abi/bind"
	"github.com/ava-labs/coreth/core/types"
	"github.com/ava-labs/coreth/ethclient"
	"github.com/ava-labs/coreth/params"
	"github.com/ava-labs/coreth/rpc"
	"github.com/decred/dcrd/dcrec/secp256k1/v3"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
)
 
func main() {
	// setup client
	rc, err := rpc.Dial("http://localhost:9650/ext/bc/C/rpc")
	if err != nil {
		log.Fatal(err)
	}
	ec := ethclient.NewClient(rc)
 
	ctx := context.Background()
 
	// fetch networkid
	networkId, err := ec.ChainID(ctx)
	if err != nil {
		log.Fatal(err)
	}
 
	// parse key
	privateKeyString := "PrivateKey-ewoqjP7PxY4yr3iLTpLisriqt94hdyDFNgchSxGGztUrTXtNN"
	privateKeyBytes, err := formatting.Decode(formatting.CB58, strings.TrimPrefix(privateKeyString, constants.SecretKeyPrefix))
	if err != nil {
		log.Fatal(err)
	}
	privateKey := secp256k1.PrivKeyFromBytes(privateKeyBytes)
	privateKeyECDSA := privateKey.ToECDSA()
 
	// derive 'c' address
	cAddress := crypto.PubkeyToAddress(privateKeyECDSA.PublicKey)
 
	// setup signer and transaction options.
	signer := types.LatestSignerForChainID(networkId)
	to := &bind.TransactOpts{
		Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) {
			return types.SignTx(transaction, signer, privateKeyECDSA)
		},
		From:     cAddress,
		Context:  ctx,
		GasLimit: params.ApricotPhase1GasLimit,
	}
 
	// deploy the contract
	storageAddress, storageTransaction, storageContract, err := DeployStorage(to, ec)
	if err != nil {
		log.Fatal(err)
	}
 
	// wait for the transaction to be accepted
	for {
		r, err := ec.TransactionReceipt(ctx, storageTransaction.Hash())
		if err != nil {
			if err.Error() != "not found" {
				log.Fatal(err)
			}
			time.Sleep(1 * time.Second)
			continue
		}
		if r.Status != 0 {
			break
		}
		time.Sleep(1 * time.Second)
	}
 
	log.Println("storageAddress", storageAddress)
	log.Println("storageTransaction", storageTransaction)
 
	// Call store on the contract
	storeTransaction, err := storageContract.Store(to, big.NewInt(1), common.BytesToAddress([]byte("addr1")))
	if err != nil {
		log.Fatal(err)
	}
 
	// wait for the transaction
	for {
		r, err := ec.TransactionReceipt(ctx, storeTransaction.Hash())
		if err != nil {
			if err.Error() != "not found" {
				log.Fatal(err)
			}
			time.Sleep(1 * time.Second)
			continue
		}
		if r.Status != 0 {
			break
		}
		time.Sleep(1 * time.Second)
	}
 
	log.Println("storeTransaction", storeTransaction)
 
	// setup call options for storage
	co := &bind.CallOpts{
		Accepted: true,
		Context:  ctx,
		From:     storageAddress,
	}
 
	// retrieve the value of the contract
	storageValue, err := storageContract.Retrieve(co)
	if err != nil {
		log.Fatal(err)
	}
 
	log.Println("storageValue", storageValue)
}

Last updated on

On this page

Edit on Github