GAS Model using Golang

When blockchain development is carried out in Golang it is common to use Go-ethereum libraries. If that is your case, in the following guide we will explain the necessary changes and examples of how to deploy a contract and send a transaction using the Golang programming language to the LACChain node.

Prerequisite

Have the version [email protected] equal to or higher, if you do not have it, please download it with the command:

 
				
					> go get github.com/ethereum/go-ethereum
				
			

Go to the archive /go-ethereum/blob/master/ethclient/ethclient.go and add the following function:

				
					func (ec *Client) SendTransactionLacchain(ctx context.Context, tx *types.Transaction) ([]byte, error) {
    var hex hexutil.Bytes
    data, err := tx.MarshalBinary()
    if err != nil {
        return nil, err
    }
    err = ec.c.CallContext(ctx, &hex, "eth_sendRawTransaction", hexutil.Encode(data))
    if err != nil {
        return nil, err
    }

    return hex, nil
}

				
			

This function will return the hash of the transaction, which is necessary to then obtain the receipt of the transaction.

Deploy a smart contract

To deploy a smart contract please create a new directory called deploy and a new file called deploy.go into this deploy directory. Now, copy the following example in deploy.go file:

				
					package main
import (
    "context"
    "crypto/ecdsa"
    "encoding/hex"
    "fmt"
    "log"
    "math/big"
    "time"

    "github.com/ethereum/go-ethereum/accounts/abi"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/rlp"
    “github.com/ethereum/go-ethereum/ethclient”
)

func main() {
    client, err := lacnet.Dial(“<PUT_YOUR_RCP_NODE_CONNECTION>”)
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3")
    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)
    }

    // arguments
    addressArg, err := abi.NewType("address", "", nil)
    if err != nil {
        log.Fatal(err)
    }
    uint256Arg, err := abi.NewType("uint256", "", nil)
    if err != nil {
        log.Fatal(err)
    }

    // Encode LACChain parameters
    lacchainAddress := common.HexToAddress(“<PUT_YOUR_NODE_ADDRESS”) //Change this by your nodeAddress, which is located at /lacchain/data/nodeAddress file
    lacchainExpiration := new(big.Int).SetInt64(time.Now().Add(time.Minute * time.Duration(20)).Unix())
    argumentsLAC := abi.Arguments{
        {Type: addressArg},
        {Type: uint256Arg},
    }
    valueLAC, err := argumentsLAC.Pack(
        lacchainAddress,
        lacchainExpiration,
    )
    if err != nil {
        log.Fatal(err)
    }

    value := big.NewInt(0)      // in wei (0 eth)
    gasLimit := uint64(1000000) // in units
    gasPrice := big.NewInt(0)   // in wei 0 gas price

    contractByteCode := “0x608060405234801561001057600080fd5b5060c78061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80636057361d146037578063b05784b8146062575b600080fd5b606060048036036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b60686088565b6040518082815260200191505060405180910390f35b8060008190555050565b6000805490509056fea26469706673582212208595760c8d4272f1711ffb94441dddc78e22630630efc2bc60b984de1caeb06c64736f6c63430006030033" //Bytecode to deploy
    data, _ := hexutil.Decode(contractByteCode + hex.EncodeToString(valueLAC)) //Adding to additional parameters nodeAddress + expiration
    tx := types.NewContractCreation(nonce, value, gasLimit, gasPrice, data)

    signedTx, err := types.SignTx(tx, types.FrontierSigner{}, privateKey)
    if err != nil {
        log.Fatal(err)
    }

    rawTxBytes, err := signedTx.MarshalBinary()
    if err != nil {
        log.Fatal(err)
    }
    rawTxHex := hex.EncodeToString(rawTxBytes)

    fmt.Println(rawTxHex) // f86...772

    rawTxRLPBytes, err := hex.DecodeString(rawTxHex)

    txRLP := new(types.Transaction)
    rlp.DecodeBytes(rawTxRLPBytes, &txRLP)

    txHash, err := client.SendTransactionLacchain(context.Background(), txRLP)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("tx sent: 0x%s\n", hex.EncodeToString(txHash))

    time.Sleep(7 * time.Second)

    receipt, err := client.TransactionReceipt(context.Background(), common.BytesToHash(txHash))
    if err != nil {
        fmt.Printf("failed get transaction receipt")
    }

    fmt.Printf("Contract address: %s", receipt.ContractAddress.String())
}

				
			

