Skip to main content

Invoke a Smart Contract (Go)

This tutorial shows the core logic to invoke a ULVM smart contract function using the Go SDK.

We’ll use the same example contract from ULVM Smart Contract Basics:

  • testStoreAndRetrieve(value: i32) -> i32
  • testStoreAndRetrieveArray() -> i32

✅ Goal: build an INVOKE_SMART_CONTRACT transaction where:

  • To = the contract address
  • payload = JSON { functionName, args, gasLimit }
  • args are encoded using transaction.Encode(...)

1) What you need before invoking​

You need:

  1. A funded wallet (or any wallet that can submit txs on your chain).
  2. A deployed contract address (the transaction ID returned from deploy).
  3. Node endpoint and blockchainId.

The contract address is the TransactionId returned by the deploy transaction. Save it somewhere because you’ll use it as To.


2) Create a wallet (example)​

This tutorial focuses on invocation logic, so we’ll keep wallet creation minimal. (You can also load from a .ukey file, and either approach works.)

import (
"fmt"
"github.com/ULedgerInc/go-sdk/pkg/crypto"
"github.com/ULedgerInc/go-sdk/pkg/wallet"
)

privateKeyHex := "<YOUR_PRIVATE_KEY_HEX>"
publicKeyHex := "<YOUR_PUBLIC_KEY_HEX>"

w, err := wallet.GetWalletFromHex(publicKeyHex, privateKeyHex, crypto.KeyTypeSecp256k1)
if err != nil {
return fmt.Errorf("GetWalletFromHex() error: %w", err)
}

3) Build the invoke payload (functionName + args)​

A ULVM invoke transaction payload is JSON that matches:

type InvokeContractPayload struct {
FunctionName string `json:"functionName"`
Args []ContractArgs `json:"args"`
GasLimit uint64 `json:"gasLimit"`
}

type ContractArgs struct {
Value []byte `json:"value"`
}

Example A — call testStoreAndRetrieve(value: i32)​

This contract function expects one i32 argument.

Core idea:

  1. Encode the argument: transaction.Encode(int32Value)
  2. Put it inside []transaction.ContractArgs
  3. Marshal InvokeContractPayload to JSON bytes
import (
"encoding/json"
"github.com/ULedgerInc/go-sdk/pkg/transaction"
)

func buildTestStoreAndRetrievePayload(value int32) ([]byte, error) {
encoded, err := transaction.Encode(value)
if err != nil {
return nil, err
}

payload := transaction.InvokeContractPayload{
FunctionName: "testStoreAndRetrieve",
Args: []transaction.ContractArgs{
{Value: encoded},
},
GasLimit: 100000,
}

return json.Marshal(payload)
}

Example B — call testStoreAndRetrieveArray()​

This function takes no arguments, so Args is an empty slice.

func buildTestStoreAndRetrieveArrayPayload() ([]byte, error) {
payload := transaction.InvokeContractPayload{
FunctionName: "testStoreAndRetrieveArray",
Args: []transaction.ContractArgs{},
GasLimit: 100000,
}

return json.Marshal(payload)
}

4) Create and submit the INVOKE transaction​

To invoke a contract, you create a transaction.ULTransactionInput:

✅ Important fields

  • BlockchainId: your chain id
  • To: contract address
  • PayloadType: INVOKE_SMART_CONTRACT
  • Payload: JSON string of your InvokeContractPayload
import (
"github.com/ULedgerInc/go-sdk/pkg/transaction"
)

func invokeContract(
nodeEndpoint string,
blockchainId string,
contractAddress string,
w wallet.UL_Wallet,
payloadBytes []byte,
) (transaction.ULTransaction, error) {

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address,
To: contractAddress,
PayloadType: transaction.INVOKE_SMART_CONTRACT.String(),
Payload: string(payloadBytes),
}

session, err := transaction.NewUL_TransactionSession(nodeEndpoint, w)
if err != nil {
return transaction.ULTransaction{}, err
}

return session.GenerateTransaction(input)
}

5) Full minimal example (core logic)​

This example invokes testStoreAndRetrieve(123).

package main

import (
"encoding/json"
"fmt"

"github.com/ULedgerInc/go-sdk/pkg/crypto"
"github.com/ULedgerInc/go-sdk/pkg/transaction"
"github.com/ULedgerInc/go-sdk/pkg/wallet"
)

func main() {
nodeEndpoint := "http://localhost:8080"
blockchainId := "<YOUR_BLOCKCHAIN_ID>"

// ✅ contract address returned from deploy tx.TransactionId
contractAddress := "<YOUR_CONTRACT_ADDRESS>"

privateKeyHex := "<YOUR_PRIVATE_KEY_HEX>"
publicKeyHex := "<YOUR_PUBLIC_KEY_HEX>"

w, err := wallet.GetWalletFromHex(publicKeyHex, privateKeyHex, crypto.KeyTypeSecp256k1)
if err != nil {
panic(err)
}

// Build invoke payload for testStoreAndRetrieve(123)
payloadBytes, err := buildTestStoreAndRetrievePayload(123)
if err != nil {
panic(err)
}

input := transaction.ULTransactionInput{
BlockchainId: blockchainId,
From: w.Address,
To: contractAddress,
PayloadType: transaction.INVOKE_SMART_CONTRACT.String(),
Payload: string(payloadBytes),
}

session, err := transaction.NewUL_TransactionSession(nodeEndpoint, w)
if err != nil {
panic(err)
}

tx, err := session.GenerateTransaction(input)
if err != nil {
panic(err)
}

fmt.Printf("Invoke tx id: %s\n", tx.TransactionId)
fmt.Printf("Status: %s | Output: %s\n", tx.Status, tx.Output)
}

func buildTestStoreAndRetrievePayload(value int32) ([]byte, error) {
encoded, err := transaction.Encode(value)
if err != nil {
return nil, err
}

payload := transaction.InvokeContractPayload{
FunctionName: "testStoreAndRetrieve",
Args: []transaction.ContractArgs{
{Value: encoded},
},
GasLimit: 100000,
}

return json.Marshal(payload)
}

Notes and troubleshooting​

Contract function name must match exactly​

FunctionName must match the exported AssemblyScript function name:

export function testStoreAndRetrieve(value: i32): i32 { ... }

So in Go, use:

FunctionName: "testStoreAndRetrieve"

To must be the contract address​

For contract calls:

  • To = contractAddress
  • From = your wallet address

Gas limit​

Start at 100000 for simple contracts and raise it for contracts that make many storage calls. The valid range is 100000–10000000.


Next steps​

  • Move on to: **ERC-20 Tokens with the Go SDK (Go)