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 contract's
.watfile - 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 as a
.wat(WebAssembly text) file, such asauction.wat
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
We recommend deploying your contract as a .wat (WebAssembly text) file.
import { readFile } from "node:fs/promises";
const wat = await readFile("./auction.wat", "utf8");
const contractSource = wat;
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 troubleshooting
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
Set gasLimit explicitly. Omitting it applies a default, and 0 is raised to the 100000 minimum
rather than meaning unlimited. The valid range is 100000–10000000, so start at 100000 for simple
contracts and raise it for contracts that make many storage calls.
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)