It is important to mention that two parameters are added, the nodeAddress and the time expiration, these two parameters are in the part of “//Encode LACChain parameters” in the previous code. These two parameters are necessary to send the transaction to the network.

 

After copying the code, consider changing the following values:

  • YOUR_NODE_RPC: Type your node RPC connection
  • YOUR_NODE_ADDRESS: Make sure to type your nodeAddress, which is located in /lacchain/data/nodeAddress file on your node.

 

Now, compile the code with the following command:

				
					> go build deploy.go
				
			

Run the executable to display the contract:

				
					> ./deploy
				
			

If everything is correct, a contract will be displayed and the transaction hash will show you.

				
					$ f90176820ca380830f42408080b90126608060405234801561001057600080fd5b5060c78061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80636057361d146037578063b05784b8146062575b600080fd5b606060048036036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b60686088565b6040518082815260200191505060405180910390f35b8060008190555050565b6000805490509056fea26469706673582212208595760c8d4272f1711ffb94441dddc78e22630630efc2bc60b984de1caeb06c64736f6c63430006030033000000000000000000000000d00e6624a73f88b39f82ab34e8bf2b4d226fd7680000000000000000000000000000000000000000000000000000000062cdebe31ba05c868ff63d72ab6df416c3817175de892119f2f3c76d6fc48a46b8e30ba2b270a020a3cf8e01be38ddecc2813187fc641816bb53e7f1115e7a44eb77c63ed0f6f8
tx sent: 0xaedf5b9c249100cc4c8139df856e065826742cc760a9b381fc179d128ec462c3
Contract address: 0xAfbb9195B995Ebee606AC453Fe717f2eE1041838   

				
			

Send a Transaction

In this example we will send a transaction to the previously deployed contract, to change the value stored in the contract.

To deploy a smart contract please create new folder called send and a new file called main.go into this directory. Now copy the following example:

				
					package main

import (
    "context"
    "crypto/ecdsa"
    "encoding/hex"
    "fmt"
    "log"
    "math/big"
    "os"
    "strings"
    "time"

    "github.com/ethereum/go-ethereum"
    "github.com/ethereum/go-ethereum/accounts/abi"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/hexutil"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/rlp"
    lacnet "prueba.com/ethclient"
)

