Skip to main content

Deploy Smart Contracts (TypeScript)

This guide shows the core steps to deploy a ULVM smart contract using the ULedger TypeScript SDK.

You will:

  1. Initialize the SDK
  2. Load or generate a wallet (the signer)
  3. Read your compiled contract (.wat or .wasm)
  4. Create a transaction session to a node
  5. Submit a DEPLOY_SMART_CONTRACT transaction
info

On many chains, your wallet must be registered on-chain before it can deploy contracts. If you get REJECTED_BY_UNAUTHORIZED, register the wallet first (see the note in Step 5).


1. Prerequisites

You need:

  • nodeEndpoint (example: http://localhost:8080)
  • blockchainId
  • A wallet that can sign transactions
  • Your contract source file:
    • auction.wat (text), or
    • auction.wasm (binary — encoding depends on node rules)

2. Initialize the SDK and load a wallet

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

const sdk = await getULedgerSDK();

// Option A: generate a new wallet
const wallet = sdk.generateWallet({ keyType: KeyType.Secp256k1 });

// Option B: restore from mnemonic / JSON (examples)
// const wallet = sdk.walletFromMnemonic(mnemonic, { keyType: KeyType.Secp256k1 });
// const wallet = sdk.walletFromJson(walletJsonString);

3. Read the contract source code

Option A: Deploy a .wat file (text)

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

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

Option B: Deploy a .wasm file (binary)

Your node may expect raw bytes, hex, or base64. A common approach is base64:

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

const wasmBytes = await readFile("./auction.wasm");
const contractSource = Buffer.from(wasmBytes).toString("base64");

Use the format your node expects for DEPLOY_SMART_CONTRACT.


4. Pick a contract address (this goes in to)

In the TS SDK, deployContract requires a contractAddress, which becomes the transaction to.

Here’s a simple helper that generates a random 32-byte hex string:

import { randomBytes } from "node:crypto";

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

const contractAddress = randomHex32();

5. Create a transaction session (connects to the node)

This call initializes the session (fetches /health to get nodeId and checks /blockchains):

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

6. Deploy the contract

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);

What happens under the hood (deploy case)

TransactionSession.deployContract(...) delegates to submitTransaction(...), which:

  • Builds a transaction input with:

    • to = contractAddress
    • from = wallet.address (deploy is not a wallet-create tx)
    • payload = sourceCode
    • payloadType = TransactionType.DeploySmartContract
    • suggestor = nodeId (from /health)
  • Signs the transaction input via the WASM backend

  • POSTs to: POST {nodeEndpoint}/blockchains/{blockchainId}/transactions


7. Next step: Invoke the contract

To call the deployed contract, submit an INVOKE_SMART_CONTRACT transaction.

Continue to: Invoke Smart Contracts (TS) (next guide).


---

## Invoke Smart Contracts (TypeScript)

```mdx
---
id: invoke-smart-contract-ts
title: Invoke a Smart Contract with the TypeScript SDK
sidebar_label: Invoke Smart Contract (TS)
sidebar_position: 5
---

# Invoke a Smart Contract (TypeScript)

This guide shows the **core logic** to invoke a ULVM smart contract function using the TypeScript SDK.

✅ Goal: create an `INVOKE_SMART_CONTRACT` transaction where:

- `to` = the **contract address**
- payload = JSON `{ functionName, args, gasLimit }`
- args are provided as `string` or `Uint8Array`

---

## 1) What you need before invoking

You need:

1. A wallet that can submit txs on your chain.
2. A deployed **contract address** (the same `contractAddress` you passed during deploy).
3. `nodeEndpoint` and `blockchainId`.

:::info
The **contract address is not the deploy transactionId** in this SDK.
Use the `contractAddress` you chose at deploy time (the transaction `to`).
:::

---

## 2) Initialize the SDK and wallet (example)

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

const sdk = await getULedgerSDK();

// Minimal example: generate a wallet
const wallet = sdk.generateWallet({ keyType: KeyType.Secp256k1 });

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

3) Create a session

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

4) Invoke a contract function

The TS session exposes:

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

Example A — call testStoreAndRetrieve(...)

If your contract accepts string args (or parses strings), you can pass "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);

Example B — call testStoreAndRetrieveArray() (no 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) Notes and common gotchas

Function name must match exactly

functionName must match the exported ULVM function name exactly.

contractAddress must be correct

For contract calls:

  • contractAddress = the same address used at deploy time
  • signer = your wallet in the session

Args must match what the contract expects

In the TS SDK:

  • args can be string or Uint8Array
  • Use Uint8Array when your contract expects VM-encoded binary arguments

If your contract expects strongly typed binary encoding (e.g., i32 encoded for the VM), provide a Uint8Array produced by your contract serializer utilities (documented separately).

Gas limit defaults

If you omit gasLimit, the session sets a default (implementation currently uses a large default). If you’re unsure, start with 100000 for basic examples and adjust.


Optional: Query results after invoke

Get a transaction by id

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

Get a block by height

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

Next steps

Continue to: **Invoke Smart Contracts (TS)