Abigen
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 can run.
# solc --version
solc, the solidity compiler commandline interface
Version: 0.8.9+commit.e5eed63a.Linux.g++
Build abigen.
# cd ~/go/src/github.com/MetalBlockchain/metalgo
# go build -o abigen cmd/abigen/main.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 metalgo
, 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/MetalBlockchain/metalgo/utils/constants"
"github.com/MetalBlockchain/metalgo/utils/formatting"
"github.com/MetalBlockchain/coreth/accounts/abi/bind"
"github.com/MetalBlockchain/coreth/core/types"
"github.com/MetalBlockchain/coreth/ethclient"
"github.com/MetalBlockchain/coreth/params"
"github.com/MetalBlockchain/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)
}