Deploy smart contract and connect with go Ethereum

In this article, I am going to deploy a smart contract to go and go-Ethereum on the local block chain.

Go-ethereum

Go, a programming language developed by Google, is one of the languages used to build the Ethereum network, and Go Ethereum is the interface used to write, edit and manipulate the Go code for the blockchain. Aside from Go, the Ethereum blockchain and protocol are also heavily based on Python and C++.

USDT smart contract

Proof-of-work (PoW)

The Ethereum network began by using a consensus mechanism that involved Proof-of-work (PoW). This allowed the nodes of the Ethereum network to agree on the state of all information recorded on the Ethereum blockchain and prevented certain kinds of economic attacks. However, Ethereum switched off proof-of-work in 2022 and started using proof-of-stake instead.

Proof-of-work has now been deprecated. Ethereum no longer uses proof-of-work as part of its consensus mechanism. Instead, it uses proof-of-stake. Read more on proof-of-stake and staking.

Proof-of-stake (PoS)

Proof-of-stake (PoS) underlies Ethereum’s consensus mechanism. Ethereum switched on its proof-of-stake mechanism in 2022 because it is more secure, less energy-intensive, and better for implementing new scaling solutions compared to the previous proof-of-work architecture.

Proof-of-stake is a way to prove that validators have put something of value into the network that can be destroyed if they act dishonestly. In Ethereum’s proof-of-stake, validators explicitly stake capital in the form of ETH into a smart contract on Ethereum. The validator is then responsible for checking that new blocks propagated over the network are valid and occasionally creating and propagating new blocks themselves. If they try to defraud the network (for example by proposing multiple blocks when they ought to send one or sending conflicting attestations), some or all of their staked ETH can be destroyed.

Get project

bash
git clone
https://github.com/owanhunte/ethereum-solidity-course-updated-code.git
cd lottery
text
node -vv18.19.0npm init

lottery.sol file

solidity
pragma solidity >=0.5.0 <0.9.0;
contract Lottery {
  address public manager;
  address payable[] public players;
  constructor() {
    manager = msg.sender;
  }
  function enter() public payable {
    require(msg.value >=.01 ether,
    "A minimum payment of .01 ether must be sent to enter the lottery");
    players.push(payable(msg.sender));
  }
  function random() private view returns (uint256) {
    return uint256(keccak256(abi.encodePacked(block.prevrandao, block.number, players)));
  }
  function pickWinner() public onlyOwner {
    uint256 index = random() % players.length;
    address contractAddress = address(this);
    players[index].transfer(contractAddress.balance);
    players = new address payable[](0);
  }
  function getPlayers() public view returns (address payable[] memory) {
    return players;
  }
  modifier onlyOwner() {
    require(msg.sender == manager, "Only owner can call this function.");
    _;
  }
}

Install dependency

bash
npm install solc
npm install @truffle/hdwallet-provider
npm install --save mocha ganache-cli web3
npm update
npm install solc@0.8.23 //deploy on infura

We need to change in package.json

text
"scripts": {    "test": "mocha"  },

Run the tests

bash
npm run test
Deploy smart contract and connect with go Ethereum

Deploy Contract

set .env file

dotenv
A
C
C
O
U
N
T_
M
N
E
M
O
N
I
C="neutral ... "
G
O
E
R
L
I_
E
N
D
P
O
I
N
T="https://sepolia.infura.io/v3/code"

Run the deploy.js

text
node deploy.js

Result

text
Attempting to deploy from account 0x6e06Dc176Ee86DCb3C428a42Ed9AAeD9c0Eb1935Contract deployed to
0xc7e367A2EaE77148459b048ba3a3f6288EB82832

TXhash:

0xe87abc8cf10bde4395220b2c2eed29e378be3f425719354dc1c65ced9a7dd03d

Go-Ethereum

popularly referred to as Geth, is the official Ethereum client for building decentralized applications using the Go programming language. Geth is one of the preferred alternatives for running, setting up nodes, and interacting with Ethereum blockchains due to its ease of use.

