Skip to main content

Invoke a Smart Contract (TypeScript)

This tutorial shows the core logic to invoke a ULVM smart contract function using the ULedger TypeScript SDK (WASM-backed).

We’ll use the same example contract from ULVM Smart Contract Basics:

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

✅ Goal: submit an INVOKE_SMART_CONTRACT transaction where:

  • contractAddress = the deployed contract address
  • functionName = exported function name
  • args = Array<string | Uint8Array>
  • gasLimit = optional number

In the TypeScript SDK, you typically invoke via:

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

rather than manually building ULTransactionInput.


1) What you need before invoking

You need:

  1. A wallet that can submit transactions on your chain.
  2. A deployed contract address (the same contractAddress you used during deploy).
  3. Node endpoint and blockchainId.
info

On many chains, your wallet must be registered on-chain before it can submit contract calls.

If you see REJECTED_BY_UNAUTHORIZED, register first:

await session.registerWallet(blockchainId);

2) Create a wallet (example)

This tutorial focuses on invocation logic, so we’ll keep wallet creation minimal. (You can also restore from JSON you previously saved.)

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

const sdk = await getULedgerSDK();

// Generate a wallet (for production, you’ll likely persist/restore it instead)
const wallet = sdk.generateWallet({ keyType: KeyType.Secp256k1 });

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

3) Create a transaction session

Use the SDK helper to create an initialized session.

const nodeEndpoint = "https://tn-w-1.uledger.net/";
const blockchainId = "08c28f29a62819120958984b761ddf8ccb45951612731409873994958fd150a2";

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

4) Invoke the contract

Example A — call testStoreAndRetrieveArray() (no args)

const contractAddress = "<YOUR_CONTRACT_ADDRESS>";

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

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

Example B — call testStoreAndRetrieve(value: i32) (one arg)

The TypeScript SDK accepts args as string or Uint8Array.

If your contract expects a raw i32, you can encode a 32-bit integer into 4 bytes (little-endian is a common convention):

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

If your ULVM runtime requires a specific serializer (like Go’s transaction.Encode(...)), use the TypeScript SDK equivalent if it exists in your build. Based on the transaction.d.ts you shared, an Encode(...) helper is not currently exposed from the TS surface, so this guide uses a simple byte encoding example.


5) Full minimal example (core logic)

This example invokes 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://tn-w-1.uledger.net/";
const blockchainId = "08c28f29a62819120958984b761ddf8ccb45951612731409873994958fd150a2";

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

// If you get REJECTED_BY_UNAUTHORIZED, uncomment:
// 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);
});

Notes and common gotchas

Function name must match exactly

functionName must match the exported ULVM function name:

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

So in TypeScript:

functionName: "testStoreAndRetrieve"

Contract address

In the TypeScript SDK, invokeContract() takes contractAddress directly.

Use the same contract address you deployed to (the value you passed as contractAddress during deploy).

Gas limit

If you’re unsure, start around 100000 for basic examples and adjust later.


Next steps

  • Move on to: **ERC-20 Tokens with the Go SDK (TS)