@rialo/spl-token
v0.4.2
Published
SPL Token-2022 client library for the Rialo blockchain
Readme
Rialo SPL Token
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.
Installation
npm install @rialo/spl-token @rialo/ts-cdkNote: @rialo/ts-cdk is a peer dependency required for cryptographic primitives and RPC communication.
Quick Start
Get Token Balance
import { createRialoClient, PublicKey, RIALO_DEVNET_CHAIN } from "@rialo/ts-cdk";
import { createSplTokenClient } from "@rialo/spl-token";
const rialoClient = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
const tokenClient = createSplTokenClient({ client: rialoClient });
const wallet = PublicKey.fromString("7EqQdEULxWcraVx3mXKFjc84LhCkMGZCkRuDpvcMwJeK");
const mint = PublicKey.fromString("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
const balance = await tokenClient.getBalance({ wallet, mint });
console.log(`Token balance: ${balance}`);Get Mint Information
const mintInfo = await tokenClient.getMintInfo({ mint });
console.log(`Supply: ${mintInfo.supply}`);
console.log(`Decimals: ${mintInfo.decimals}`);
console.log(`Mint Authority: ${mintInfo.mintAuthority?.toString() ?? "disabled"}`);
console.log(`Freeze Authority: ${mintInfo.freezeAuthority?.toString() ?? "disabled"}`);Transfer Tokens
import { Keypair } from "@rialo/ts-cdk";
const signer = Keypair.fromSecretKey(secretKeyBytes);
const recipient = PublicKey.fromString("4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU");
// Get current block height for transaction validity
const validFrom = BigInt(Date.now());
// Transfer 1 token (with 6 decimals = 1,000,000 smallest units)
const signature = await tokenClient.transfer({
params: {
source: signer.publicKey,
destination: recipient,
mint,
amount: 1_000_000n,
decimals: 6,
},
validFrom,
signer,
});
console.log(`Transfer complete: ${signature}`);Core Modules
SplTokenClient
High-level client for common token operations.
import { createSplTokenClient, SplTokenClient } from "@rialo/spl-token";
// Create client (defaults to Token-2022 program)
const tokenClient = createSplTokenClient({ client: rialoClient });
// Query operations
const mintInfo = await tokenClient.getMintInfo({ mint });
const balance = await tokenClient.getBalance({ wallet, mint });
const metadata = await tokenClient.getTokenMetadata({ mint });
const tokenAccountInfo = await tokenClient.getTokenAccountInfo({ tokenAccount });
// ATA operations
const { address, bump } = tokenClient.getAssociatedTokenAddress({ wallet, mint });
const exists = await tokenClient.ataExists({ wallet, mint });
// Transfer operations
const instructions = await tokenClient.createTransferInstructions(params);
const tx = await tokenClient.createTransferTransaction({ params, validFrom });
const signature = await tokenClient.transfer({ params, validFrom, signer });Account Parsing
Parse raw account data into structured TypeScript objects.
import { parseMintAccount, parseTokenAccount, parseTokenMetadata } from "@rialo/spl-token";
// Parse mint account
const accountInfo = await rialoClient.getAccountInfo(mintPubkey);
const mintInfo = parseMintAccount({ address: mintPubkey, data: accountInfo.data });
// MintInfo structure:
// {
// address: PublicKey,
// supply: bigint,
// decimals: number,
// isInitialized: boolean,
// mintAuthority: PublicKey | null,
// freezeAuthority: PublicKey | null,
// }
// Parse token account
const tokenAccountInfo = await rialoClient.getAccountInfo(tokenAccountPubkey);
const tokenAccount = parseTokenAccount({ address: tokenAccountPubkey, data: tokenAccountInfo.data });
// TokenAccountInfo structure:
// {
// address: PublicKey,
// mint: PublicKey,
// owner: PublicKey,
// amount: bigint,
// delegate: PublicKey | null,
// state: TokenAccountState,
// isNative: bigint | null,
// delegatedAmount: bigint,
// closeAuthority: PublicKey | null,
// }
// Parse Token-2022 native metadata extension
const metadata = parseTokenMetadata({ mint: mintPubkey, data: accountInfo.data });
if (metadata) {
console.log(`Name: ${metadata.name}`);
console.log(`Symbol: ${metadata.symbol}`);
console.log(`URI: ${metadata.uri}`);
}PDA Derivation
Derive Associated Token Addresses (ATAs) for wallet/mint combinations.
import {
findAssociatedTokenAddress,
getAssociatedTokenAddressSync,
} from "@rialo/spl-token";
// Get ATA with bump seed
const { address, bump } = findAssociatedTokenAddress({ wallet, mint });
console.log(`ATA: ${address.toString()}`);
console.log(`Bump: ${bump}`);
// Get just the address (convenience function)
const ata = getAssociatedTokenAddressSync({ wallet, mint });
// Use with legacy Token program
import { TOKEN_PROGRAM_ID } from "@rialo/spl-token";
const legacyProgramId = PublicKey.fromString(TOKEN_PROGRAM_ID);
const legacyAta = findAssociatedTokenAddress({ wallet, mint, programId: legacyProgramId });Instruction Building
Build SPL Token instructions for transactions.
import {
initializeMintInstruction,
mintToInstruction,
transferCheckedInstruction,
transferInstruction,
createAssociatedTokenAccountInstruction,
createAssociatedTokenAccountIdempotentInstruction,
} from "@rialo/spl-token";
import { TransactionBuilder } from "@rialo/ts-cdk";
// Initialize mint (Token-2022)
const initMintIx = initializeMintInstruction({
mint,
decimals: 6,
mintAuthority,
freezeAuthority,
});
// Mint tokens to an account
const mintToIx = mintToInstruction({
mint,
destination: destinationTokenAccount,
authority: mintAuthority,
amount: 1_000_000n,
});
// TransferChecked (recommended - includes decimals verification)
const transferIx = transferCheckedInstruction({
source: sourceTokenAccount, // Source ATA
mint, // Token mint
destination: destTokenAccount, // Destination ATA
authority, // Owner of source account (signer)
amount: 1_000_000n, // Amount in smallest units
decimals: 6, // Expected decimals
});
// Create ATA (fails if exists)
const createAtaIx = createAssociatedTokenAccountInstruction({
payer, // Account paying rent
associatedToken: ata, // ATA address to create
owner, // Wallet that will own the ATA
mint, // Token mint
});
// Create ATA idempotently (succeeds even if exists)
const createAtaIdempotentIx = createAssociatedTokenAccountIdempotentInstruction({
payer,
associatedToken: ata,
owner,
mint,
});
// Build transaction
const validFrom = BigInt(Date.now());
const tx = TransactionBuilder.create()
.setPayer(payer)
.setValidFrom(validFrom)
.addInstruction(initMintIx)
.addInstruction(createAtaIdempotentIx)
.addInstruction(mintToIx)
.addInstruction(transferIx)
.build();
const signedTx = tx.sign(keypair);
const signature = await rialoClient.sendTransaction(signedTx.serialize());Error Handling
Structured error handling with specific error codes.
import { SplTokenError, SplTokenErrorCode } from "@rialo/spl-token";
try {
const balance = await tokenClient.getBalance({ wallet, mint });
} catch (error) {
if (error instanceof SplTokenError) {
switch (error.code) {
case SplTokenErrorCode.MINT_NOT_FOUND:
console.log("Mint does not exist");
break;
case SplTokenErrorCode.TOKEN_ACCOUNT_NOT_FOUND:
console.log("Token account does not exist");
break;
case SplTokenErrorCode.ACCOUNT_NOT_INITIALIZED:
console.log("Account exists but is not initialized");
break;
case SplTokenErrorCode.INSUFFICIENT_BALANCE:
console.log(`Insufficient balance: ${error.details?.available}`);
break;
case SplTokenErrorCode.ACCOUNT_FROZEN:
console.log("Token account is frozen");
break;
default:
console.log(`Token error: ${error.message}`);
}
}
}Constants
import {
// Program IDs
TOKEN_2022_PROGRAM_ID, // "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"
ASSOCIATED_TOKEN_PROGRAM_ID, // "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
TOKEN_PROGRAM_ID, // "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" (legacy)
// Account sizes
MINT_SIZE, // 82 bytes
TOKEN_ACCOUNT_SIZE, // 165 bytes
// Enums
TokenAccountState, // Uninitialized, Initialized, Frozen
TokenInstruction, // Transfer, TransferChecked, etc.
ExtensionType, // TokenMetadata, TransferFeeConfig, etc.
AccountType, // Uninitialized, Mint, Account
} from "@rialo/spl-token";Types
import type {
MintInfo,
TokenAccountInfo,
TokenMetadata,
TransferParams,
AtaDerivationResult,
TokenAccountFilter,
RawExtension,
MetadataPointerExtension,
Instruction,
AccountMeta,
} from "@rialo/spl-token";
// TransferParams for high-level transfer operations
const params: TransferParams = {
source: senderWallet, // Source wallet public key
destination: recipientWallet, // Destination wallet public key
mint: mintPubkey, // Token mint
amount: 1_000_000n, // Amount in smallest units
decimals: 6, // Token decimals
createDestinationAta: true, // Auto-create destination ATA (default: true)
};Token-2022 Extensions
The library supports parsing Token-2022 extension data:
import {
parseTokenMetadata,
parseMetadataPointerExtension,
getMintExtensions,
ExtensionType,
} from "@rialo/spl-token";
// Get all extensions from a mint
const extensions = getMintExtensions({ data: mintAccountData });
for (const ext of extensions) {
console.log(`Extension type: ${ext.type}`);
}
// Parse specific extensions
const metadata = parseTokenMetadata({ mint, data: mintAccountData });
const pointer = parseMetadataPointerExtension({ data: mintAccountData });Examples
Complete Transfer Workflow
import { createRialoClient, Keypair, PublicKey, RIALO_DEVNET_CHAIN } from "@rialo/ts-cdk";
import { createSplTokenClient } from "@rialo/spl-token";
async function transferTokens() {
// Setup clients
const rialoClient = createRialoClient({ chain: RIALO_DEVNET_CHAIN });
const tokenClient = createSplTokenClient({ client: rialoClient });
// Load wallet
const sender = Keypair.fromSecretKey(/* your secret key */);
const recipient = PublicKey.fromString("RecipientAddress...");
const mint = PublicKey.fromString("TokenMintAddress...");
// Get mint info for decimals
const mintInfo = await tokenClient.getMintInfo({ mint });
// Check sender balance
const senderBalance = await tokenClient.getBalance({ wallet: sender.publicKey, mint });
console.log(`Sender balance: ${senderBalance}`);
// Calculate transfer amount (1 token)
const amount = BigInt(10 ** mintInfo.decimals);
if (senderBalance < amount) {
throw new Error("Insufficient balance");
}
// Execute transfer
const validFrom = BigInt(Date.now());
const signature = await tokenClient.transfer({
params: {
source: sender.publicKey,
destination: recipient,
mint,
amount,
decimals: mintInfo.decimals,
},
validFrom,
signer: sender,
});
console.log(`Transfer successful: ${signature}`);
// Verify new balances
const newSenderBalance = await tokenClient.getBalance({ wallet: sender.publicKey, mint });
const recipientBalance = await tokenClient.getBalance({ wallet: recipient, mint });
console.log(`New sender balance: ${newSenderBalance}`);
console.log(`Recipient balance: ${recipientBalance}`);
}Manual Transaction Building
import { TransactionBuilder, PublicKey } from "@rialo/ts-cdk";
import {
findAssociatedTokenAddress,
createAssociatedTokenAccountIdempotentInstruction,
transferCheckedInstruction,
TOKEN_2022_PROGRAM_ID,
} from "@rialo/spl-token";
async function buildTransferTransaction(
sender: PublicKey,
recipient: PublicKey,
mint: PublicKey,
amount: bigint,
decimals: number,
) {
const programId = PublicKey.fromString(TOKEN_2022_PROGRAM_ID);
// Derive ATAs
const senderAta = findAssociatedTokenAddress({ wallet: sender, mint, programId }).address;
const recipientAta = findAssociatedTokenAddress({ wallet: recipient, mint, programId }).address;
// Build transaction with both instructions
const validFrom = BigInt(Date.now());
const tx = TransactionBuilder.create()
.setPayer(sender)
.setValidFrom(validFrom)
.addInstruction(
createAssociatedTokenAccountIdempotentInstruction({
payer: sender,
associatedToken: recipientAta,
owner: recipient,
mint,
programId,
}),
)
.addInstruction(
transferCheckedInstruction({
source: senderAta,
mint,
destination: recipientAta,
authority: sender,
amount,
decimals,
programId,
}),
)
.build();
return tx;
}Development
# Install dependencies
npm install
# Build
npm run build
# Test
npm test
# Type check
npm run typecheck
# Lint
npm run lintAPI Reference
SplTokenClient Methods
| Method | Description |
| --- | --- |
| getMintInfo({ mint }) | Get mint account information |
| getTokenMetadata({ mint }) | Get Token-2022 native metadata |
| getTokenAccountInfo({ tokenAccount }) | Get token account information |
| getBalance({ wallet, mint }) | Get token balance for a wallet |
| getAssociatedTokenAddress({ wallet, mint }) | Derive ATA address |
| ataExists({ wallet, mint }) | Check if ATA exists |
| createTransferInstructions(params) | Build transfer instructions |
| createTransferTransaction({ params, validFrom }) | Build complete transaction |
| transfer({ params, validFrom, signer }) | Execute transfer |
Parsing Functions
| Function | Description |
| --- | --- |
| parseMintAccount({ address, data }) | Parse mint account data |
| parseTokenAccount({ address, data }) | Parse token account data |
| parseTokenMetadata({ mint, data }) | Parse Token-2022 metadata extension |
| parseMetadataPointerExtension({ data }) | Parse metadata pointer extension |
| getMintExtensions({ data }) | Get all extensions from mint |
Instruction Builders
| Function | Description |
| --- | --- |
| initializeMintInstruction({ mint, decimals, mintAuthority, freezeAuthority?, programId? }) | Build InitializeMint2 instruction |
| mintToInstruction({ mint, destination, authority, amount, programId? }) | Build MintTo instruction |
| transferCheckedInstruction({ source, mint, destination, authority, amount, decimals, programId? }) | Build TransferChecked instruction |
| transferInstruction({ source, destination, authority, amount, programId? }) | Build Transfer instruction (legacy) |
| createAssociatedTokenAccountInstruction({ payer, associatedToken, owner, mint, programId? }) | Build CreateATA instruction |
| createAssociatedTokenAccountIdempotentInstruction({ payer, associatedToken, owner, mint, programId? }) | Build idempotent CreateATA |
PDA Functions
| Function | Description |
| --- | --- |
| findAssociatedTokenAddress({ wallet, mint, programId? }) | Derive ATA with bump |
| getAssociatedTokenAddressSync({ wallet, mint, programId? }) | Derive ATA address only |
License
Apache License 2.0
