npm.io
0.18.1 • Published 6d ago

@rialo/spl-token

Licence
Apache-2.0
Version
0.18.1
Deps
1
Size
893 kB
Vulns
0
Weekly
0

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-cdk

Note: @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 lint

API 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

Keywords