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) -> i32testStoreAndRetrieveArray() -> i32
✅ Objetivo: construir una transaction
INVOKE_SMART_CONTRACTdonde:
To= la dirección del contrato- payload = JSON
{ functionName, args, gasLimit }- los args se codifican usando
transaction.Encode(...)
1) Lo que se necesita antes de invocar
Se necesita:
- Una billetera con fondos (o cualquier billetera que pueda enviar txs en la chain).
- Una dirección de contrato desplegado (el ID de transaction devuelto al desplegar).
- Endpoint del node y blockchainId.
La dirección del contrato es el
TransactionIddevuelto por la transaction de despliegue. Se debe guardar porque se usará comoTo.
2) Crear una billetera (ejemplo)
Este tutorial se centra en la lógica de invocación, por lo que mantendremos la creación de la billetera al mínimo.
(También se puede cargar desde un archivo .ukey, y cualquiera de los dos enfoques 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:
- Codificar el argumento:
transaction.Encode(int32Value) - Colocarlo dentro de
[]transaction.ContractArgs - Serializar
InvokeContractPayloada 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, se crea un transaction.ULTransactionInput:
✅ Campos importantes
BlockchainId: el id de la chainTo: dirección del contratoPayloadType:INVOKE_SMART_CONTRACTPayload: string JSON delInvokeContractPayload
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 solución de problemas
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 se usa:
FunctionName: "testStoreAndRetrieve"
To debe ser la dirección del contrato
Para llamadas a contratos:
To= contractAddressFrom= la dirección de la billetera
Gas limit
Se recomienda comenzar con 100000 para contratos simples y aumentarlo en contratos que realicen muchas llamadas de almacenamiento. El rango válido es 100000–10000000.
Próximos pasos
- Continuar con: **Tokens ERC-20 con el SDK de Go (Go)