Docs
v1

Guide

SDK

The Tenrai SDK for TypeScript and JavaScript. Every REST route, the WebSocket streams, signed answers and a wallet that signs locally.

On this page

@tenrai/sdk is the client for the node API and the Tenrai API. It runs in Node.js 22.12 or later and in browsers, from TypeScript and plain JavaScript.

Shell
npm install @tenrai/sdk

Connect

JavaScript
import { TenraiClient } from "@tenrai/sdk";

const api = new TenraiClient();                                                  // the Tenrai API
const node = new TenraiClient({ url: "http://127.0.0.1:28701", target: "node" }); // your own node
OptionDefaultMeaning
urlhttps://api.tenrai-technologies.orgA path prefix such as /tenrai is kept
target"api""api" paces requests for the Tenrai API, "node" sends without a limit
timeoutMillis15000Per request
retries2For reads refused with 429 or 503, or without an answer
verifysignedResponses(…), see below
fetch, WebSocketthe runtime'sFor example WebSocket from the ws package

With target: "api" the client sends at most 10 reads per second in bursts of 20, 8 at a time, and one submission at a time, at most two per second. That keeps it below the limits of the Tenrai API.

Read

Each group of routes is a namespace on the client:

JavaScript
await tenrai.chain.head();
await tenrai.address.balance(address);
await tenrai.address.balances([a, b, c]);         // up to 64 in one request
await tenrai.transactions.status(transactionId);
await tenrai.fees.estimate();
await tenrai.staking.validators();

Answers are the API's JSON documents, typed. pages walks paged routes:

TypeScript
import { pages } from "@tenrai/sdk";

for await (const page of pages((cursor) => tenrai.address.history(address, { cursor }))) {
  console.log(page.events.length);
}

Wallet

@tenrai/sdk/wallet builds and signs with the protocol rules compiled to WebAssembly, the same code the node runs. Keys stay inside the WASM.

JavaScript
import { Wallet } from "@tenrai/sdk/wallet";

const wallet = await Wallet.fromMnemonic(phrase, { network: "mainnet" });
wallet.address(0);                                              // first receiving address
const { atoms } = await wallet.balance(tenrai);
const sent = await wallet.send(tenrai, { to, amount, payload: "invoice 42" });
const payout = await wallet.send(tenrai, { payments: [{ to: alice, amount: a }, { to: bob, amount: b }] });
wallet.close();
CallUse
prepare, submitSign first, send later, for a confirm step in between
maxSendableThe most one transfer can send
sweep, compoundEverything to one address; merge the outputs of each address
burnPays an amount to the burn address of the wallet's network. It cannot be undone
mergeForCompounds first when an amount needs more outputs than one transfer spends
resendSends transfers whose answer was lost again while they can still execute
bond, unbond, withdraw, positionsStaking
newAddress, owns, rescanAddress handling
proveAddressAnswers an address ownership request

Messenger

wallet.messenger(tenrai) chats with other wallets, TCC-0009. Every frame pays one atom to the other side and its fee; the messenger key stays in the WASM.

JavaScript
const messenger = wallet.messenger(tenrai);
messenger.id();                                   // TRM-4CA0-35E2-…, the same on every device of this wallet
await messenger.start(address, "Hi");             // the note is public
const chats = await messenger.chats();            // rebuilt from the wallet's history
const request = chats.find((chat) => chat.state === "request");
await messenger.accept(request, "Hello");         // spends a coin of the address the request reached
await messenger.send(request, "From here on end-to-end encrypted");
StateMeaning
requestSomeone wrote to this wallet; accept opens the chat
waitingThis wallet asked; it can write once the other side accepts
openBoth sides write, every text end-to-end encrypted

MESSENGER_LIMITS gives the longest note (218 bytes), accept text (202) and message (970). chats throws when a busy node left transactions unread; what it read stays cached, so the next call asks only for the rest. Show a chat's text escaped, never as markup.

An app that keeps only a limited transaction history would lose older messages with it. exportFrames() returns the messenger frames the wallet has seen, by default up to 10,000, to store apart from the history; importFrames(frames) hands them to a new messenger, so its chats include messages older than the kept history.

Failures throw TenraiWalletError with a code: insufficient, compound-needed, fee-ceiling, fee-unsettled, too-heavy, too-many-outputs, wrong-network, invalid, rejected or unconfirmed.

Streams

JavaScript
const events = tenrai.events({ afterHeight: lastHeight });     // every finalized cut, resumed
events.on("cut", (event) => save(event.cut.height));

const status = tenrai.transactions.watch(transactionId);       // one transaction
status.on("final", (update) => console.log(update.lifecycle));

const live = tenrai.live();                                    // subscriptions by topic
live.subscribe({ topic: "cuts", onEvent: (event) => console.log(event.data.cut.height) });

waitForFinal(id, { validUntil }) resolves once a transaction is finalized or can no longer execute.

Signed answers

JavaScript
import { signedResponses } from "@tenrai/sdk/verify";

const tenrai = new TenraiClient({ verify: signedResponses({ identity: "c97d68ce…", chainId: "…" }) });

Pin the identity the operator publishes. Every REST answer is then checked, and a missing or wrong signature throws TenraiSignatureError.

Errors

ClassThrown when
TenraiApiErrorThe server refused: status, code, retryAfterMillis, retryable
TenraiNetworkError, TenraiTimeoutErrorNo answer
TenraiResponseErrorThe answer is not what the API defines
TenraiSignatureErrorA signature is missing or does not verify
TenraiWalletErrorThe wallet cannot build the transfer

All extend TenraiError.

Helpers

FunctionImportResult
parseTrai("12.00005")@tenrai/sdk1200005000n
formatTrai("1200005000")@tenrai/sdk"12.00005"
addressVerificationWords(address)@tenrai/sdkThe six verification words
burnAddress(network), isBurnAddress(address)@tenrai/sdkThe burn address of a network; whether an address is one
createAddressProofRequest, verifyAddressProof@tenrai/sdkAddress ownership
paymentRequest, parsePaymentRequest@tenrai/sdk/walletPayment requests
isValidAddress(text, network)@tenrai/sdk/walletChecks an address by the protocol rules
isMessengerPayload(payload)@tenrai/sdk/walletWhether a payload is a messenger frame, to label it instead of showing it
isChatText(text)@tenrai/sdk/walletThe text rule of the messenger

The package ships its full API reference and runnable examples.

Type to search guides, concepts and reference pages.