Saltar al contenido principal

Desplegar Smart Contracts (TypeScript)

Esta guía muestra los pasos principales para desplegar un smart contract ULVM usando el SDK de TypeScript de ULedger.

Pasos:

  1. Inicializar el SDK
  2. Cargar o generar una billetera (el firmante)
  3. Leer el archivo .wat del contrato
  4. Crear una sesión de transaction hacia un node
  5. Enviar una transaction DEPLOY_SMART_CONTRACT
info

En muchas chains, la billetera debe estar registrada on-chain antes de poder desplegar contratos. Si se obtiene REJECTED_BY_UNAUTHORIZED, se debe registrar la billetera primero (ver la nota en el Paso 5).


1. Requisitos previos​

Se necesita:

  • nodeEndpoint (ejemplo: http://localhost:8080)
  • blockchainId
  • Una billetera que pueda firmar transactions
  • El contrato como archivo .wat (texto de WebAssembly), por ejemplo auction.wat

2. Inicializar el SDK y cargar una billetera​

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

const sdk = await getULedgerSDK();

// Opción A: generar una nueva billetera
const wallet = sdk.generateWallet({ keyType: KeyType.Secp256k1 });

// Opción B: restaurar desde mnemonic / JSON (ejemplos)
// const wallet = sdk.walletFromMnemonic(mnemonic, { keyType: KeyType.Secp256k1 });
// const wallet = sdk.walletFromJson(walletJsonString);

3. Leer el código fuente del contrato​

Se recomienda desplegar el contrato como un archivo .wat (texto de WebAssembly).

import { readFile } from "node:fs/promises";

const wat = await readFile("./auction.wat", "utf8");
const contractSource = wat;

4. Elegir una dirección de contrato (esto va en to)​

En el SDK de TS, deployContract requiere un contractAddress, que se convierte en el to de la transaction.

A continuación se muestra un helper simple que genera una cadena hex aleatoria de 32 bytes:

import { randomBytes } from "node:crypto";

function randomHex32(): string {
return randomBytes(32).toString("hex"); // 64 caracteres hex
}

const contractAddress = randomHex32();

5. Crear una sesión de transaction (se conecta al node)​

Esta llamada inicializa la sesión (obtiene /health para conseguir el nodeId y verifica /blockchains):

const nodeEndpoint = "http://localhost:8080";
const session = await sdk.createSession(nodeEndpoint, wallet);

6. Desplegar el contrato​

import { TransactionType } from "@uledgerinc/typescript-sdk";

const tx = await session.deployContract({
blockchainId,
sourceCode: contractSource,
contractAddress,
});

console.log("Deploy TX ID:", tx.transactionId);
console.log("Status:", tx.status);
console.log("Output:", tx.output);
console.log("Contract Address (to):", contractAddress);

Qué ocurre internamente (caso de despliegue)​

TransactionSession.deployContract(...) delega en submitTransaction(...), que:

  • Construye un input de transaction con:

    • to = contractAddress
    • from = wallet.address (el despliegue no es una tx de creación de billetera)
    • payload = sourceCode
    • payloadType = TransactionType.DeploySmartContract
    • suggestor = nodeId (desde /health)
  • Firma el input de la transaction mediante el backend WASM

  • Hace POST a: POST {nodeEndpoint}/blockchains/{blockchainId}/transactions


7. Siguiente paso: Invocar el contrato​

Para llamar al contrato desplegado, se envía una transaction INVOKE_SMART_CONTRACT.

Continuar en: Invocar Smart Contracts (TS) (siguiente guía).


---

## Invocar Smart Contracts (TypeScript)

```mdx
---
id: invoke-smart-contract-ts
title: Invocar un Smart Contract con el SDK de TypeScript
sidebar_label: Invocar Smart Contract (TS)
sidebar_position: 5
---

# Invocar un Smart Contract (TypeScript)

Esta guía muestra la **lógica principal** para invocar una función de un smart contract ULVM usando el SDK de TypeScript.

✅ Objetivo: crear una transaction `INVOKE_SMART_CONTRACT` donde:

- `to` = la **dirección del contrato**
- payload = JSON `{ functionName, args, gasLimit }`
- los args se proporcionan como `string` o `Uint8Array`

---

## 1) Requisitos antes de invocar

Se necesita:

1. Una billetera que pueda enviar txs en la chain.
2. Una **dirección de contrato** desplegada (el mismo `contractAddress` que se pasó durante el despliegue).
3. `nodeEndpoint` y `blockchainId`.

:::info
La **dirección del contrato no es el transactionId del despliegue** en este SDK.
Se debe usar el `contractAddress` elegido en el momento del despliegue (el `to` de la transaction).
:::

---

## 2) Inicializar el SDK y la billetera (ejemplo)

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

const sdk = await getULedgerSDK();

// Ejemplo mínimo: generar una billetera
const wallet = sdk.generateWallet({ keyType: KeyType.Secp256k1 });

// O restaurar:
// const wallet = sdk.walletFromMnemonic(mnemonic, { keyType: KeyType.Secp256k1 });
// const wallet = sdk.walletFromJson(walletJsonString);

3) Crear una sesión​

const nodeEndpoint = "http://localhost:8080";
const session = await sdk.createSession(nodeEndpoint, wallet);

4) Invocar una función del contrato​

La sesión de TS expone:

invokeContract(options: {
blockchainId: string;
contractAddress: string;
functionName: string;
args?: Array<string | Uint8Array>;
gasLimit?: number;
}): Promise<ULTransaction>

Ejemplo A — llamar a testStoreAndRetrieve(...)​

Si el contrato acepta args de tipo string (o parsea strings), se puede pasar "123":

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

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

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

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

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

5) Notas y solución de problemas​

El nombre de función debe coincidir exactamente​

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

contractAddress debe ser correcto​

Para llamadas a contratos:

  • contractAddress = la misma dirección usada en el momento del despliegue
  • firmante = la billetera de la sesión

Los args deben coincidir con lo que el contrato espera​

En el SDK de TS:

  • args puede ser string o Uint8Array
  • Se debe usar Uint8Array cuando el contrato espera argumentos binarios codificados por la VM

Si el contrato espera codificación binaria con tipos fuertes (por ejemplo, i32 codificado para la VM), se debe proporcionar un Uint8Array producido por las utilidades de serialización de contratos (documentadas por separado).

Valores predeterminados del gas limit​

Se debe establecer gasLimit de forma explícita. Si se omite, se aplica un valor predeterminado, y 0 se eleva al mínimo de 100000 en lugar de significar ilimitado. El rango válido es 100000–10000000, por lo que conviene comenzar con 100000 para contratos simples y aumentarlo en contratos que realizan muchas llamadas de almacenamiento.


Opcional: Consultar resultados después de invocar​

Obtener una transaction por id​

const loaded = await session.getTransaction(blockchainId, tx.transactionId);
console.log("Loaded status:", loaded.status, "| output:", loaded.output);

Obtener un block por altura​

const block = await session.getBlock(blockchainId, tx.blockHeight);
console.log("Block height:", block.height);

Próximos pasos​

Continuar en: **Invocar Smart Contracts (TS)