Saltar al contenido principal

Invocar un Smart Contract (Go)

Este tutorial muestra la lógica central para invocar una función de smart contract de ULVM usando el SDK de Go.

Usaremos el mismo contrato de ejemplo de ULVM Smart Contract Basics:

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

✅ Objetivo: construir una transaction INVOKE_SMART_CONTRACT donde:

  • To = la dirección del contrato
  • payload = JSON { functionName, args, gasLimit }
  • los args se codifican usando transaction.Encode(...)

1) Lo que necesitas antes de invocar

Necesitas:

  1. Un wallet con fondos (o cualquier wallet que pueda enviar txs en tu chain).
  2. Una dirección de contrato desplegado (el ID de transaction devuelto al desplegar).
  3. Endpoint del node y blockchainId.

La dirección del contrato es el TransactionId devuelto por la transaction de despliegue. Guárdalo en algún lugar — lo usarás como To.


2) Crear un wallet (ejemplo)

Este tutorial se centra en la lógica de invocación, por lo que mantendremos la creación del wallet al mínimo. (También puedes cargarlo desde un archivo .ukey — cualquier enfoque funciona.)

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) Construir el payload de invocación (functionName + args)

El payload de una transaction de invocación de ULVM es un JSON que coincide con:

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

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

Ejemplo A — llamar a testStoreAndRetrieve(value: i32)

Esta función del contrato espera un argumento i32.

Idea central:

  1. Codificar el argumento: transaction.Encode(int32Value)
  2. Colocarlo dentro de []transaction.ContractArgs
  3. Serializar InvokeContractPayload a bytes JSON
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)
}

Ejemplo B — llamar a testStoreAndRetrieveArray()

Esta función no recibe argumentos, por lo que Args es un slice vacío.

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

return json.Marshal(payload)
}

4) Crear y enviar la transaction INVOKE

Para invocar un contrato, creas un transaction.ULTransactionInput:

Campos importantes

  • BlockchainId: el id de tu chain
  • To: dirección del contrato
  • PayloadType: INVOKE_SMART_CONTRACT
  • Payload: string JSON de tu 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) Ejemplo mínimo completo (lógica central)

Este ejemplo invoca 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>"

// ✅ dirección del contrato devuelta del 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)
}

// Construir invoke payload para 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)
}

Notas y errores comunes

El nombre de la función del contrato debe coincidir exactamente

FunctionName debe coincidir con el nombre de la función AssemblyScript exportada:

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

Por lo tanto, en Go, usa:

FunctionName: "testStoreAndRetrieve"

To debe ser la dirección del contrato

Para llamadas a contratos:

  • To = contractAddress
  • From = la dirección de tu wallet

Gas limit

Si no estás seguro, comienza con algo como 100000 para ejemplos básicos y ajústalo más adelante.


Próximos pasos

  • Continúa con: **Tokens ERC-20 con el SDK de Go (Go)