Smart Contracts - Goland - Javascript

Pre-Requisites

Before deploying your smart contracts in the LACChain Networks, see this overview that explains the minor changes you will need to incorporate to them.

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 2024 © All rights Reserved. Designed by LACNet