Saltar al contenido principal

Invocar un Smart Contract (TypeScript)

Este tutorial muestra la lógica principal para invocar una función de smart contract de ULVM usando el SDK de TypeScript de ULedger (respaldado por WASM).

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

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

✅ Objetivo: enviar una transacción INVOKE_SMART_CONTRACT donde:

  • contractAddress = la dirección del contrato desplegado
  • functionName = nombre de la función exportada
  • args = Array<string | Uint8Array>
  • gasLimit = número opcional

En el SDK de TypeScript, generalmente se invoca mediante:

  • session.invokeContract({ blockchainId, contractAddress, functionName, args, gasLimit })

en lugar de construir ULTransactionInput manualmente.


1) Requisitos antes de invocar​

Se necesita:

  1. Una billetera que pueda enviar transacciones en la chain.
  2. Una dirección de contrato desplegado (el mismo contractAddress que se usó durante el despliegue).
  3. Endpoint del node y blockchainId.
info

En muchas chains, la billetera debe estar registrada en la chain antes de poder enviar llamadas a contratos.

Si aparece REJECTED_BY_UNAUTHORIZED, se debe registrar la billetera primero:

await session.registerWallet(blockchainId);

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 restaurar desde un JSON guardado previamente.)

import { getULedgerSDK, KeyType } from "@uledgerinc/typescript-sdk";

const sdk = await getULedgerSDK();

// Genera una billetera (en producción, probablemente se persiste o se restaura en su lugar)
const wallet = sdk.generateWallet({ keyType: KeyType.Secp256k1 });

console.log("Wallet address:", wallet.address);

3) Crear una sesión de transacción​

Se usa el helper del SDK para crear una sesión inicializada.

const nodeEndpoint = "https://your-node-url";
const blockchainId = "08c28f29a62819120958984b761ddf8ccb45951612731409873994958fd150a2";

const session = await sdk.createSession(nodeEndpoint, wallet);

4) Invocar el contrato​

Ejemplo A — llamar a testStoreAndRetrieveArray() (sin args)​

const contractAddress = "<YOUR_CONTRACT_ADDRESS>";

const tx = await session.invokeContract({
blockchainId,
contractAddress,
functionName: "testStoreAndRetrieveArray",
args: [], // opcional; vacío está bien
gasLimit: 100000, // opcional
});

console.log("Invoke tx id:", tx.transactionId);
console.log("Status:", tx.status, "| Output:", tx.output);

Ejemplo B — llamar a testStoreAndRetrieve(value: i32) (un arg)​

El SDK de TypeScript acepta args como string o Uint8Array.

Si el contrato espera un i32 sin procesar, se puede codificar un entero de 32 bits en 4 bytes (little-endian es una convención común):

function i32LE(n: number): Uint8Array {
const buf = new ArrayBuffer(4);
new DataView(buf).setInt32(0, n, true);
return new Uint8Array(buf);
}

const tx = await session.invokeContract({
blockchainId,
contractAddress,
functionName: "testStoreAndRetrieve",
args: [i32LE(123)],
gasLimit: 100000,
});

console.log("Invoke tx id:", tx.transactionId);
console.log("Status:", tx.status, "| Output:", tx.output);

El SDK de TypeScript no expone un helper Encode(...) equivalente al transaction.Encode(...) de Go. Esta guía usa en su lugar una codificación directa de bytes. Si el runtime de ULVM requiere un serializador específico, se debe aplicar antes de pasar los bytes del argumento.


5) Ejemplo mínimo completo (lógica principal)​

Este ejemplo invoca testStoreAndRetrieve(123).

import { getULedgerSDK, KeyType } from "@uledgerinc/typescript-sdk";

function i32LE(n: number): Uint8Array {
const buf = new ArrayBuffer(4);
new DataView(buf).setInt32(0, n, true);
return new Uint8Array(buf);
}

async function main() {
const sdk = await getULedgerSDK();

const wallet = sdk.generateWallet({ keyType: KeyType.Secp256k1 });

const nodeEndpoint = "https://your-node-url";
const blockchainId = "08c28f29a62819120958984b761ddf8ccb45951612731409873994958fd150a2";

const session = await sdk.createSession(nodeEndpoint, wallet);

// Si se obtiene REJECTED_BY_UNAUTHORIZED, se debe descomentar:
// await session.registerWallet(blockchainId);

const contractAddress = "<YOUR_CONTRACT_ADDRESS>";

const tx = await session.invokeContract({
blockchainId,
contractAddress,
functionName: "testStoreAndRetrieve",
args: [i32LE(123)],
gasLimit: 100000,
});

console.log("Invoke tx id:", tx.transactionId);
console.log("Status:", tx.status, "| Output:", tx.output);
}

main().catch((e) => {
console.error("Invoke failed:", e);
process.exit(1);
});

Notas y solución de problemas​

El nombre de la función debe coincidir exactamente​

functionName debe coincidir con el nombre de la función ULVM exportada:

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

Por lo tanto, en TypeScript:

functionName: "testStoreAndRetrieve"

Dirección del contrato​

En el SDK de TypeScript, invokeContract() recibe contractAddress directamente.

Se debe usar la misma dirección del contrato al que se desplegó (el valor pasado como contractAddress durante el despliegue).

Gas limit​

Se recomienda comenzar con 100000 para contratos simples y aumentarlo en contratos que realizan muchas llamadas de almacenamiento. El rango válido es 100000–10000000.


Próximos pasos​

  • Continuar con: **Tokens ERC-20 con el SDK de Go (TS)