Deploy Smart Contracts (TypeScript)
This guide shows the core steps to deploy a ULVM smart contract using the ULedger TypeScript SDK.
You will:
- Initialize the SDK
- Load or generate a wallet (the signer)
- Read your compiled contract (
.wator.wasm) - Create a transaction session to a node
- Submit a
DEPLOY_SMART_CONTRACTtransaction
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), orauction.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 = contractAddressfrom = wallet.address(deploy is not a wallet-create tx)payload = sourceCodepayloadType = TransactionType.DeploySmartContractsuggestor = 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:
argscan bestringorUint8Array- Use
Uint8Arraywhen 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)