@rialo/spl-token 0.3.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 ADDED
@@ -0,0 +1,522 @@
1
+ # Rialo SPL Token
2
+
3
+ A TypeScript library for SPL Token-2022 operations on the Rialo blockchain. Provides account parsing, PDA derivation, instruction building, and a high-level client API for token operations.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @rialo/spl-token @rialo/ts-cdk
9
+ ```
10
+
11
+ Note: `@rialo/ts-cdk` is a peer dependency required for cryptographic primitives and RPC communication.
12
+
13
+ ## Quick Start
14
+
15
+ ### Get Token Balance
16
+
17
+ ```typescript
18
+ import { createRialoClient, PublicKey, RIALO_DEVNET_CHAIN } from "@rialo/ts-cdk";
19
+ import { createSplTokenClient } from "@rialo/spl-token";
20
+
21
+ const rialoClient = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
22
+ const tokenClient = createSplTokenClient({ client: rialoClient });
23
+
24
+ const wallet = PublicKey.fromString("7EqQdEULxWcraVx3mXKFjc84LhCkMGZCkRuDpvcMwJeK");
25
+ const mint = PublicKey.fromString("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
26
+
27
+ const balance = await tokenClient.getBalance({ wallet, mint });
28
+ console.log(`Token balance: ${balance}`);
29
+ ```
30
+
31
+ ### Get Mint Information
32
+
33
+ ```typescript
34
+ const mintInfo = await tokenClient.getMintInfo({ mint });
35
+
36
+ console.log(`Supply: ${mintInfo.supply}`);
37
+ console.log(`Decimals: ${mintInfo.decimals}`);
38
+ console.log(`Mint Authority: ${mintInfo.mintAuthority?.toString() ?? "disabled"}`);
39
+ console.log(`Freeze Authority: ${mintInfo.freezeAuthority?.toString() ?? "disabled"}`);
40
+ ```
41
+
42
+ ### Transfer Tokens
43
+
44
+ ```typescript
45
+ import { Keypair } from "@rialo/ts-cdk";
46
+
47
+ const signer = Keypair.fromSecretKey(secretKeyBytes);
48
+ const recipient = PublicKey.fromString("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU");
49
+
50
+ // Get current block height for transaction validity
51
+ const validFrom = BigInt(Date.now());
52
+
53
+ // Transfer 1 token (with 6 decimals = 1,000,000 smallest units)
54
+ const signature = await tokenClient.transfer({
55
+ params: {
56
+ source: signer.publicKey,
57
+ destination: recipient,
58
+ mint,
59
+ amount: 1_000_000n,
60
+ decimals: 6,
61
+ },
62
+ validFrom,
63
+ signer,
64
+ });
65
+
66
+ console.log(`Transfer complete: ${signature}`);
67
+ ```
68
+
69
+ ## Core Modules
70
+
71
+ ### SplTokenClient
72
+
73
+ High-level client for common token operations.
74
+
75
+ ```typescript
76
+ import { createSplTokenClient, SplTokenClient } from "@rialo/spl-token";
77
+
78
+ // Create client (defaults to Token-2022 program)
79
+ const tokenClient = createSplTokenClient({ client: rialoClient });
80
+
81
+ // Query operations
82
+ const mintInfo = await tokenClient.getMintInfo({ mint });
83
+ const balance = await tokenClient.getBalance({ wallet, mint });
84
+ const metadata = await tokenClient.getTokenMetadata({ mint });
85
+ const tokenAccountInfo = await tokenClient.getTokenAccountInfo({ tokenAccount });
86
+
87
+ // ATA operations
88
+ const { address, bump } = tokenClient.getAssociatedTokenAddress({ wallet, mint });
89
+ const exists = await tokenClient.ataExists({ wallet, mint });
90
+
91
+ // Transfer operations
92
+ const instructions = await tokenClient.createTransferInstructions(params);
93
+ const tx = await tokenClient.createTransferTransaction({ params, validFrom });
94
+ const signature = await tokenClient.transfer({ params, validFrom, signer });
95
+ ```
96
+
97
+ ### Account Parsing
98
+
99
+ Parse raw account data into structured TypeScript objects.
100
+
101
+ ```typescript
102
+ import { parseMintAccount, parseTokenAccount, parseTokenMetadata } from "@rialo/spl-token";
103
+
104
+ // Parse mint account
105
+ const accountInfo = await rialoClient.getAccountInfo(mintPubkey);
106
+ const mintInfo = parseMintAccount({ address: mintPubkey, data: accountInfo.data });
107
+
108
+ // MintInfo structure:
109
+ // {
110
+ // address: PublicKey,
111
+ // supply: bigint,
112
+ // decimals: number,
113
+ // isInitialized: boolean,
114
+ // mintAuthority: PublicKey | null,
115
+ // freezeAuthority: PublicKey | null,
116
+ // }
117
+
118
+ // Parse token account
119
+ const tokenAccountInfo = await rialoClient.getAccountInfo(tokenAccountPubkey);
120
+ const tokenAccount = parseTokenAccount({ address: tokenAccountPubkey, data: tokenAccountInfo.data });
121
+
122
+ // TokenAccountInfo structure:
123
+ // {
124
+ // address: PublicKey,
125
+ // mint: PublicKey,
126
+ // owner: PublicKey,
127
+ // amount: bigint,
128
+ // delegate: PublicKey | null,
129
+ // state: TokenAccountState,
130
+ // isNative: bigint | null,
131
+ // delegatedAmount: bigint,
132
+ // closeAuthority: PublicKey | null,
133
+ // }
134
+
135
+ // Parse Token-2022 native metadata extension
136
+ const metadata = parseTokenMetadata({ mint: mintPubkey, data: accountInfo.data });
137
+ if (metadata) {
138
+ console.log(`Name: ${metadata.name}`);
139
+ console.log(`Symbol: ${metadata.symbol}`);
140
+ console.log(`URI: ${metadata.uri}`);
141
+ }
142
+ ```
143
+
144
+ ### PDA Derivation
145
+
146
+ Derive Associated Token Addresses (ATAs) for wallet/mint combinations.
147
+
148
+ ```typescript
149
+ import {
150
+ findAssociatedTokenAddress,
151
+ getAssociatedTokenAddressSync,
152
+ } from "@rialo/spl-token";
153
+
154
+ // Get ATA with bump seed
155
+ const { address, bump } = findAssociatedTokenAddress({ wallet, mint });
156
+ console.log(`ATA: ${address.toString()}`);
157
+ console.log(`Bump: ${bump}`);
158
+
159
+ // Get just the address (convenience function)
160
+ const ata = getAssociatedTokenAddressSync({ wallet, mint });
161
+
162
+ // Use with legacy Token program
163
+ import { TOKEN_PROGRAM_ID } from "@rialo/spl-token";
164
+
165
+ const legacyProgramId = PublicKey.fromString(TOKEN_PROGRAM_ID);
166
+ const legacyAta = findAssociatedTokenAddress({ wallet, mint, programId: legacyProgramId });
167
+ ```
168
+
169
+ ### Instruction Building
170
+
171
+ Build SPL Token instructions for transactions.
172
+
173
+ ```typescript
174
+ import {
175
+ initializeMintInstruction,
176
+ mintToInstruction,
177
+ transferCheckedInstruction,
178
+ transferInstruction,
179
+ createAssociatedTokenAccountInstruction,
180
+ createAssociatedTokenAccountIdempotentInstruction,
181
+ } from "@rialo/spl-token";
182
+ import { TransactionBuilder } from "@rialo/ts-cdk";
183
+
184
+ // Initialize mint (Token-2022)
185
+ const initMintIx = initializeMintInstruction({
186
+ mint,
187
+ decimals: 6,
188
+ mintAuthority,
189
+ freezeAuthority,
190
+ });
191
+
192
+ // Mint tokens to an account
193
+ const mintToIx = mintToInstruction({
194
+ mint,
195
+ destination: destinationTokenAccount,
196
+ authority: mintAuthority,
197
+ amount: 1_000_000n,
198
+ });
199
+
200
+ // TransferChecked (recommended - includes decimals verification)
201
+ const transferIx = transferCheckedInstruction({
202
+ source: sourceTokenAccount, // Source ATA
203
+ mint, // Token mint
204
+ destination: destTokenAccount, // Destination ATA
205
+ authority, // Owner of source account (signer)
206
+ amount: 1_000_000n, // Amount in smallest units
207
+ decimals: 6, // Expected decimals
208
+ });
209
+
210
+ // Create ATA (fails if exists)
211
+ const createAtaIx = createAssociatedTokenAccountInstruction({
212
+ payer, // Account paying rent
213
+ associatedToken: ata, // ATA address to create
214
+ owner, // Wallet that will own the ATA
215
+ mint, // Token mint
216
+ });
217
+
218
+ // Create ATA idempotently (succeeds even if exists)
219
+ const createAtaIdempotentIx = createAssociatedTokenAccountIdempotentInstruction({
220
+ payer,
221
+ associatedToken: ata,
222
+ owner,
223
+ mint,
224
+ });
225
+
226
+ // Build transaction
227
+ const validFrom = BigInt(Date.now());
228
+ const tx = TransactionBuilder.create()
229
+ .setPayer(payer)
230
+ .setValidFrom(validFrom)
231
+ .addInstruction(initMintIx)
232
+ .addInstruction(createAtaIdempotentIx)
233
+ .addInstruction(mintToIx)
234
+ .addInstruction(transferIx)
235
+ .build();
236
+
237
+ const signedTx = tx.sign(keypair);
238
+ const signature = await rialoClient.sendTransaction(signedTx.serialize());
239
+ ```
240
+
241
+ ### Error Handling
242
+
243
+ Structured error handling with specific error codes.
244
+
245
+ ```typescript
246
+ import { SplTokenError, SplTokenErrorCode } from "@rialo/spl-token";
247
+
248
+ try {
249
+ const balance = await tokenClient.getBalance({ wallet, mint });
250
+ } catch (error) {
251
+ if (error instanceof SplTokenError) {
252
+ switch (error.code) {
253
+ case SplTokenErrorCode.MINT_NOT_FOUND:
254
+ console.log("Mint does not exist");
255
+ break;
256
+ case SplTokenErrorCode.TOKEN_ACCOUNT_NOT_FOUND:
257
+ console.log("Token account does not exist");
258
+ break;
259
+ case SplTokenErrorCode.ACCOUNT_NOT_INITIALIZED:
260
+ console.log("Account exists but is not initialized");
261
+ break;
262
+ case SplTokenErrorCode.INSUFFICIENT_BALANCE:
263
+ console.log(`Insufficient balance: ${error.details?.available}`);
264
+ break;
265
+ case SplTokenErrorCode.ACCOUNT_FROZEN:
266
+ console.log("Token account is frozen");
267
+ break;
268
+ default:
269
+ console.log(`Token error: ${error.message}`);
270
+ }
271
+ }
272
+ }
273
+ ```
274
+
275
+ ## Constants
276
+
277
+ ```typescript
278
+ import {
279
+ // Program IDs
280
+ TOKEN_2022_PROGRAM_ID, // "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
281
+ ASSOCIATED_TOKEN_PROGRAM_ID, // "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
282
+ TOKEN_PROGRAM_ID, // "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" (legacy)
283
+
284
+ // Account sizes
285
+ MINT_SIZE, // 82 bytes
286
+ TOKEN_ACCOUNT_SIZE, // 165 bytes
287
+
288
+ // Enums
289
+ TokenAccountState, // Uninitialized, Initialized, Frozen
290
+ TokenInstruction, // Transfer, TransferChecked, etc.
291
+ ExtensionType, // TokenMetadata, TransferFeeConfig, etc.
292
+ AccountType, // Uninitialized, Mint, Account
293
+ } from "@rialo/spl-token";
294
+ ```
295
+
296
+ ## Types
297
+
298
+ ```typescript
299
+ import type {
300
+ MintInfo,
301
+ TokenAccountInfo,
302
+ TokenMetadata,
303
+ TransferParams,
304
+ AtaDerivationResult,
305
+ TokenAccountFilter,
306
+ RawExtension,
307
+ MetadataPointerExtension,
308
+ Instruction,
309
+ AccountMeta,
310
+ } from "@rialo/spl-token";
311
+
312
+ // TransferParams for high-level transfer operations
313
+ const params: TransferParams = {
314
+ source: senderWallet, // Source wallet public key
315
+ destination: recipientWallet, // Destination wallet public key
316
+ mint: mintPubkey, // Token mint
317
+ amount: 1_000_000n, // Amount in smallest units
318
+ decimals: 6, // Token decimals
319
+ createDestinationAta: true, // Auto-create destination ATA (default: true)
320
+ };
321
+ ```
322
+
323
+ ## Token-2022 Extensions
324
+
325
+ The library supports parsing Token-2022 extension data:
326
+
327
+ ```typescript
328
+ import {
329
+ parseTokenMetadata,
330
+ parseMetadataPointerExtension,
331
+ getMintExtensions,
332
+ ExtensionType,
333
+ } from "@rialo/spl-token";
334
+
335
+ // Get all extensions from a mint
336
+ const extensions = getMintExtensions({ data: mintAccountData });
337
+ for (const ext of extensions) {
338
+ console.log(`Extension type: ${ext.type}`);
339
+ }
340
+
341
+ // Parse specific extensions
342
+ const metadata = parseTokenMetadata({ mint, data: mintAccountData });
343
+ const pointer = parseMetadataPointerExtension({ data: mintAccountData });
344
+ ```
345
+
346
+ ## Examples
347
+
348
+ ### Complete Transfer Workflow
349
+
350
+ ```typescript
351
+ import { createRialoClient, Keypair, PublicKey, RIALO_DEVNET_CHAIN } from "@rialo/ts-cdk";
352
+ import { createSplTokenClient } from "@rialo/spl-token";
353
+
354
+ async function transferTokens() {
355
+ // Setup clients
356
+ const rialoClient = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
357
+ const tokenClient = createSplTokenClient({ client: rialoClient });
358
+
359
+ // Load wallet
360
+ const sender = Keypair.fromSecretKey(/* your secret key */);
361
+ const recipient = PublicKey.fromString("RecipientAddress...");
362
+ const mint = PublicKey.fromString("TokenMintAddress...");
363
+
364
+ // Get mint info for decimals
365
+ const mintInfo = await tokenClient.getMintInfo({ mint });
366
+
367
+ // Check sender balance
368
+ const senderBalance = await tokenClient.getBalance({ wallet: sender.publicKey, mint });
369
+ console.log(`Sender balance: ${senderBalance}`);
370
+
371
+ // Calculate transfer amount (1 token)
372
+ const amount = BigInt(10 ** mintInfo.decimals);
373
+
374
+ if (senderBalance < amount) {
375
+ throw new Error("Insufficient balance");
376
+ }
377
+
378
+ // Execute transfer
379
+ const validFrom = BigInt(Date.now());
380
+ const signature = await tokenClient.transfer({
381
+ params: {
382
+ source: sender.publicKey,
383
+ destination: recipient,
384
+ mint,
385
+ amount,
386
+ decimals: mintInfo.decimals,
387
+ },
388
+ validFrom,
389
+ signer: sender,
390
+ });
391
+
392
+ console.log(`Transfer successful: ${signature}`);
393
+
394
+ // Verify new balances
395
+ const newSenderBalance = await tokenClient.getBalance({ wallet: sender.publicKey, mint });
396
+ const recipientBalance = await tokenClient.getBalance({ wallet: recipient, mint });
397
+ console.log(`New sender balance: ${newSenderBalance}`);
398
+ console.log(`Recipient balance: ${recipientBalance}`);
399
+ }
400
+ ```
401
+
402
+ ### Manual Transaction Building
403
+
404
+ ```typescript
405
+ import { TransactionBuilder, PublicKey } from "@rialo/ts-cdk";
406
+ import {
407
+ findAssociatedTokenAddress,
408
+ createAssociatedTokenAccountIdempotentInstruction,
409
+ transferCheckedInstruction,
410
+ TOKEN_2022_PROGRAM_ID,
411
+ } from "@rialo/spl-token";
412
+
413
+ async function buildTransferTransaction(
414
+ sender: PublicKey,
415
+ recipient: PublicKey,
416
+ mint: PublicKey,
417
+ amount: bigint,
418
+ decimals: number,
419
+ ) {
420
+ const programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID);
421
+
422
+ // Derive ATAs
423
+ const senderAta = findAssociatedTokenAddress({ wallet: sender, mint, programId }).address;
424
+ const recipientAta = findAssociatedTokenAddress({ wallet: recipient, mint, programId }).address;
425
+
426
+ // Build transaction with both instructions
427
+ const validFrom = BigInt(Date.now());
428
+ const tx = TransactionBuilder.create()
429
+ .setPayer(sender)
430
+ .setValidFrom(validFrom)
431
+ .addInstruction(
432
+ createAssociatedTokenAccountIdempotentInstruction({
433
+ payer: sender,
434
+ associatedToken: recipientAta,
435
+ owner: recipient,
436
+ mint,
437
+ programId,
438
+ }),
439
+ )
440
+ .addInstruction(
441
+ transferCheckedInstruction({
442
+ source: senderAta,
443
+ mint,
444
+ destination: recipientAta,
445
+ authority: sender,
446
+ amount,
447
+ decimals,
448
+ programId,
449
+ }),
450
+ )
451
+ .build();
452
+
453
+ return tx;
454
+ }
455
+ ```
456
+
457
+ ## Development
458
+
459
+ ```bash
460
+ # Install dependencies
461
+ npm install
462
+
463
+ # Build
464
+ npm run build
465
+
466
+ # Test
467
+ npm test
468
+
469
+ # Type check
470
+ npm run typecheck
471
+
472
+ # Lint
473
+ npm run lint
474
+ ```
475
+
476
+ ## API Reference
477
+
478
+ ### SplTokenClient Methods
479
+
480
+ | Method | Description |
481
+ | --- | --- |
482
+ | `getMintInfo({ mint })` | Get mint account information |
483
+ | `getTokenMetadata({ mint })` | Get Token-2022 native metadata |
484
+ | `getTokenAccountInfo({ tokenAccount })` | Get token account information |
485
+ | `getBalance({ wallet, mint })` | Get token balance for a wallet |
486
+ | `getAssociatedTokenAddress({ wallet, mint })` | Derive ATA address |
487
+ | `ataExists({ wallet, mint })` | Check if ATA exists |
488
+ | `createTransferInstructions(params)` | Build transfer instructions |
489
+ | `createTransferTransaction({ params, validFrom })` | Build complete transaction |
490
+ | `transfer({ params, validFrom, signer })` | Execute transfer |
491
+
492
+ ### Parsing Functions
493
+
494
+ | Function | Description |
495
+ | --- | --- |
496
+ | `parseMintAccount({ address, data })` | Parse mint account data |
497
+ | `parseTokenAccount({ address, data })` | Parse token account data |
498
+ | `parseTokenMetadata({ mint, data })` | Parse Token-2022 metadata extension |
499
+ | `parseMetadataPointerExtension({ data })` | Parse metadata pointer extension |
500
+ | `getMintExtensions({ data })` | Get all extensions from mint |
501
+
502
+ ### Instruction Builders
503
+
504
+ | Function | Description |
505
+ | --- | --- |
506
+ | `initializeMintInstruction({ mint, decimals, mintAuthority, freezeAuthority?, programId? })` | Build InitializeMint2 instruction |
507
+ | `mintToInstruction({ mint, destination, authority, amount, programId? })` | Build MintTo instruction |
508
+ | `transferCheckedInstruction({ source, mint, destination, authority, amount, decimals, programId? })` | Build TransferChecked instruction |
509
+ | `transferInstruction({ source, destination, authority, amount, programId? })` | Build Transfer instruction (legacy) |
510
+ | `createAssociatedTokenAccountInstruction({ payer, associatedToken, owner, mint, programId? })` | Build CreateATA instruction |
511
+ | `createAssociatedTokenAccountIdempotentInstruction({ payer, associatedToken, owner, mint, programId? })` | Build idempotent CreateATA |
512
+
513
+ ### PDA Functions
514
+
515
+ | Function | Description |
516
+ | --- | --- |
517
+ | `findAssociatedTokenAddress({ wallet, mint, programId? })` | Derive ATA with bump |
518
+ | `getAssociatedTokenAddressSync({ wallet, mint, programId? })` | Derive ATA address only |
519
+
520
+ ## License
521
+
522
+ Apache License 2.0