@rialo/ts-cdk 0.5.0-alpha.0 → 0.8.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,441 +1,502 @@
1
- # Rialo TypeScript CDK
1
+ # @rialo/ts-cdk
2
2
 
3
- [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
4
-
5
- A TypeScript library for interacting with the Rialo blockchain. Provides cryptographic primitives, transaction building, and RPC client for blockchain communication.
3
+ TypeScript library for building on the Rialo blockchain. Provides Ed25519 cryptographic
4
+ primitives, BIP39/SLIP-0010 HD wallet derivation, transaction construction and signing,
5
+ a JSON-RPC client for node communication, program deployment, and HPKE encryption for
6
+ TEE programs. Works in Node.js ≥ 18 and modern browsers (ESM and CJS bundles included).
6
7
 
7
8
  ## Installation
8
9
 
9
10
  ```bash
10
11
  npm install @rialo/ts-cdk
12
+ # or
13
+ pnpm add @rialo/ts-cdk
14
+ # or
15
+ yarn add @rialo/ts-cdk
11
16
  ```
12
17
 
13
- ## Quick Start
18
+ Requires Node.js ≥ 18. Ships both ESM (`dist/index.mjs`) and CJS (`dist/index.js`) bundles with
19
+ bundled TypeScript declarations. Works in modern browsers when bundled with Vite/webpack.
14
20
 
15
- ### Create a keypair and sign a message
21
+ ## Key public types
16
22
 
17
- ```typescript
18
- import { Keypair } from "@rialo/ts-cdk";
23
+ ### Cryptography
19
24
 
20
- const keypair = Keypair.generate();
21
- console.log("Address:", keypair.publicKey.toString());
25
+ | Export | Purpose |
26
+ |---|---|
27
+ | `Keypair` | Ed25519 keypair; generate, derive from seed, sign, verify, dispose |
28
+ | `PublicKey` | 32-byte Ed25519 public key; PDA derivation, base58 encoding |
29
+ | `Signature` | 64-byte Ed25519 signature; base58 encoding |
30
+ | `Mnemonic` | BIP39 mnemonic; generate, restore, derive keypairs via SLIP-0010 |
22
31
 
23
- const message = new TextEncoder().encode("Hello Rialo");
24
- const signature = keypair.sign(message);
25
- const isValid = keypair.verify(message, signature);
26
- ```
27
-
28
- ### Connect to the blockchain
32
+ ### Keyring management
29
33
 
30
- ```typescript
31
- import { createRialoClient, RIALO_DEVNET_CHAIN } from "@rialo/ts-cdk";
34
+ | Export | Purpose |
35
+ |---|---|
36
+ | `RialoKeyring` | Named collection of `Keypair` instances with an active signing key |
37
+ | `InMemoryKeyringProvider` | Create `RialoKeyring` from a mnemonic or a list of keypairs |
38
+ | `DerivedKeypairInfo` | Metadata for a single derived keypair (index, pubkey, path) |
32
39
 
33
- const client = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
34
-
35
- // Query blockchain
36
- const balance = await client.getBalance(keypair.publicKey);
37
- const height = await client.getBlockHeight();
38
- const chainId = client.getChainIdentifier();
39
- ```
40
-
41
- ### Build and send a transaction
40
+ ### Transactions
42
41
 
43
- ```typescript
44
- import { TransactionBuilder, transferInstruction } from "@rialo/ts-cdk";
42
+ | Export | Purpose |
43
+ |---|---|
44
+ | `TransactionBuilder` | Fluent builder for constructing transactions |
45
+ | `Transaction` | Signed/unsigned transaction; serialize, deserialize, multi-sig |
46
+ | `Message` | Compiled transaction message with account keys and instructions |
47
+ | `Instruction` | Single operation: program ID, accounts, data |
48
+ | `AccountMeta` | Account reference with `isSigner` / `isWritable` flags |
49
+ | `AccountMetaTable` | Deduplication table for account references within a message |
50
+ | `transferInstruction` | System-program transfer helper |
51
+ | `createAccount` | System-program create-account helper |
45
52
 
46
- // transaction valid from
47
- const validFrom = BigInt(Date.now());
53
+ ### RPC
48
54
 
49
- // Create transfer instruction
50
- const transfer = transferInstruction(
51
- keypair.publicKey,
52
- recipientAddress,
53
- BigInt(1_000_000) // amount in kelvins
54
- );
55
+ | Export | Purpose |
56
+ |---|---|
57
+ | `createRialoClient` | Factory for `RialoClient`; accepts a chain preset or custom config |
58
+ | `getDefaultRialoClientConfig` | Returns a `RialoClientConfig` for `"mainnet"`, `"devnet"`, `"testnet"`, or `"localnet"` |
59
+ | `RialoClient` | Full RPC surface for a Rialo node |
60
+ | `RIALO_MAINNET_CHAIN` / `…TESTNET…` / `…DEVNET…` / `…LOCALNET…` | Chain presets |
55
61
 
56
- // Build and sign transaction
57
- const tx = TransactionBuilder.create()
58
- .setPayer(keypair.publicKey)
59
- .setValidFrom(validFrom)
60
- .addInstruction(transfer)
61
- .build();
62
+ ### Other
62
63
 
63
- const signedTx = tx.sign(keypair);
64
+ | Export | Purpose |
65
+ |---|---|
66
+ | `Signer` / `KeypairSigner` | Abstract signing interface for hardware wallets and custom signers |
67
+ | `ProgramDeployment` | Deploy PolkaVM program binaries to the network |
68
+ | `RexValue` | Typed wrapper for plain or HPKE-encrypted byte payloads |
69
+ | `RialoError` / `RialoErrorType` | Structured error with discriminated type field |
64
70
 
65
- // Send to network
66
- const signature = await client.sendTransaction(signedTx.serialize());
67
- ```
71
+ ## Usage
68
72
 
69
- ### Generate and use mnemonics
73
+ ### Keypair and signing
70
74
 
71
75
  ```typescript
72
- import { Mnemonic } from "@rialo/ts-cdk";
76
+ import { Keypair, PublicKey } from "@rialo/ts-cdk";
73
77
 
74
- // Generate 12-word mnemonic
75
- const mnemonic = Mnemonic.generate();
78
+ // Generate a random keypair
79
+ const keypair = Keypair.generate();
80
+ console.log("Address:", keypair.publicKey.toString());
76
81
 
77
- // Derive keypair (default path: m/44'/756'/0'/0')
78
- const keypair = await mnemonic.toKeypair();
82
+ // Restore from a known 32-byte secret key
83
+ const restored = Keypair.fromSecretKey(secretKeyBytes);
79
84
 
80
- // Use custom derivation path
81
- const keypair2 = await mnemonic.toKeypair("m/44'/756'/0'/1'");
85
+ const message = new TextEncoder().encode("Hello Rialo");
86
+ const sig = keypair.sign(message);
87
+ const ok = keypair.verify(message, sig);
82
88
 
83
- // Restore from existing mnemonic
84
- const restored = Mnemonic.fromPhrase("your twelve word mnemonic phrase here...");
85
- const restoredKeypair = await restored.toKeypair();
89
+ // Always dispose keypairs when finished to zero out the private key.
90
+ keypair.dispose();
86
91
  ```
87
92
 
88
- ## Core Modules
93
+ ### BIP39 mnemonic and HD derivation
89
94
 
90
- ### Crypto
91
-
92
- Ed25519 cryptographic primitives for key management and signing.
95
+ Rialo uses BIP44 coin type 756 with SLIP-0010 (`m/44'/756'/{index}'/0'`).
93
96
 
94
97
  ```typescript
95
- import { Keypair, PublicKey, Signature } from "@rialo/ts-cdk";
98
+ import { Mnemonic } from "@rialo/ts-cdk";
96
99
 
97
- // Generate random keypair
98
- const keypair = Keypair.generate();
100
+ // Generate a new 12-word mnemonic (pass 256 for 24-word)
101
+ const mnemonic = Mnemonic.generate(); // 128-bit entropy, 12 words
102
+ console.log(mnemonic.toString());
103
+ console.log("Words:", mnemonic.getWordCount()); // 12
104
+
105
+ // Validate a phrase without constructing a Mnemonic
106
+ const valid = Mnemonic.isValid("word1 word2 … word12");
99
107
 
100
- // From existing secret key
101
- const keypair = Keypair.fromSecretKey(secretBytes);
108
+ // Restore from an existing phrase
109
+ const restored = Mnemonic.fromPhrase("word1 word2 … word12");
102
110
 
103
- // Public key operations
104
- const pubkey = PublicKey.fromString("base58string");
105
- const address = pubkey.toString();
106
- const bytes = pubkey.toBytes();
111
+ // Derive account keypairs
112
+ const account0 = await mnemonic.toKeypair(0); // m/44'/756'/0'/0'
113
+ const account1 = await mnemonic.toKeypair(1); // m/44'/756'/1'/0'
107
114
 
108
- // Signature operations
109
- const sig = Signature.fromBytes(signatureBytes);
115
+ // With an optional BIP39 passphrase
116
+ const secure = await mnemonic.toKeypair(0, { passphrase: "my-secret" });
110
117
 
111
- // validFrom for transactions
112
- const validFrom = BigInt(Date.now());
118
+ // Derive with an explicit full path (advanced)
119
+ const custom = await mnemonic.toKeypairWithPath("m/44'/756'/0'/0'");
120
+
121
+ // Derive multiple at once (indices 0–4)
122
+ const keypairs = await mnemonic.deriveKeypairs(5);
123
+
124
+ // Convert to raw 64-byte BIP39 seed
125
+ const seed = await mnemonic.toSeed();
113
126
  ```
114
127
 
115
- ### Transactions
128
+ ### Keyring management
116
129
 
117
- Build and sign transactions with instructions.
130
+ `RialoKeyring` manages a set of derived keypairs and tracks the active signing key,
131
+ useful when an application needs to sign on behalf of multiple accounts.
118
132
 
119
133
  ```typescript
120
- import {
121
- TransactionBuilder,
122
- transferInstruction,
123
- type Instruction,
124
- type AccountMeta
125
- } from "@rialo/ts-cdk";
134
+ import { Mnemonic, InMemoryKeyringProvider } from "@rialo/ts-cdk";
126
135
 
127
- // Simple transfer
128
- const transfer = transferInstruction(from, to, amount);
136
+ const mnemonic = Mnemonic.generate();
137
+ const provider = new InMemoryKeyringProvider();
129
138
 
130
- // Custom instruction
131
- const instruction: Instruction = {
132
- programId: PublicKey.fromString("YourProgramId"),
133
- accounts: [
134
- { pubkey: account1, isSigner: true, isWritable: true },
135
- { pubkey: account2, isSigner: false, isWritable: true },
136
- ],
137
- data: instructionData, // Uint8Array
138
- };
139
+ // Derive 3 keypairs into a single keyring
140
+ const keyring = await provider.createFromMnemonic(mnemonic, 3);
139
141
 
140
- // Build transaction
141
- const tx = TransactionBuilder.create()
142
- .setPayer(payerPublicKey)
143
- .setValidFrom(validFrom)
144
- .addInstruction(instruction)
145
- .build();
142
+ // Switch the active keypair
143
+ keyring.setActiveKeypair(2);
144
+ console.log("Active:", keyring.pubkeyString());
146
145
 
147
- // Single signer
148
- const signed = tx.sign(keypair);
146
+ // Sign with the active keypair
147
+ const sig = keyring.sign(messageBytes);
149
148
 
150
- // Multi-sig
151
- const partial = tx.partialSign(signer1);
152
- const complete = partial.partialSign(signer2);
149
+ // Sign with a specific keypair without switching active
150
+ const sig1 = keyring.signWithKeypair(messageBytes, 1);
151
+
152
+ // Inspect all keypairs
153
+ for (const info of keyring.getKeypairsInfo()) {
154
+ console.log(`index=${info.index} pubkey=${info.pubkeyString}`);
155
+ }
153
156
 
154
- // Serialize for network
155
- const bytes = signed.serialize();
157
+ // Securely zero all private keys when done
158
+ keyring.dispose();
156
159
  ```
157
160
 
158
- ### RPC Client
161
+ ### Build and send a transaction
159
162
 
160
- Communicate with Rialo blockchain nodes.
163
+ `TransactionBuilder` requires a `configHashPrefix` fetched from the network. Every
164
+ transaction submitted to a live node must carry the current prefix or it will be
165
+ rejected.
161
166
 
162
167
  ```typescript
163
168
  import {
169
+ TransactionBuilder,
170
+ transferInstruction,
164
171
  createRialoClient,
165
172
  getDefaultRialoClientConfig,
166
173
  RIALO_DEVNET_CHAIN,
167
- RIALO_MAINNET_CHAIN
168
174
  } from "@rialo/ts-cdk";
169
175
 
170
- // Using preset chain configurations
176
+ // Using a chain preset directly
171
177
  const client = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
172
178
 
173
- // Using getDefaultRialoClientConfig helper
174
- const config = getDefaultRialoClientConfig('mainnet');
175
- const mainnetClient = createRialoClient({
176
- ...config,
177
- transport: {
178
- timeout: 30000,
179
- maxRetries: 3,
180
- }
181
- });
179
+ // Or by network name — useful when network comes from config/env
180
+ // const client = createRialoClient(getDefaultRialoClientConfig("devnet"));
182
181
 
183
- // Query methods
184
- const balance = await client.getBalance(publicKey);
185
- const accountInfo = await client.getAccountInfo(publicKey);
186
- const blockHeight = await client.getBlockHeight();
187
- const chainId = client.getChainIdentifier();
188
-
189
- // Get transaction info (returns blockHeight and optional error)
190
- const txInfo = await client.getTransaction(signature);
191
- if (txInfo) {
192
- console.log(`Confirmed in block: ${txInfo.blockHeight}`);
193
- if (txInfo.err) {
194
- console.log(`Transaction failed: ${txInfo.err}`);
195
- }
196
- }
182
+ const configHashPrefix = await client.getConfigHashPrefix(); // required
197
183
 
198
- // Send transactions
199
- const signature = await client.sendTransaction(serializedTx);
184
+ const transfer = transferInstruction(
185
+ keypair.publicKey,
186
+ recipientPubkey,
187
+ 1_000_000n, // amount in kelvin; 1 RLO = 1_000_000_000 kelvin
188
+ );
200
189
 
201
- // Send and wait for confirmation
202
- const result = await client.sendAndConfirmTransaction(serializedTx);
203
- console.log(`Executed: ${result.executed}`);
190
+ const tx = TransactionBuilder.create()
191
+ .setPayer(keypair.publicKey)
192
+ .setValidFrom(BigInt(Date.now()))
193
+ .setConfigHashPrefix(configHashPrefix)
194
+ .addInstruction(transfer)
195
+ .build();
204
196
 
205
- // Confirm an existing transaction
206
- const confirmed = await client.confirmTransaction(signature, {
207
- maxRetries: 10,
208
- retryDelay: 200,
209
- });
197
+ const signedTx = tx.sign(keypair);
198
+ const sig = await client.sendTransaction(signedTx.serialize());
199
+ console.log("Signature:", sig.toString());
200
+ ```
210
201
 
211
- // Devnet/testnet only - request airdrop
212
- const airdropSig = await client.requestAirdrop(publicKey, 1_000_000_000n);
202
+ ### Multi-sig transactions
213
203
 
214
- // Request airdrop and wait for confirmation
215
- const airdropResult = await client.requestAirdropAndConfirm(
216
- publicKey,
217
- 1_000_000_000n
218
- );
204
+ ```typescript
205
+ // Each partial sign adds one signer's signature; isSigned() returns true when all
206
+ // required signers have contributed.
207
+ const partial = tx.partialSign(signer1);
208
+ const complete = partial.partialSign(signer2);
209
+ complete.ensureSigned(); // throws if any signature is missing
219
210
 
220
- // Get minimum balance for rent exemption
221
- const minBalance = await client.getMinimumBalanceForRentExemption(128);
211
+ // Async signer interface (hardware wallets, browser extensions, etc.)
212
+ const signedAsync = await tx.signWith(new KeypairSigner(keypair));
213
+ ```
222
214
 
223
- // Get fee for a message
224
- const fee = await client.getFeeForMessage(base64Message);
215
+ ### Deserialize a transaction
225
216
 
226
- // Batch query multiple accounts
227
- const accounts = await client.getMultipleAccounts([pubkey1, pubkey2]);
217
+ `Transaction.deserialize` reconstructs a transaction from its wire bytes — useful for
218
+ inspecting or re-signing transactions received from another party.
228
219
 
229
- // Get accounts by owner/program with pagination
230
- const result = await client.getAccountsByOwner(programId, { type: "programAccounts" });
220
+ ```typescript
221
+ import { Transaction } from "@rialo/ts-cdk";
231
222
 
232
- // Workflow lineage traversal
233
- const lineage = await client.getWorkflowLineage({
234
- signature: rootTxSignature,
235
- maxDepth: 5,
236
- });
223
+ // Reconstruct from wire bytes (e.g. received over a socket)
224
+ const tx = Transaction.deserialize(wireBytes);
237
225
 
238
- // Get subscription for an account and nonce
239
- const sub = await client.getSubscription(accountPubkey, "my-nonce");
226
+ // Inspect the compiled message
227
+ for (const ix of tx.message.instructions) {
228
+ console.log("program:", ix.programId.toString());
229
+ }
240
230
 
241
- // Get transactions triggered by a subscription
242
- const triggered = await client.getTriggeredTransactions(subscriptionPubkey);
231
+ // Re-sign (e.g. when acting as co-signer)
232
+ const reSigned = tx.partialSign(myKeypair);
243
233
  ```
244
234
 
245
- ### Signers
235
+ ### Borsh serialization for instruction data
246
236
 
247
- Interface for different signing strategies.
237
+ Use Borsh to encode typed parameters into the opaque `data` field of an `Instruction`.
248
238
 
249
239
  ```typescript
250
- import { KeypairSigner, type Signer } from "@rialo/ts-cdk";
240
+ import { field, serialize, deserialize } from "@dao-xyz/borsh";
241
+ import { Instruction, AccountMeta } from "@rialo/ts-cdk";
251
242
 
252
- // Keypair signer for local signing
253
- const signer = new KeypairSigner(keypair);
243
+ class TransferParams {
244
+ @field({ type: "u64" }) amount!: bigint;
245
+ constructor(amount: bigint) { this.amount = amount; }
246
+ }
254
247
 
255
- // Use signer interface
256
- const signature = await signer.sign(message);
257
- const pubkey = signer.publicKey();
248
+ const params = new TransferParams(1_000_000n);
249
+ const data = Buffer.from(serialize(params));
258
250
 
259
- // Implement custom signer for hardware wallets, etc.
260
- class CustomSigner implements Signer {
261
- async sign(message: Uint8Array): Promise<Signature> {
262
- // your signing logic
263
- }
264
- publicKey(): PublicKey {
265
- // return public key
266
- }
267
- }
251
+ const ix = new Instruction({
252
+ programId: myProgramId,
253
+ accounts: [new AccountMeta({ pubkey: myAccount, isSigner: false, isWritable: true })],
254
+ data,
255
+ });
268
256
  ```
269
257
 
270
- ## Advanced Features
271
-
272
258
  ### Program Derived Addresses (PDAs)
273
259
 
274
- PDAs are deterministic addresses derived from seeds and a program ID. They are off the Ed25519 curve, meaning no private key exists for them - only the program can sign on their behalf.
260
+ PDAs are off-curve addresses deterministically derived from seeds and a program ID.
261
+ No private key exists; only the program can sign on their behalf.
275
262
 
276
263
  ```typescript
277
264
  import { PublicKey } from "@rialo/ts-cdk";
278
265
 
279
- // Find a PDA with automatic bump seed discovery
280
- const [vaultPda, vaultBump] = PublicKey.findProgramAddress(
266
+ // Automatic bump discovery returns [address, bump]
267
+ const [vaultPda, bump] = PublicKey.findProgramAddress(
281
268
  ["vault", userPubkey.toBytes()],
282
- programId
269
+ programId,
283
270
  );
284
271
 
285
- console.log(`Vault: ${vaultPda}, Bump: ${vaultBump}`);
286
-
287
- // Create PDA with known bump (more efficient for verification)
272
+ // With known bump (more efficient)
288
273
  const pda = PublicKey.createProgramAddress(
289
- ["metadata", mintPubkey.toBytes(), new Uint8Array([254])],
290
- metadataProgramId
274
+ ["metadata", mintPubkey.toBytes(), new Uint8Array([bump])],
275
+ metadataProgramId,
291
276
  );
277
+ ```
292
278
 
293
- // Create derived address from base key (CAN be on curve)
294
- const derivedAddress = PublicKey.createWithSeed(
295
- userPubkey,
296
- "savings-account",
297
- systemProgramId
298
- );
279
+ > **Note:** Each seed must be 32 bytes; at most 16 seeds per derivation. String seeds
280
+ > are UTF-8-encoded automatically.
281
+
282
+ ### Deploy a program
283
+
284
+ ```typescript
285
+ import { ProgramDeployment } from "@rialo/ts-cdk";
286
+
287
+ const deployment = new ProgramDeployment({
288
+ programData: fs.readFileSync("program.polkavm"),
289
+ programKeypair: programKeypair,
290
+ config: {
291
+ chunkSize: 900, // bytes per upload chunk
292
+ maxRetries: 5,
293
+ },
294
+ });
299
295
 
300
- // Check if an address is on the Ed25519 curve
301
- const isOnCurve = pubkey.isOnCurve();
296
+ const programId = await deployment.deploy(client, payerKeypair);
297
+ console.log("Program deployed:", programId.toString());
302
298
  ```
303
299
 
304
- **Seed Constraints:**
300
+ ### Encrypt a secret for TEE programs
305
301
 
306
- - Maximum 16 seeds per derivation
307
- - Each seed maximum 32 bytes
308
- - Seeds can be strings (UTF-8 encoded) or `Uint8Array`
302
+ `RexValue` wraps a plain or HPKE-encrypted payload. Use it to pass sensitive data to
303
+ programs that run inside a Trusted Execution Environment (TEE). The node's HPKE public
304
+ key is fetched via `getSecretSharingPubkey()`; the CDK handles the HPKE encryption.
309
305
 
310
- ### Hierarchical Deterministic Wallets
306
+ ```typescript
307
+ import { RexValue } from "@rialo/ts-cdk";
308
+
309
+ // 1. Fetch the TEE's HPKE public key from the node
310
+ const skPub = await client.getSecretSharingPubkey();
311
+
312
+ // 2. Encrypt your secret — RexValue.fromPlaintext performs HPKE encryption
313
+ const plaintext = new TextEncoder().encode("Bearer sk-…");
314
+ const rexValue = await RexValue.fromPlaintext(plaintext, skPub);
315
+
316
+ // 3. Serialise as Borsh bytes and embed in your instruction data
317
+ const borshBytes = rexValue.toBorsh();
318
+ ```
319
+
320
+ To include unencrypted bytes (for non-sensitive payloads):
321
+
322
+ ```typescript
323
+ const plain = RexValue.plain(new TextEncoder().encode("public-data"));
324
+ const borshBytes = plain.toBorsh();
325
+ ```
311
326
 
312
- The SDK uses SLIP-0010 for Ed25519 key derivation with BIP44 paths.
327
+ ### Custom signer (hardware wallets)
313
328
 
314
329
  ```typescript
315
- // Coin type 756 (R=7, L=5, O=6 on phone keypad)
316
- const defaultPath = "m/44'/756'/0'/0'";
330
+ import { type Signer, PublicKey, Signature } from "@rialo/ts-cdk";
331
+
332
+ class LedgerSigner implements Signer {
333
+ async getPublicKey(): Promise<PublicKey> {
334
+ return PublicKey.fromBytes(await ledger.getPublicKey());
335
+ }
336
+ async signMessage(message: Uint8Array): Promise<Signature> {
337
+ const sigBytes = await ledger.sign(message);
338
+ return Signature.fromBytes(sigBytes);
339
+ }
340
+ }
317
341
 
318
- // Derive multiple accounts
319
- const account0 = await mnemonic.toKeypair(0);
320
- const account1 = await mnemonic.toKeypair(1);
321
- const account2 = await mnemonic.toKeypair(2);
342
+ const signedTx = await tx.signWith(new LedgerSigner());
322
343
  ```
323
344
 
324
- ### Transaction Serialization
345
+ ### Subscription workflows (REX)
346
+
347
+ REX subscriptions let a program trigger downstream transactions automatically. Use
348
+ `getSubscription` to inspect an active subscription and `getTriggeredTransactions` to
349
+ audit which transactions it has fired.
325
350
 
326
351
  ```typescript
327
- // Serialize signed transaction
328
- const bytes = signedTx.serialize();
352
+ import { createRialoClient, RIALO_DEVNET_CHAIN } from "@rialo/ts-cdk";
329
353
 
330
- // Deserialize
331
- import { Transaction } from "@rialo/ts-cdk";
332
- const tx = Transaction.deserialize(bytes);
354
+ const client = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
355
+
356
+ // Look up a subscription by subscriber address and nonce
357
+ const subscription = await client.getSubscription(subscriberPubkey, nonce);
358
+ console.log("Topic:", subscription.topic);
359
+ console.log("Kind:", subscription.kind);
333
360
 
334
- // Check signing status
335
- const isSigned = tx.isSigned();
336
- const sigCount = tx.getRequiredSignatureCount();
361
+ // List the last 10 transactions triggered by this subscription account
362
+ const triggered = await client.getTriggeredTransactions(subscriptionAccount, 10);
363
+ for (const tx of triggered) {
364
+ console.log(`sig=${tx.signature} block=${tx.blockNumber}`);
365
+ }
337
366
  ```
338
367
 
339
- ### Error Handling
368
+ ### Workflow lineage
369
+
370
+ ```typescript
371
+ const lineage = await client.getWorkflowLineage(request);
372
+ // lineage.nodes contains the DAG of related transactions.
373
+ ```
374
+
375
+ ## Error handling
340
376
 
341
377
  ```typescript
342
378
  import { RialoError, RialoErrorType } from "@rialo/ts-cdk";
343
379
 
344
380
  try {
345
- await client.sendTransaction(tx);
346
- } catch (error) {
347
- if (error instanceof RialoError) {
348
- switch (error.type) {
381
+ await client.sendTransaction(signedTx.serialize());
382
+ } catch (err) {
383
+ if (err instanceof RialoError) {
384
+ switch (err.type) {
385
+ case RialoErrorType.RPC:
386
+ console.error("Node error", err.details?.code, err.details?.message);
387
+ break;
349
388
  case RialoErrorType.NETWORK:
350
- // Retry logic
389
+ console.error("Network failure:", err.cause);
351
390
  break;
352
391
  case RialoErrorType.INVALID_INPUT:
353
- // Handle invalid input
354
- break;
355
- case RialoErrorType.RPC:
356
- // RPC specific error
357
- console.log(error.details); // RPC error details
392
+ console.error("Bad argument:", err.message);
358
393
  break;
394
+ default:
395
+ throw err;
359
396
  }
360
397
  }
361
398
  }
362
399
  ```
363
400
 
364
- ### Keypair Security
365
-
366
- ```typescript
367
- // Generate keypair
368
- const keypair = Keypair.generate();
369
-
370
- // Use for signing
371
- const signature = keypair.sign(message);
372
-
373
- // Securely dispose when done
374
- keypair.dispose(); // Zeros out private key in memory
375
- ```
376
-
377
- ## Network URLs
378
-
379
- ```typescript
380
- import {
381
- URL_MAINNET,
382
- URL_TESTNET,
383
- URL_DEVNET,
384
- URL_LOCALNET
385
- } from "@rialo/ts-cdk";
386
- ```
401
+ ### Error types
402
+
403
+ | `RialoErrorType` | Condition |
404
+ |---|---|
405
+ | `RPC` | Node rejected the request |
406
+ | `NETWORK` | HTTP/network failure |
407
+ | `TRANSACTION` | Transaction build or signing error |
408
+ | `WALLET` | Keypair load or create failure |
409
+ | `ENCRYPTION` | HPKE encryption/decryption failure |
410
+ | `BIP32` | HD key derivation error |
411
+ | `INVALID_INPUT` | Invalid parameter or data |
412
+ | `PARSE_PUBKEY` | Public key parse failure |
413
+ | `INVALID_BLOCKHASH_FORMAT` | Blockhash format error |
414
+ | `CONFIG` | Configuration load or parse failure |
415
+ | `JSON` | JSON parse error |
416
+ | `SERIALIZATION` | Borsh/bincode serialisation error |
417
+ | `PASSWORD` | Password validation failure |
418
+
419
+ ## RPC reference
420
+
421
+ | Method | Returns | Notes |
422
+ |---|---|---|
423
+ | `getConfigHashPrefix()` | `ConfigHashPrefix` | Required for every `TransactionBuilder` |
424
+ | `getBalance(pubkey)` | `Kelvin` | Balance as `bigint` |
425
+ | `sendTransaction(bytes, opts?)` | `Signature` | Submit signed bytes |
426
+ | `sendAndConfirmTransaction(bytes, opts?)` | `ConfirmedTransaction` | Submit and poll |
427
+ | `confirmTransaction(sig, opts?)` | `ConfirmedTransaction` | Poll by signature |
428
+ | `getAccountInfo(pubkey)` | `AccountInfo` | Raw account data |
429
+ | `getMultipleAccounts(pubkeys)` | `OptionalAccountInfo[]` | Batch query |
430
+ | `getAccountsByOwner(owner, filter?)` | `[OwnerAccount[], PaginationInfo?]` | Program accounts |
431
+ | `getBlockHeight()` | `bigint` | Finalized height |
432
+ | `getTransaction(sig)` | `TransactionResponse` | By signature |
433
+ | `getEpochInfo()` | `EpochInfo` | Current epoch |
434
+ | `getSignaturesForAddress(pubkey)` | `SignatureInfo[]` | Transaction history for an account |
435
+ | `requestAirdrop(pubkey, kelvin)` | `Signature` | Devnet/testnet only |
436
+ | `requestAirdropAndConfirm(pubkey, kelvin)` | `ConfirmedTransaction` | Airdrop + wait |
437
+ | `getFeeForMessage(base64Msg)` | `FeeResponse` | Estimate fee before sending |
438
+ | `getMinimumBalanceForRentExemption(size)` | `bigint` | Rent-exempt threshold |
439
+ | `getSecretSharingPubkey()` | `SecretSharingPubkey` | TEE HPKE public key |
440
+ | `getWorkflowLineage(req)` | `GetWorkflowLineageResponse` | Workflow DAG traversal |
441
+ | `getSubscription(subscriber, nonce)` | `Subscription` | REX subscription lookup |
442
+ | `getTriggeredTransactions(account, limit?)` | `TriggeredTransaction[]` | Subscription-triggered txs |
387
443
 
388
444
  ## Constants
389
445
 
390
446
  ```typescript
391
447
  import {
392
- KELVIN_PER_RLO, // 1_000_000_000
393
- SYSTEM_PROGRAM_ID, // "11111111111111111111111111111111"
394
- PUBLIC_KEY_LENGTH, // 32
395
- SECRET_KEY_LENGTH, // 32
396
- SIGNATURE_LENGTH, // 64
448
+ KELVIN_PER_RLO, // 1_000_000_000
449
+ SYSTEM_PROGRAM_ID, // "11111111111111111111111111111111"
450
+ PUBLIC_KEY_LENGTH, // 32
451
+ SECRET_KEY_LENGTH, // 32
452
+ SIGNATURE_LENGTH, // 64
453
+ URL_MAINNET, // "https://mainnet.rialo.io:4101"
454
+ URL_TESTNET, // "https://testnet.rialo.io:4101"
455
+ URL_DEVNET, // "https://devnet.rialo.io:4101"
456
+ URL_LOCALNET, // "http://localhost:4104"
397
457
  } from "@rialo/ts-cdk";
458
+
459
+ // Currency conversion (KELVIN_PER_RLO is a number; use BigInt() when passing to RPC methods)
460
+ const kelvin = BigInt(Math.round(1.5 * KELVIN_PER_RLO)); // 1.5 RLO → 1_500_000_000n kelvin
398
461
  ```
399
462
 
400
- ## Examples
463
+ ## Caveats
401
464
 
402
- The `examples/` directory contains complete working examples:
465
+ - **`configHashPrefix`** `TransactionBuilder.setConfigHashPrefix()` is required.
466
+ Omitting it or using a stale value causes the node to reject the transaction with a
467
+ replay-protection error.
403
468
 
404
- - `01-basic-operations.ts` - Keypairs, signatures, and basic types
405
- - `02-wallet-management.ts` - HD wallet patterns (reference implementation)
406
- - `03-transaction-operations.ts` - Transaction building and signing
407
- - `05-alice-bob-transaction.ts` - Complete transfer workflow with RPC
469
+ - **Currency units** all RPC amounts are in *kelvin*; 1 RLO = `KELVIN_PER_RLO`
470
+ (1 000 000 000) kelvin. The `transferInstruction` helper takes kelvin as `bigint`.
408
471
 
409
- Run examples:
472
+ - **Derivation path** — Rialo uses BIP44 coin type 756. `Mnemonic.toKeypair(index)` uses
473
+ `m/44'/756'/{index}'/0'`. Keys derived on other coin types are not compatible.
410
474
 
411
- ```bash
412
- pnpm tsx examples/01-basic-operations.ts
413
- ```
475
+ - **Keypair disposal** — call `keypair.dispose()` (and `keyring.dispose()`) when the
476
+ keypair is no longer needed. After disposal, signing methods throw `CryptoError`.
414
477
 
415
- ## Development
478
+ - **PDA seeds** — strings are UTF-8-encoded; `Uint8Array` seeds are used as-is. A seed
479
+ exceeding 32 bytes throws `CryptoError.MAX_SEED_LENGTH_EXCEEDED`.
416
480
 
417
- ```bash
418
- # Install dependencies
419
- pnpm install
481
+ - **Node.js version** — requires Node.js ≥ 18 for the native `crypto` module and
482
+ `TextEncoder`/`TextDecoder` globals.
420
483
 
421
- # Build
422
- pnpm build
484
+ - **`AccountMetaTable`** — used internally by `TransactionBuilder` to deduplicate account
485
+ references in a compiled `Message`. Constructing it manually is only needed for
486
+ advanced multi-program transactions.
423
487
 
424
- # Test
425
- pnpm test
488
+ ## Development
426
489
 
427
- # Lint
428
- pnpm lint
490
+ ```bash
491
+ pnpm install
492
+ pnpm build # compiles ESM + CJS bundles to dist/
493
+ pnpm test # runs vitest suite
494
+ pnpm lint # biome check
429
495
  ```
430
496
 
431
- ## Security
432
-
433
- - Private keys stored as `Uint8Array` in memory
434
- - Uses `@noble/curves` and `@noble/hashes` for cryptography
435
- - Call `keypair.dispose()` to zero out private keys
436
- - Never commit private keys or mnemonics
437
- - Use environment variables for sensitive data
438
-
439
- ## License
497
+ ## See also
440
498
 
441
- Apache License 2.0
499
+ - [`rialo-cdk`](../rialo-rs-cdk/README.md) — underlying Rust crate; source of truth for wire format and error codes
500
+ - [`rialo-py-cdk`](../rialo-py-cdk/README.md) — Python bindings with equivalent API surface
501
+ - `developer-frameworks/cdk/spec.wit` — WIT specification from which the RPC surface is derived
502
+ - `developer-frameworks/cdk/rialo-ts-cdk/examples/` — runnable end-to-end examples