bash
go get github.com/ethereum/go-ethereum
go
package main
import (
"context"
"fmt"
"log"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)
var (contractAddress = common.HexToAddress("0xc7e367A2EaE77148459b048ba3a3f6288EB82832") myAddress =
common.HexToAddress("0x6e06Dc176Ee86DCb3C428a42Ed9AAeD9c0Eb1935")
)
func main() {
    client, err := ethclient.Dial("https://sepolia.infura.io/v3/code") if err != nil {
        log.Fatal(err)
    }
    networkID, err := client.NetworkID(context.Background()) if err != nil {
        log.Fatal(err)
    }
    fmt.Println(networkID) balance, err := client.BalanceAt(context.Background(), contractAddress,
    nil) if err != nil {
        log.Fatal(err)
    }
    fmt.Println(balance) myBalance, err := client.BalanceAt(context.Background(), myAddress, nil) if
    err != nil {
        log.Fatal(err)
    }
    fmt.Println(myBalance)
}

You can connect to the infura gateway if you don’t have an existing client. Infura manages a bunch of Ethereum [geth and parity] nodes that are secure, reliable, scalable and lowers the barrier to entry for newcomers when it comes to plugging into the Ethereum network.

go
‍‍‍client, err := ethclient.Dial("
https://mainnet.infura.io")

Using Ganache

Ganache is an Ethereum simulator that makes developing Ethereum applications faster, easier, and safer. It includes all popular RPC functions and features (like events) and can be run deterministically to make development a breeze.

You must first install Node.js >= v16.0.0 and npm >= 7.10.0.

bash
sudo
npm install -g ganache-cli
text
ganache
text
ganache-cli

Connect to Ganache

go
client, err := ethclient.Dial("
http://localhost:8545")if err != nil {  log.Fatal(err)}
text
ganache-cli -m "much repair shock carbon improve miss forget sock include bullet interest solution"

Download UI Ganache

text
https://trufflesuite.com/ganache/
bash
wget
https://github.com/trufflesuite/ganache-ui/releases/download/v2.7.1/ganache-2.7.1-linux-x86_64.AppImage
chmod u+x <AppImage File>./exampleName.AppImagecd ~; ./ganache-2.7.1-linux-x86_64.AppImage
go
dlopen(): error loading libfuse.so.2sudo apt install libfuse2

Golang codes

go
address = "http://127.0.0.1:7545" client, err := ethclient.Dial(address) if err != nil {
log.Fatal(err) }

Deploy on Ganache-cli

properties
GOERLI_ENDPOINT="
http://localhost:8545"
text
ganache-cli -m "neutral ..."
text
node deploy.js
text
Attempting to deploy from account 0x6e06Dc176Ee86DCb3C428a42Ed9AAeD9c0Eb1935[
'0x6e06Dc176Ee86DCb3C428a42Ed9AAeD9c0Eb1935']Contract deployed to
0xf6e2551d2AFaFBA200D3803aE6a72B515Fa9f825

In Ganache Terminal

text
Transaction: 0x40fb2033ec7b4c8a9c95945d7f3b83f9b73405eb43723cc7e4729d12a225b673  Contract created:
0xf6e2551d2afafba200d3803ae6a72b515fa9f825  Gas usage: 646841  Block number: 1  Block time: Mon Dec
18 2023 16:58:05 GMT

Account Balances

Reading the balance of an account is pretty simple; call the BalanceAt method of the client passing it the account address and optional block number. Setting nil as the block number will return the latest balance.

javascript
var (contractAddress = common.HexToAddress("0xf6e2551d2afafba200d3803ae6a72b515fa9f825") myAddress =
common.HexToAddress("0x6e06Dc176Ee86DCb3C428a42Ed9AAeD9c0Eb1935"))
go
account := common.HexToAddress(myAddress)balance, err := client.BalanceAt(context.Background(),
account, nil)if err != nil {  log.Fatal(err)}fmt.Println(balance) // 999997816911625000000

Get balance on blockchain 1

go
blockNumber := big.NewInt(1) balance, err = client.BalanceAt(context.Background(), myAddress,
blockNumber) if err != nil {  log.Fatal(err) }

Because we’re dealing with big numbers we’ll have to import the native Go math and math/big packages

Pending balance

‍‍‍Sometimes you’ll want to know what the pending account balance is, for example after submitting or waiting for a transaction to be confirmed. The client provides a similar method to BalanceAt called PendingBalanceAt which accepts the account address as a parameter.

go
pendingBalance, err := client.PendingBalanceAt(context.Background(), myAddress)
fmt.Println(pendingBalance) // 25729324269165216042

Block header

You can call the client’s HeaderByNumber to return header information about a block. It'll return the latest block header if you pass nil.

go
header, err := client.HeaderByNumber(context.Background(), nil)if err != nil {
log.Fatal(err)}fmt.Println(header.Number.String()) // 1

Full block

Call the client’s BlockByNumber method to get the full block. You can read all the contents and metadata of the block such as block number, block timestamp, block hash, block difficulty, as well as the list of transactions and much much more.

Count Block

go
count, err := client.TransactionCount(context.Background(), block.Hash()) if err != nil {
log.Fatal(err) } fmt.Println(count)

Transaction data

go
for _, tx := range block.Transactions() {  fmt.Println(tx.Hash().Hex())
fmt.Println(tx.Value().String())      fmt.Println(tx.Gas())
fmt.Println(tx.GasPrice().Uint64())   fmt.Println(tx.Nonce())              }
text
___________________________Transaction0x40fb2033ec7b4c8a9c95945d7f3b83f9b73405eb43723cc7e4729d12a225
b673064684145000000000

Smart Contract Compilation & ABI

Solc is available as a snapcraft package for Ubuntu.

bash
sudo snap install solc --edge

‍‍‍‍‍‍We also need to install a tool called abigen for generating the ABI from a solidity smart contract.

bash
go get -u github.com/ethereum/go-ethereum
cd $GOPATH/src/github.com/ethereum/go-ethereum/make
make
devtools

Abigen

Abigen is a binding-generator for easily interacting with Ethereum using Go. Abigen creates easy-to-use, type-safe Go packages from Ethereum smart contract definitions known as ABIs. This abstracts away a lot of the complexity of handling smart contract deployment and interaction in Go native applications such as encoding and decoding smart contracts into EVM bytecode. Abigen comes bundled with Geth. A full Geth installation includes the abigen binary. Abigen can also be built independently by navigating to go-ethereum/cmd/abigen and running go build, or equivalently:

text
$ cd $GOPATH/src/github.com/ethereum/go-ethereum$ go build ./cmd/abigen

Use smart contract in Golang

Make ABI from contract.

text
solc --abi Lottery.sol
go
======= Lottery.sol:Lottery =======Contract JSON
ABI[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"enter","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getPlayers","outputs":[{"internalType":"address payable[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"manager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pickWinner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"players","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

Or build in file :

text
solc --abi Storage.sol -o build

The contract binding can then be generated by passing the ABI to abigen as follows:

go
abigen --abi ./build/Lottery.abi --pkg main --type Lottery --out Lottery.go

In order to deploy a smart contract from Go, we also need to compile the solidity smart contract to EVM bytecode. The EVM bytecode is what will be sent in the data field of the transaction. The bin file is required for generating the deploy methods on the Go contract file.

text
solc --bin Lottery.sol
text
======= Lottery.sol:Lottery
=======Binary:608060405234801561000f575f80fd5b50335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610a7f8061005c5f395ff3fe608060405260043610610049575f3560e01c8063481c6a751461004d5780635d495aea146100775780638b5b9ccc1461008d578063e97dcb62146100b7578063f71d96cb146100c1575b5f80fd5b348015610058575f80fd5b506100616100fd565b60405161006e919061054c565b60405180910390f35b348015610082575f80fd5b5061008b610120565b005b348015610098575f80fd5b506100a16102c5565b6040516100ae919061062d565b60405180910390f35b6100bf610350565b005b3480156100cc575f80fd5b506100e760048036038101906100e29190610684565b6103fc565b6040516100f491906106be565b60405180910390f35b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146101ad576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101a490610757565b60405180910390fd5b5f6001805490506101bc610437565b6101c691906107a2565b90505f309050600182815481106101e0576101df6107d2565b5b905f5260205f20015f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc8273ffffffffffffffffffffffffffffffffffffffff163190811502906040515f60405180830381858888f19350505050158015610261573d5f803e3d5ffd5b505f67ffffffffffffffff81111561027c5761027b6107ff565b5b6040519080825280602002602001820160405280156102aa5781602001602082028036833780820191505090505b50600190805190602001906102c092919061046b565b505050565b6060600180548060200260200160405190810160405280929190818152602001828054801561034657602002820191905f5260205f20905b815f9054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116102fd575b5050505050905090565b662386f26fc1000034101561039a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103919061089c565b60405180910390fd5b600133908060018154018082558091505060019003905f5260205f20015f9091909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b6001818154811061040b575f80fd5b905f5260205f20015f915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b5f4443600160405160200161044e939291906109ea565b604051602081830303815290604052805190602001205f1c905090565b828054828255905f5260205f209081019282156104e1579160200282015b828111156104e0578251825f6101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555091602001919060010190610489565b5b5090506104ee91906104f2565b5090565b5b80821115610509575f815f9055506001016104f3565b5090565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6105368261050d565b9050919050565b6105468161052c565b82525050565b5f60208201905061055f5f83018461053d565b92915050565b5f81519050919050565b5f82825260208201905092915050565b5f819050602082019050919050565b5f6105988261050d565b9050919050565b6105a88161058e565b82525050565b5f6105b9838361059f565b60208301905092915050565b5f602082019050919050565b5f6105db82610565565b6105e5818561056f565b93506105f08361057f565b805f5b8381101561062057815161060788826105ae565b9750610612836105c5565b9250506001810190506105f3565b5085935050505092915050565b5f6020820190508181035f83015261064581846105d1565b905092915050565b5f80fd5b5f819050919050565b61066381610651565b811461066d575f80fd5b50565b5f8135905061067e8161065a565b92915050565b5f602082840312156106995761069861064d565b5b5f6106a684828501610670565b91505092915050565b6106b88161058e565b82525050565b5f6020820190506106d15f8301846106af565b92915050565b5f82825260208201905092915050565b7f4f6e6c79206f776e65722063616e2063616c6c20746869732066756e6374696f5f8201527f6e2e000000000000000000000000000000000000000000000000000000000000602082015250565b5f6107416022836106d7565b915061074c826106e7565b604082019050919050565b5f6020820190508181035f83015261076e81610735565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f6107ac82610651565b91506107b783610651565b9250826107c7576107c6610775565b5b828206905092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f41206d696e696d756d207061796d656e74206f66202e3031206574686572206d5f8201527f7573742062652073656e7420746f20656e74657220746865206c6f7474657279602082015250565b5f6108866040836106d7565b91506108918261082c565b604082019050919050565b5f6020820190508181035f8301526108b38161087a565b9050919050565b5f819050919050565b6108d46108cf82610651565b6108ba565b82525050565b5f81549050919050565b5f81905092915050565b5f819050815f5260205f209050919050565b6109098161058e565b82525050565b5f61091a8383610900565b60208301905092915050565b5f815f1c9050919050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f61096261095d83610926565b610931565b9050919050565b5f6109748254610950565b9050919050565b5f600182019050919050565b5f610991826108da565b61099b81856108e4565b93506109a6836108ee565b805f5b838110156109dd576109ba82610969565b6109c4888261090f565b97506109cf8361097b565b9250506001810190506109a9565b5085935050505092915050565b5f6109f582866108c3565b602082019150610a0582856108c3565b602082019150610a158284610987565b915081905094935050505056fea264697066735822122002a23570c68d06ef9b64bc5066fb949367588412ac467a89d0e3d8098007384864736f6c637829302e382e32342d646576656c6f702e323032332e31322e31352b636f6d6d69742e3162356336663636005a

Deploying contracts to Ethereum

text
solc --bin Lottery.sol -o Lottery.bin

Then abigen can be run again, this time passing Storage.bin:

go
abigen --abi ./build/Lottery.abi --pkg main --type Lottery --out Lottery.go --bin
./Lottery.bin/Lottery.bin

The new DeployLottery() function can be used to deploy the contract to an Ethereum testnet from a Go application. To do this requires incorporating the bindings into a Go application that also handles account management, authorization and Ethereum backend to deploy the contract through. Specifically, this requires:

  1. A running Geth node connected to an Ethereum testnet
  2. An account in the keystore prefunded with enough ETH to cover gas costs for deploying and interacting with the contract

Use Private key in Ganache-cli

text
ganache-cli -m "neutral ..."
Deploy smart contract and connect with go Ethereum
go
address := "http://localhost:8545" // ganache cli client, err := ethclient.Dial(address) if err !=
nil {  log.Fatal(err) } privateKey, err :=
crypto.HexToECDSA("872e066455dcfb1e38e1c6109f3522f2d2344e3b58fbfbe0b47803c971784760") if err != nil
{  log.Fatal(err) } nonce, err := client.PendingNonceAt(context.Background(),
crypto.PubkeyToAddress(privateKey.PublicKey)) if err != nil {  log.Fatal(err) } gasPrice, err :=
client.SuggestGasPrice(context.Background()) if err != nil {  log.Fatal(err) }
fmt.Println("gasPrice: ", gasPrice) chainID, err := client.ChainID(context.Background())
fmt.Println("networkID :", chainID) auth, err := bind.NewKeyedTransactorWithChainID(privateKey,
chainID) if err != nil {  fmt.Println(err) } auth.Nonce = big.NewInt(int64(nonce)) auth.Value =
big.NewInt(0)      // in wei auth.GasLimit = uint64(300000) // in units auth.GasPrice = gasPrice
txAddress, tx, instance, err := contracts.DeployLottery(auth, client) if err != nil {
fmt.Println("DeployLottery")  log.Fatal(err.Error()) } fmt.Println(instance, address, tx, instance)
fmt.Println(txAddress.Hex()) // 0x147B8eb97fD247D06C4006D269c90C1908Fb5D54
fmt.Println(tx.Hash().Hex())
go
fmt.Println(txAddress.Hex())
fmt.Println(tx.Hash().Hex())0xf6e2551d2AFaFBA200D3803aE6a72B515Fa9f8250x81ea1d8c480a316f5721edf31a3c1f39f3827b5cc75e9b6fcad928c3e261dd08
text
Transaction: 0x81ea1d8c480a316f5721edf31a3c1f39f3827b5cc75e9b6fcad928c3e261dd08  Contract created:
0xf6e2551d2afafba200d3803ae6a72b515fa9f825  Gas usage: 300000  Block number: 1  Block time: Tue Dec
19 2023 14:10:57   Runtime error: code size to deposit exceeds maximum code size

Loading a Smart Contract

Once you’ve compiled your smart contract’s ABI to a Go package using the abigen tool, the next step is to call the "New" method, which is in the format New<ContractName>, so in our example if you recall it's going to be NewStore. This initializer method takes in the address of the smart contract and returns a contract instance that you can start interact with it.

text
Transaction: 0x25b79f942ebccfae067bb94a0e76282e7c1c2db825e5cbee18da1bb487596e06
go
address := "http://localhost:8545" // ganache cli client, err := ethclient.Dial(address) if err !=
nil {  log.Fatal(err) } contractAddress :=
common.HexToAddress("0x25b79f942ebccfae067bb94a0e76282e7c1c2db825e5cbee18da1bb487596e06") instance,
err := contracts.NewLottery(contractAddress, client) if err != nil {  log.Fatal(err) }

Writing to a Smart Contract

Contract transaction:

text
Transaction: 0x25b79f942ebccfae067bb94a0e76282e7c1c2db825e5cbee18da1bb487596e06

Use privates keys in ganache:

Deploy smart contract and connect with go Ethereum
go
address := "http://localhost:8545" // ganache cli client, err := ethclient.Dial(address) if err !=
nil {  log.Fatal(err) } privateKey, err :=
crypto.HexToECDSA("872e066455dcfb1e38e1c6109f3522f2d2344e3b58fbfbe0b47803c971784760") 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) } fmt.Println(gasPrice)
chainID, err := client.ChainID(context.Background()) if err != nil {  log.Fatal(err) }
fmt.Println("networkID :", chainID) auth, err := bind.NewKeyedTransactorWithChainID(privateKey,
chainID) if err != nil {  log.Fatal(err) } auth.Nonce = big.NewInt(int64(nonce)) auth.Value =
big.NewInt(0)      // in wei auth.GasLimit = uint64(3000000) // in units auth.GasPrice = gasPrice
contractAddress :=
common.HexToAddress("0x25b79f942ebccfae067bb94a0e76282e7c1c2db825e5cbee18da1bb487596e06") balance,
err := client.BalanceAt(context.Background(), contractAddress, nil) if err != nil {  log.Fatal(err)
} fmt.Println("balance : ", balance) myAddress :=
common.HexToAddress("0x6e06Dc176Ee86DCb3C428a42Ed9AAeD9c0Eb1935") myBalance, err :=
client.BalanceAt(context.Background(), myAddress, nil) if err != nil {  log.Fatal(err) }
fmt.Println("myBalance : ", myBalance) instance, err := contracts.NewLottery(contractAddress,
client) if err != nil {  fmt.Println("NewLottery")  log.Fatal(err) } tx, err := instance.Enter(auth)
if err != nil {  fmt.Println("Enter")  log.Fatal(err) }
bash
go run *.go2000000000networkID : 1337balance :  0myBalance :  999997000000000000000
text
Transaction: 0xa531650fe246012062408f17e4d56ace4622a4680ac799279262c60666201fe5  Gas usage: 21064
Block number: 6  Block time: Tue Dec 19 2023 15:18:07 GMT