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://your-node-url";
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);

The TypeScript SDK does not expose an Encode(...) helper equivalent to Go’s transaction.Encode(...). This guide uses a direct byte encoding instead. If your ULVM runtime requires a specific serializer, apply it before passing the argument bytes.


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://your-node-url";
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 troubleshooting​

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​

Start at 100000 for simple contracts and raise it for contracts that make many storage calls. The valid range is 100000–10000000.


Next steps​

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