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 — 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 — 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 common gotchas

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

If you’re unsure, start with something like 100000 for basic examples and adjust later.


Next steps

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