TokenAccount Record
software.sava.core.accounts.token.TokenAccount
Represents an SPL Token account containing token balance and metadata.
Constants
Standard size of a token account (165 bytes)
Field Offsets
int MINT_OFFSET = 0;
int OWNER_OFFSET = 32;
int AMOUNT_OFFSET = 64;
int DELEGATE_OPTION_OFFSET = 72;
int DELEGATE_OFFSET = 76;
int STATE_OFFSET = 108;
int IS_NATIVE_OPTION_OFFSET = 109;
int IS_NATIVE_OFFSET = 113;
int DELEGATED_AMOUNT_OFFSET = 121;
int CLOSE_AUTHORITY_OPTION_OFFSET = 129;
int CLOSE_AUTHORITY_OFFSET = 133;
Record Components
record TokenAccount(
PublicKey address,
PublicKey mint,
PublicKey owner,
long amount,
int delegateOption,
PublicKey delegate,
AccountState state,
int isNativeOption,
long isNative,
long delegatedAmount,
int closeAuthorityOption,
PublicKey closeAuthority
)
Mint address for this token type
Owner of the token account
Token balance (raw amount, not accounting for decimals)
0 = no delegate, 1 = has delegate
Delegate authority (null if delegateOption == 0)
Account state (Uninitialized, Initialized, Frozen)
0 = not native, 1 = native SOL
Rent-exempt reserve for native accounts
Amount delegated to delegate
0 = no close authority, 1 = has close authority
Close authority (null if closeAuthorityOption == 0)
Factory Methods
static TokenAccount read(PublicKey publicKey, byte[] data)
static TokenAccount read(PublicKey publicKey, byte[] data, int offset)
RPC Filters
static Filter createMintFilter(PublicKey mint)
RPC filter for accounts with specified mint
static Filter createOwnerFilter(PublicKey owner)
RPC filter for accounts with specified owner
static Filter createDelegateFilter(PublicKey delegate)
static Filter createCloseAuthorityFilter(PublicKey closeAuthority)
Example Usage
import software.sava.core.accounts.token.TokenAccount;
import software.sava.core.accounts.PublicKey;
// Parse token account from data
var tokenAccount = TokenAccount.read(accountAddress, accountData);
// Access fields
var mint = tokenAccount.mint();
var owner = tokenAccount.owner();
var balance = tokenAccount.amount();
System.out.println("Token: " + mint.toBase58());
System.out.println("Owner: " + owner.toBase58());
System.out.println("Balance: " + balance);
// Check if frozen
if (tokenAccount.state() == AccountState.Frozen) {
System.out.println("Account is frozen");
}
// Check for delegate
if (tokenAccount.delegateOption() == 1) {
var delegate = tokenAccount.delegate();
var delegatedAmount = tokenAccount.delegatedAmount();
System.out.println("Delegated " + delegatedAmount + " to " + delegate.toBase58());
}
// Create RPC filter to find all token accounts for a mint
var filter = TokenAccount.createMintFilter(mintAddress);
Mint Record
software.sava.core.accounts.token.Mint
Represents an SPL Token mint account.
Constants
Record Components
record Mint(
PublicKey address,
PublicKey mintAuthority,
long supply,
int decimals,
boolean initialized,
PublicKey freezeAuthority
)
Authority that can mint new tokens (null if no authority)
Total supply of tokens (raw amount)
Whether mint is initialized
Authority that can freeze accounts (null if no authority)
Factory Method
static Mint read(PublicKey address, byte[] data)
Parsed mint account, or null if data is empty
Example Usage
import software.sava.core.accounts.token.Mint;
// Parse mint account
var mint = Mint.read(mintAddress, mintData);
// Access fields
var decimals = mint.decimals();
var supply = mint.supply();
var mintAuthority = mint.mintAuthority();
System.out.println("Decimals: " + decimals);
System.out.println("Supply: " + supply);
System.out.println("UI Supply: " + (supply / Math.pow(10, decimals)));
// Check authorities
if (mint.mintAuthority() != null) {
System.out.println("Mint authority: " + mint.mintAuthority().toBase58());
} else {
System.out.println("Minting disabled (no authority)");
}
if (mint.freezeAuthority() != null) {
System.out.println("Can freeze accounts");
}
Token2022 Record
software.sava.core.accounts.token.Token2022
Represents a Token-2022 mint with extensions.
Record Components
record Token2022(
Mint mint,
AccountType accountType,
Map<ExtensionType, TokenExtension> extensions
)
Account type (Uninitialized, Mint, Account)
extensions
Map<ExtensionType, TokenExtension>
Map of extension type to extension data
Factory Method
static Token2022 read(PublicKey address, byte[] data)
Parsed Token-2022 mint with extensions
Extension Types
enum ExtensionType {
Uninitialized,
TransferFeeConfig,
TransferFeeAmount,
MintCloseAuthority,
ConfidentialTransferMint,
ConfidentialTransferAccount,
DefaultAccountState,
ImmutableOwner,
MemoTransfer,
NonTransferable,
InterestBearingConfig,
CpiGuard,
PermanentDelegate,
NonTransferableAccount,
TransferHook,
TransferHookAccount,
ConfidentialTransferFeeConfig,
ConfidentialTransferFeeAmount,
MetadataPointer,
TokenMetadata,
GroupPointer,
TokenGroup,
GroupMemberPointer,
TokenGroupMember,
ConfidentialMintBurn,
ScaledUiAmount,
Pausable,
PausableAccount
}
Example Usage
import software.sava.core.accounts.token.Token2022;
import software.sava.core.accounts.token.extensions.*;
// Parse Token-2022 mint
var token2022 = Token2022.read(mintAddress, mintData);
// Access base mint info
var mint = token2022.mint();
System.out.println("Supply: " + mint.supply());
System.out.println("Decimals: " + mint.decimals());
// Check extensions
var extensions = token2022.extensions();
if (extensions.containsKey(ExtensionType.TransferFeeConfig)) {
var feeConfig = (TransferFeeConfig) extensions.get(ExtensionType.TransferFeeConfig);
System.out.println("Has transfer fee");
}
if (extensions.containsKey(ExtensionType.MetadataPointer)) {
var metadata = (MetadataPointer) extensions.get(ExtensionType.MetadataPointer);
System.out.println("Metadata pointer: " + metadata.metadataAddress().toBase58());
}
if (extensions.containsKey(ExtensionType.PermanentDelegate)) {
var permDelegate = (PermanentDelegate) extensions.get(ExtensionType.PermanentDelegate);
System.out.println("Permanent delegate: " + permDelegate.delegate().toBase58());
}
Token2022Account Record
software.sava.core.accounts.token.Token2022Account
Represents a Token-2022 token account with extensions.
Example Usage
import software.sava.core.accounts.token.Token2022Account;
// Parse Token-2022 account
var token2022Account = Token2022Account.read(accountAddress, accountData);
// Access token account info
var tokenAccount = token2022Account.tokenAccount();
var owner = tokenAccount.owner();
var balance = tokenAccount.amount();
// Check for account extensions
var extensions = token2022Account.extensions();
if (extensions.containsKey(ExtensionType.ImmutableOwner)) {
System.out.println("Owner cannot be changed");
}
if (extensions.containsKey(ExtensionType.CpiGuard)) {
System.out.println("CPI guard enabled");
}
AccountState Enum
software.sava.core.accounts.token.AccountState
enum AccountState {
Uninitialized,
Initialized,
Frozen
}
Common Token Operations
Calculate UI Amount
public static double toUiAmount(long rawAmount, int decimals) {
return rawAmount / Math.pow(10, decimals);
}
public static long fromUiAmount(double uiAmount, int decimals) {
return (long) (uiAmount * Math.pow(10, decimals));
}
// Example
var mint = Mint.read(mintAddress, mintData);
var tokenAccount = TokenAccount.read(accountAddress, accountData);
double uiBalance = toUiAmount(tokenAccount.amount(), mint.decimals());
System.out.println("Balance: " + uiBalance);
Find Associated Token Account
import software.sava.core.accounts.PublicKey;
import software.sava.core.accounts.SolanaAccounts;
import java.util.List;
public static PublicKey findAssociatedTokenAddress(
PublicKey wallet,
PublicKey mint
) {
var accounts = SolanaAccounts.MAIN_NET;
var seeds = List.of(
wallet.toByteArray(),
accounts.tokenProgram().toByteArray(),
mint.toByteArray()
);
var pda = PublicKey.findProgramAddress(
seeds,
accounts.associatedTokenAccountProgram()
);
return pda.publicKey();
}
// Usage
var ataAddress = findAssociatedTokenAddress(walletAddress, mintAddress);
System.out.println("ATA: " + ataAddress.toBase58());