func main() {
    contractDeployed := os.Args[1]
    contractAddress := common.HexToAddress(contractDeployed)

    client, err := lacnet.Dial(“<PUT_YOUR_RPC_NODE_CONNECTION>”)
    if err != nil {
        log.Fatal(err)
    }

    privateKey, err := crypto.HexToECDSA("c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3")
    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)
    }

    // arguments
    addressArg, err := abi.NewType("address", "", nil)
    if err != nil {
        log.Fatal(err)
    }
    uint256Arg, err := abi.NewType("uint256", "", nil)
    if err != nil {
        log.Fatal(err)
    }
    // Encode LACChain parameters
    lacchainAddress := common.HexToAddress(“PUT_YOUR_NODEADDRESS“) //Change this by your nodeAddress /lacchain/data/nodeAddress
    lacchainExpiration := new(big.Int).SetInt64(time.Now().Add(time.Minute * time.Duration(20)).Unix())
    argumentsLAC := abi.Arguments{
        {Type: addressArg},
        {Type: uint256Arg},
    }
    valueLAC, err := argumentsLAC.Pack(
        lacchainAddress,
        lacchainExpiration,
    )
    if err != nil {
        log.Fatal(err)
    }

    value := big.NewInt(0)      // in wei (0 eth)
    gasLimit := uint64(1000000) // in units
    gasPrice := big.NewInt(0)   // in wei 0 gas price

    json := `[{"inputs": [{"internalType": "uint256","name": "num","type": "uint256"}],"name": "store","outputs": [],"stateMutability": "nonpayable","type": "function"}]`
    //  method := NewMethod("", "", Constructor, "nonpayable", false, false, []Argument{{"a", Uint256, false}, {"b", Uint256, false}}, nil)
    // Test from JSON
    abiContract, err := abi.JSON(strings.NewReader(json))
    if err != nil {
        log.Fatal(err)
    }

    packed, err := abiContract.Pack("store", big.NewInt(30))
    if err != nil {
        log.Fatal(err)
    }

    data, _ := hexutil.Decode("0x" + hex.EncodeToString(packed) + hex.EncodeToString(valueLAC)) //Adding to additional parameters nodeAddress + expiration
    tx := types.NewTx(&types.LegacyTx{
        Nonce:    nonce,
        To:       &contractAddress,
        Value:    value,
        Gas:      gasLimit,
        GasPrice: gasPrice,
        Data:     data,
    })

    signedTx, err := types.SignTx(tx, types.FrontierSigner{}, privateKey)
    if err != nil {
        log.Fatal(err)
    }

    rawTxBytes, err := signedTx.MarshalBinary()
    if err != nil {
        log.Fatal(err)
    }
    rawTxHex := hex.EncodeToString(rawTxBytes)

    fmt.Println(rawTxHex) // f86...772

    rawTxRLPBytes, err := hex.DecodeString(rawTxHex)

    txRLP := new(types.Transaction)
    rlp.DecodeBytes(rawTxRLPBytes, &txRLP)

    txHash, err := client.SendTransactionLacchain(context.Background(), txRLP)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("tx sent: 0x%s\n", hex.EncodeToString(txHash))

    time.Sleep(7 * time.Second)

    receipt, err := client.TransactionReceipt(context.Background(), common.BytesToHash(txHash))
    if err != nil {
        fmt.Printf("failed get transaction receipt")
    }

    fmt.Printf("Transaction status: %d\n", receipt.Status)

    input, _ := hexutil.Decode("0xb05784b8")
    msg := ethereum.CallMsg{To: &contractAddress, Data: input}

    result, err := client.CallContract(context.Background(), msg, receipt.BlockNumber)
    if err != nil {
        fmt.Println("failed get value from smart contract", err)
    }

    resultArgument := abi.Arguments{
        {Type: uint256Arg},
    }
    newValue, err := resultArgument.Unpack(
        result,
    )
    if err != nil {
        fmt.Println(err)
    }

    fmt.Printf("New value is:%s", newValue)
}


				
			

It is important to mention that two parameters are added, the nodeAddress and the time expiration, these two parameters are in the part of “//Encode LACChain parameters” in the previous code. These two parameters are necessary to send the transaction to the network.

 

After copying the code, consider changing the following values:

  • YOUR_NODE_RPC: Type your node RPC connection
  • YOUR_NODE_ADDRESS: Make sure to type your nodeAddress, which is located in /lacchain/data/nodeAddress file on your node.

 

Now, compile the code with the following command:

				
					
> cd send
> go build sendTransaction.go

				
			

Run the executable to display the contract:

				
					> ./sendTransaction <PUT_YOUR_CONTRACT_ADDRESS_DEPLOYED>  
				
			

For example, for our previously deployed contract it would be:

				
					
> ./sendTransaction 0xAfbb9195B995Ebee606AC453Fe717f2eE1041838

				
			

If everything is correct, you will see the following lines in your console:

				
					
f8c7820cb080830f424094afbb9195b995ebee606ac453fe717f2ee104183880b8646057361d000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000d00e6624a73f88b39f82ab34e8bf2b4d226fd7680000000000000000000000000000000000000000000000000000000062cdfff81ca075b5ddaa8d82f3657e0acb3534cbbba102ac962b8c7ce0bf66e9a0316aed02eea07553d5fb04ecf70a838110693a41b34a36ed800cc395786b90beff29bcffdfe5
tx sent: 0xafc31b1c79e71b2a7e4cc9632d66c6c63a3577a240848420877d34052797ec00
Transaction status: 1
New value is:[30] 

				
			

Copyright 2022 © All rights Reserved. Designed by LACNet