@rialo/ts-cdk
TypeScript library for building on the Rialo blockchain. Provides Ed25519 cryptographic primitives, BIP39/SLIP-0010 HD wallet derivation, transaction construction and signing, a JSON-RPC client for node communication, program deployment, and HPKE encryption for TEE programs. Works in Node.js ≥ 18 and modern browsers (ESM and CJS bundles included).
Installation
npm install @rialo/ts-cdk
# or
pnpm add @rialo/ts-cdk
# or
yarn add @rialo/ts-cdkRequires Node.js ≥ 18. Ships both ESM (dist/index.mjs) and CJS (dist/index.js) bundles with
bundled TypeScript declarations. Works in modern browsers when bundled with Vite/webpack.
Key public types
Cryptography
| Export | Purpose |
|---|---|
Keypair |
Ed25519 keypair; generate, derive from seed, sign, verify, dispose |
PublicKey |
32-byte Ed25519 public key; PDA derivation, base58 encoding |
Signature |
64-byte Ed25519 signature; base58 encoding |
Mnemonic |
BIP39 mnemonic; generate, restore, derive keypairs via SLIP-0010 |
Keyring management
| Export | Purpose |
|---|---|
RialoKeyring |
Named collection of Keypair instances with an active signing key |
InMemoryKeyringProvider |
Create RialoKeyring from a mnemonic or a list of keypairs |
DerivedKeypairInfo |
Metadata for a single derived keypair (index, pubkey, path) |
Transactions
| Export | Purpose |
|---|---|
TransactionBuilder |
Fluent builder for constructing transactions |
Transaction |
Signed/unsigned transaction; serialize, deserialize, multi-sig |
Message |
Compiled transaction message with account keys and instructions |
Instruction |
Single operation: program ID, accounts, data |
AccountMeta |
Account reference with isSigner / isWritable flags |
AccountMetaTable |
Deduplication table for account references within a message |
transferInstruction |
System-program transfer helper |
createAccount |
System-program create-account helper |
RPC
| Export | Purpose |
|---|---|
createRialoClient |
Factory for RialoClient; accepts a chain preset or custom config |
getDefaultRialoClientConfig |
Returns a RialoClientConfig for "mainnet", "devnet", "testnet", or "localnet" |
RialoClient |
Full RPC surface for a Rialo node |
RIALO_MAINNET_CHAIN / …TESTNET… / …DEVNET… / …LOCALNET… |
Chain presets |
Other
| Export | Purpose |
|---|---|
Signer / KeypairSigner |
Abstract signing interface for hardware wallets and custom signers |
ProgramDeployment |
Deploy PolkaVM program binaries to the network |
RexValue |
Typed wrapper for plain or HPKE-encrypted byte payloads |
RialoError / RialoErrorType |
Structured error with discriminated type field |
Usage
Keypair and signing
import { Keypair, PublicKey } from "@rialo/ts-cdk";
// Generate a random keypair
const keypair = Keypair.generate();
console.log("Address:", keypair.publicKey.toString());
// Restore from a known 32-byte secret key
const restored = Keypair.fromSecretKey(secretKeyBytes);
const message = new TextEncoder().encode("Hello Rialo");
const sig = keypair.sign(message);
const ok = keypair.verify(message, sig);
// Always dispose keypairs when finished to zero out the private key.
keypair.dispose();BIP39 mnemonic and HD derivation
Rialo uses BIP44 coin type 756 with SLIP-0010 (m/44'/756'/{index}'/0').
import { Mnemonic } from "@rialo/ts-cdk";
// Generate a new 12-word mnemonic (pass 256 for 24-word)
const mnemonic = Mnemonic.generate(); // 128-bit entropy, 12 words
console.log(mnemonic.toString());
console.log("Words:", mnemonic.getWordCount()); // 12
// Validate a phrase without constructing a Mnemonic
const valid = Mnemonic.isValid("word1 word2 … word12");
// Restore from an existing phrase
const restored = Mnemonic.fromPhrase("word1 word2 … word12");
// Derive account keypairs
const account0 = await mnemonic.toKeypair(0); // m/44'/756'/0'/0'
const account1 = await mnemonic.toKeypair(1); // m/44'/756'/1'/0'
// With an optional BIP39 passphrase
const secure = await mnemonic.toKeypair(0, { passphrase: "my-secret" });
// Derive with an explicit full path (advanced)
const custom = await mnemonic.toKeypairWithPath("m/44'/756'/0'/0'");
// Derive multiple at once (indices 0–4)
const keypairs = await mnemonic.deriveKeypairs(5);
// Convert to raw 64-byte BIP39 seed
const seed = await mnemonic.toSeed();Keyring management
RialoKeyring manages a set of derived keypairs and tracks the active signing key,
useful when an application needs to sign on behalf of multiple accounts.
import { Mnemonic, InMemoryKeyringProvider } from "@rialo/ts-cdk";
const mnemonic = Mnemonic.generate();
const provider = new InMemoryKeyringProvider();
// Derive 3 keypairs into a single keyring
const keyring = await provider.createFromMnemonic(mnemonic, 3);
// Switch the active keypair
keyring.setActiveKeypair(2);
console.log("Active:", keyring.pubkeyString());
// Sign with the active keypair
const sig = keyring.sign(messageBytes);
// Sign with a specific keypair without switching active
const sig1 = keyring.signWithKeypair(messageBytes, 1);
// Inspect all keypairs
for (const info of keyring.getKeypairsInfo()) {
console.log(`index=${info.index} pubkey=${info.pubkeyString}`);
}
// Securely zero all private keys when done
keyring.dispose();Build and send a transaction
TransactionBuilder requires a configHashPrefix fetched from the network. Every
transaction submitted to a live node must carry the current prefix or it will be
rejected.
import {
TransactionBuilder,
transferInstruction,
createRialoClient,
getDefaultRialoClientConfig,
RIALO_DEVNET_CHAIN,
} from "@rialo/ts-cdk";
// Using a chain preset directly
const client = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
// Or by network name — useful when network comes from config/env
// const client = createRialoClient(getDefaultRialoClientConfig("devnet"));
const configHashPrefix = await client.getConfigHashPrefix(); // required
const transfer = transferInstruction(
keypair.publicKey,
recipientPubkey,
1_000_000n, // amount in kelvin; 1 RLO = 1_000_000_000 kelvin
);
const tx = TransactionBuilder.create()
.setPayer(keypair.publicKey)
.setValidFrom(BigInt(Date.now()))
.setConfigHashPrefix(configHashPrefix)
.addInstruction(transfer)
.build();
const signedTx = tx.sign(keypair);
const sig = await client.sendTransaction(signedTx.serialize());
console.log("Signature:", sig.toString());Multi-sig transactions
// Each partial sign adds one signer's signature; isSigned() returns true when all
// required signers have contributed.
const partial = tx.partialSign(signer1);
const complete = partial.partialSign(signer2);
complete.ensureSigned(); // throws if any signature is missing
// Async signer interface (hardware wallets, browser extensions, etc.)
const signedAsync = await tx.signWith(new KeypairSigner(keypair));Deserialize a transaction
Transaction.deserialize reconstructs a transaction from its wire bytes — useful for
inspecting or re-signing transactions received from another party.
import { Transaction } from "@rialo/ts-cdk";
// Reconstruct from wire bytes (e.g. received over a socket)
const tx = Transaction.deserialize(wireBytes);
// Inspect the compiled message
for (const ix of tx.message.instructions) {
console.log("program:", ix.programId.toString());
}
// Re-sign (e.g. when acting as co-signer)
const reSigned = tx.partialSign(myKeypair);Borsh serialization for instruction data
Use Borsh to encode typed parameters into the opaque data field of an Instruction.
import { field, serialize, deserialize } from "@dao-xyz/borsh";
import { Instruction, AccountMeta } from "@rialo/ts-cdk";
class TransferParams {
@field({ type: "u64" }) amount!: bigint;
constructor(amount: bigint) { this.amount = amount; }
}
const params = new TransferParams(1_000_000n);
const data = Buffer.from(serialize(params));
const ix = new Instruction({
programId: myProgramId,
accounts: [new AccountMeta({ pubkey: myAccount, isSigner: false, isWritable: true })],
data,
});Program Derived Addresses (PDAs)
PDAs are off-curve addresses deterministically derived from seeds and a program ID. No private key exists; only the program can sign on their behalf.
import { PublicKey } from "@rialo/ts-cdk";
// Automatic bump discovery — returns [address, bump]
const [vaultPda, bump] = PublicKey.findProgramAddress(
["vault", userPubkey.toBytes()],
programId,
);
// With known bump (more efficient)
const pda = PublicKey.createProgramAddress(
["metadata", mintPubkey.toBytes(), new Uint8Array([bump])],
metadataProgramId,
);Note: Each seed must be ≤ 32 bytes; at most 16 seeds per derivation. String seeds are UTF-8-encoded automatically.
Deploy a program
import { ProgramDeployment } from "@rialo/ts-cdk";
const deployment = new ProgramDeployment({
programData: fs.readFileSync("program.polkavm"),
programKeypair: programKeypair,
config: {
chunkSize: 900, // bytes per upload chunk
maxRetries: 5,
},
});
const programId = await deployment.deploy(client, payerKeypair);
console.log("Program deployed:", programId.toString());Encrypt a secret for TEE programs
RexValue wraps a plain or HPKE-encrypted payload. Use it to pass sensitive data to
programs that run inside a Trusted Execution Environment (TEE). The node's HPKE public
key is fetched via getSecretSharingPubkey(); the CDK handles the HPKE encryption.
import { RexValue } from "@rialo/ts-cdk";
// 1. Fetch the TEE's HPKE public key from the node
const skPub = await client.getSecretSharingPubkey();
// 2. Encrypt your secret — RexValue.fromPlaintext performs HPKE encryption
const plaintext = new TextEncoder().encode("Bearer sk-…");
const rexValue = await RexValue.fromPlaintext(plaintext, skPub);
// 3. Serialise as Borsh bytes and embed in your instruction data
const borshBytes = rexValue.toBorsh();To include unencrypted bytes (for non-sensitive payloads):
const plain = RexValue.plain(new TextEncoder().encode("public-data"));
const borshBytes = plain.toBorsh();Custom signer (hardware wallets)
import { type Signer, PublicKey, Signature } from "@rialo/ts-cdk";
class LedgerSigner implements Signer {
async getPublicKey(): Promise<PublicKey> {
return PublicKey.fromBytes(await ledger.getPublicKey());
}
async signMessage(message: Uint8Array): Promise<Signature> {
const sigBytes = await ledger.sign(message);
return Signature.fromBytes(sigBytes);
}
}
const signedTx = await tx.signWith(new LedgerSigner());Subscription workflows (REX)
REX subscriptions let a program trigger downstream transactions automatically. Use
getSubscription to inspect an active subscription and getTriggeredTransactions to
audit which transactions it has fired.
import { createRialoClient, RIALO_DEVNET_CHAIN } from "@rialo/ts-cdk";
const client = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
// Look up a subscription by subscriber address and nonce
const subscription = await client.getSubscription(subscriberPubkey, nonce);
console.log("Topic:", subscription.topic);
console.log("Kind:", subscription.kind);
// List the last 10 transactions triggered by this subscription account
const triggered = await client.getTriggeredTransactions(subscriptionAccount, 10);
for (const tx of triggered) {
console.log(`sig=${tx.signature} block=${tx.blockNumber}`);
}Workflow lineage
const lineage = await client.getWorkflowLineage(request);
// lineage.nodes contains the DAG of related transactions.Error handling
import { RialoError, RialoErrorType } from "@rialo/ts-cdk";
try {
await client.sendTransaction(signedTx.serialize());
} catch (err) {
if (err instanceof RialoError) {
switch (err.type) {
case RialoErrorType.RPC:
console.error("Node error", err.details?.code, err.details?.message);
break;
case RialoErrorType.NETWORK:
console.error("Network failure:", err.cause);
break;
case RialoErrorType.INVALID_INPUT:
console.error("Bad argument:", err.message);
break;
default:
throw err;
}
}
}Error types
RialoErrorType |
Condition |
|---|---|
RPC |
Node rejected the request |
NETWORK |
HTTP/network failure |
TRANSACTION |
Transaction build or signing error |
WALLET |
Keypair load or create failure |
ENCRYPTION |
HPKE encryption/decryption failure |
BIP32 |
HD key derivation error |
INVALID_INPUT |
Invalid parameter or data |
PARSE_PUBKEY |
Public key parse failure |
INVALID_BLOCKHASH_FORMAT |
Blockhash format error |
CONFIG |
Configuration load or parse failure |
JSON |
JSON parse error |
SERIALIZATION |
Borsh/bincode serialisation error |
PASSWORD |
Password validation failure |
RPC reference
| Method | Returns | Notes |
|---|---|---|
getConfigHashPrefix() |
ConfigHashPrefix |
Required for every TransactionBuilder |
getBalance(pubkey) |
Kelvin |
Balance as bigint |
sendTransaction(bytes, opts?) |
Signature |
Submit signed bytes |
sendAndConfirmTransaction(bytes, opts?) |
ConfirmedTransaction |
Submit and poll |
confirmTransaction(sig, opts?) |
ConfirmedTransaction |
Poll by signature |
getAccountInfo(pubkey) |
AccountInfo |
Raw account data |
getMultipleAccounts(pubkeys) |
OptionalAccountInfo[] |
Batch query |
getAccountsByOwner(owner, filter?) |
[OwnerAccount[], PaginationInfo?] |
Program accounts |
getBlockHeight() |
bigint |
Finalized height |
getTransaction(sig) |
TransactionResponse |
By signature |
getEpochInfo() |
EpochInfo |
Current epoch |
getSignaturesForAddress(pubkey) |
SignatureInfo[] |
Transaction history for an account |
requestAirdrop(pubkey, kelvin) |
Signature |
Devnet/testnet only |
requestAirdropAndConfirm(pubkey, kelvin) |
ConfirmedTransaction |
Airdrop + wait |
getFeeForMessage(base64Msg) |
FeeResponse |
Estimate fee before sending |
getMinimumBalanceForRentExemption(size) |
bigint |
Rent-exempt threshold |
getSecretSharingPubkey() |
SecretSharingPubkey |
TEE HPKE public key |
getWorkflowLineage(req) |
GetWorkflowLineageResponse |
Workflow DAG traversal |
getSubscription(subscriber, nonce) |
Subscription |
REX subscription lookup |
getTriggeredTransactions(account, limit?) |
TriggeredTransaction[] |
Subscription-triggered txs |
Constants
import {
KELVIN_PER_RLO, // 1_000_000_000
SYSTEM_PROGRAM_ID, // "11111111111111111111111111111111"
PUBLIC_KEY_LENGTH, // 32
SECRET_KEY_LENGTH, // 32
SIGNATURE_LENGTH, // 64
URL_MAINNET, // "https://mainnet.rialo.io:4101"
URL_TESTNET, // "https://testnet.rialo.io:4101"
URL_DEVNET, // "https://devnet.rialo.io:4101"
URL_LOCALNET, // "http://localhost:4104"
} from "@rialo/ts-cdk";
// Currency conversion (KELVIN_PER_RLO is a number; use BigInt() when passing to RPC methods)
const kelvin = BigInt(Math.round(1.5 * KELVIN_PER_RLO)); // 1.5 RLO → 1_500_000_000n kelvinCaveats
configHashPrefix—TransactionBuilder.setConfigHashPrefix()is required. Omitting it or using a stale value causes the node to reject the transaction with a replay-protection error.Currency units — all RPC amounts are in kelvin; 1 RLO =
KELVIN_PER_RLO(1 000 000 000) kelvin. ThetransferInstructionhelper takes kelvin asbigint.Derivation path — Rialo uses BIP44 coin type 756.
Mnemonic.toKeypair(index)usesm/44'/756'/{index}'/0'. Keys derived on other coin types are not compatible.Keypair disposal — call
keypair.dispose()(andkeyring.dispose()) when the keypair is no longer needed. After disposal, signing methods throwCryptoError.PDA seeds — strings are UTF-8-encoded;
Uint8Arrayseeds are used as-is. A seed exceeding 32 bytes throwsCryptoError.MAX_SEED_LENGTH_EXCEEDED.Node.js version — requires Node.js ≥ 18 for the native
cryptomodule andTextEncoder/TextDecoderglobals.AccountMetaTable— used internally byTransactionBuilderto deduplicate account references in a compiledMessage. Constructing it manually is only needed for advanced multi-program transactions.
Development
pnpm install
pnpm build # compiles ESM + CJS bundles to dist/
pnpm test # runs vitest suite
pnpm lint # biome checkSee also
rialo-cdk— underlying Rust crate; source of truth for wire format and error codesrialo-py-cdk— Python bindings with equivalent API surfacedeveloper-frameworks/cdk/spec.wit— WIT specification from which the RPC surface is deriveddeveloper-frameworks/cdk/rialo-ts-cdk/examples/— runnable end-to-end examples