Blockchain on Golang Part 5 : Smart Contract Deploy

Ajiyba Nesij Korkmaz
4 min readJun 27, 2023

This document describes the process of deploying a smart contract. The process involves three steps: initializing the network and importing a wallet, preparing transaction configurations, and deploying the smart contract. The main function of the code snippet sets up the Avalanche Fuji test network, imports a wallet with a private key, prepares transaction options, deploys a TestToken contract, and returns the contract address and transaction hash.

If you want to see the necessary steps for compiling a smart contract, you can refer to the previous article.

Step 1 : Init Network and Import wallet

We will use the methods prepared in Step 1 and Step 2.

The InitNetwork function allows us to connect to the specified blockchain network provided as a parameter. Assigning it to the globally defined client variable will facilitate our future usage.

// Init Network
func InitNetwork(networkRPC string) {
var err error
client, err = ethclient.Dial(networkRPC)
if err != nil {
log.Fatal(err)
}
fmt.Println("Connected")
}

The ImportWallet function takes the private key of your wallet as a parameter and converts it to a public key of type Address and a private key of type *ecdsa.PrivateKey. We will later use these addresses to deploy and interact with the contract.

// Import Wallet
func ImportWallet(privateKey string) (common.Address, *ecdsa.PrivateKey) {
importedPrivateKey, err := crypto.HexToECDSA(privateKey)
if err != nil {
log.Fatal(err)
}

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

address := crypto.PubkeyToAddress(*publicKeyECDSA)
return address, importedPrivateKey
}

Step 2 : Prepare Transaction Configurations

This function is typically used before deploying or interacting with a smart contract to set up the transaction parameters correctly.

The PrepareTransaction function prepares transaction options for interacting with a smart contract. It retrieves the nonce, suggests a gas price, obtains the chain ID, creates an authenticator with the private key and chain ID, and sets various transaction parameters. The function returns the transaction options, allowing for the execution of transactions with the smart contract.

func PrepareTransaction(publicKey common.Address, privateKey *ecdsa.PrivateKey) *bind.TransactOpts {
nonce, err := client.PendingNonceAt(context.Background(), publicKey)
if err != nil {
log.Fatal(err)
}

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

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

auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
if err != nil {
panic(err)
}

auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0)
auth.GasLimit = uint64(15000000) // It can change
auth.GasPrice = gasPrice

return auth
}

Step 3 : Main Function

In summary, this code snippet sets up the Avalanche Fuji test network, imports a wallet with a private key, prepares transaction options, deploys a TestToken contract, and returns the contract address and transaction hash.

  • It initializes the Avalanche Fuji test network by providing the RPC endpoint URL.
  • The ImportWallet function is called with a private key as an argument to import a wallet. This function returns the public and private addresses associated with the imported wallet.
  • The PrepareTransaction function is invoked with the wallet's public and private addresses to prepare the transaction options for interacting with the smart contract.
  • The DeployTestToken function is called on the TestToken contract, passing the transaction options (config), the Ethereum client, and the wallet's public address. This function deploys the TestToken contract to the blockchain and returns the contract address, transaction object, contract instance, and an error (if any).
  • Finally, the contract address and transaction hash are printed to the console for reference
func main() {
avalancheFuji := "https://api.avax-test.network/ext/bc/C/rpc"
InitNetwork(avalancheFuji)

walletPublicAddress, walletPrivateAddress := ImportWallet("814.........83") // Private Key Here
config := PrepareTransaction(walletPublicAddress, walletPrivateAddress)

address, tx, instance, err := TestToken.DeployTestToken(config, client, walletPublicAddress)
if err != nil {
log.Fatal(err)
}

fmt.Println("Contract Address", address.Hex())
fmt.Println("Transaction", tx.Hash().Hex())

_ = instance // we will use for interaction with contract

}

Warning: If you encounter the “invalid opcode: PUSH0” error, you can recompile the smart contract using the following code:

solc --evm-version paris --bin TestToken.sol --abi TestToken.sol -o build --overwrite

abigen --bin=./build/TestToken.bin --abi=./build/TestToken.abi --pkg=testToken --out=TestToken.go

Conclusion

In this document, we learned about the process of deploying a smart contract. The process involves initializing the network and importing a wallet, preparing transaction configurations, and deploying the smart contract. We also saw a code snippet that sets up the Avalanche Fuji test network, imports a wallet with a private key, prepares transaction options, deploys a TestToken contract, and returns the contract address and transaction hash.

Overall, this document provides a helpful guide for those looking to deploy their own smart contract.

Blockchain development in Golang offers exciting opportunities, and with this guide, you have taken the first steps towards building powerful and decentralized applications on the Ethereum platform.

Happy coding and exploring the world of blockchain technology!

--

--