A Solana wallet does not hold an SPL token balance at its wallet address. The balance lives in a separate token account tied to one wallet and one mint.
That distinction explains many confusing token bugs. A wallet can own several accounts for the same asset, a recipient may not have an account yet, and a transfer can reach a valid account for the wrong mint.
This guide gives you a working model of SPL tokens and a Token Transfer Preflight for reviewing transfers before users sign them.
One token uses several account types
Most fungible tokens on Solana use the original Token Program or the Token Extension Program, commonly called Token-2022. Both share the same core model.
mint account
├─ defines the asset
├─ records decimals and total supply
└─ stores optional mint and freeze authorities
wallet address
└─ controls one or more token accounts
├─ references one mint
├─ stores a balance
└─ may have a delegate or close authorityThe mint account identifies the asset. Token accounts hold units of that asset. A wallet address can control token accounts, but it is not itself the balance record.
All of these are Solana accounts with addresses. Their decoded data and owning program determine their role. If program ownership, account authority, and wallet ownership still feel interchangeable, start with the Solana account model guide.
The mint account defines the asset
A mint is the canonical account for one SPL token. Its address is the token's durable identity on a cluster. Two tokens can share a name, symbol, and image while remaining different assets because their mint addresses differ.
The core mint state includes:
- supply: total base units currently issued.
- decimals: how clients display those base units.
- mintAuthority: the optional authority allowed to create more units.
- freezeAuthority: the optional authority allowed to freeze or thaw token accounts.
Metadata such as a display name or image is not the asset's identity. Interfaces should keep the mint address available whenever identity matters, especially for deposits, swaps, and administrative actions.
Supply changes through minting and burning
Minting adds units to a destination token account and increases the mint's supply. The current mint authority must authorize it.
Burning removes units from a token account and reduces supply. A normal transfer does neither. The source balance falls while the destination balance rises by the same number of units.
This difference matters in analytics. A transfer event is movement, not issuance. A token account balance is not the total supply.
Token accounts hold balances, while ATAs provide a default
A token account holds units for exactly one mint. Its state records the mint, the token authority usually described as its owner, the raw amount, and optional delegate and close-authority fields.
One wallet can control several token accounts for the same mint. That flexibility supports custody systems, application vaults, and isolated balances. It also means that (wallet, mint) does not identify a unique token account by itself.
An associated token account, or ATA, solves the common case. Its address is derived deterministically from:
wallet authority + token program + mintWallets and applications can derive the same default address without storing a lookup table. The token program is part of the derivation, so an ATA for the original Token Program is not the same address as an ATA for Token-2022.
The recipient ATA may not exist
A first-time recipient might not have an ATA for the mint. Your application can include an idempotent create-ATA instruction before the transfer. If the account already exists, the idempotent instruction leaves the expected account in place.
Account creation requires lamports for rent-exempt storage. Decide whether the sender, recipient, or product sponsor pays, and show that cost before signing.
Do not assume every valid token account is an ATA. A recipient can provide another token account they control. If your product only accepts canonical ATAs, enforce that policy explicitly instead of treating it as a protocol rule.
Decimals convert integer units into display amounts
SPL token amounts are stored as integers. The mint's decimals field tells clients where to place the decimal point for display.
For a mint with six decimals:
1 token = 1,000,000 base units
2.75 tokens = 2,750,000 base unitsDecimals do not grant fractional on-chain storage. They are a presentation convention applied to an integer amount.
Never calculate token values with JavaScript floating-point numbers. Values such as 0.1 cannot always be represented exactly. Parse the user's decimal string, validate the permitted precision, and convert it to a bigint base-unit value.
function toBaseUnits(value: string, decimals: number): bigint {
const [whole, fraction = ''] = value.split('.');
if (!/^\d+$/.test(whole) || !/^\d*$/.test(fraction)) {
throw new Error('Invalid token amount');
}
if (fraction.length > decimals) {
throw new Error(`Maximum precision is ${decimals} decimals`);
}
return BigInt(whole + fraction.padEnd(decimals, '0'));
}Read decimals from the verified mint account. Do not trust an arbitrary client field or hard-code a value because one familiar stablecoin uses it.
Authorities determine who can change what
The word owner is overloaded in Solana development. The Token Program owns token and mint accounts at the runtime level. Inside a token account's data, an authority controls transfers and other protected actions.
The main token roles are:
- Mint authority: can mint new units and increase supply.
- Freeze authority: can freeze and thaw accounts for that mint.
- Token authority: can transfer or burn units from a token account.
- Delegate: can transfer or burn up to an approved allowance.
- Close authority: can close an empty token account and direct its reclaimed lamports.
Authority design is part of the token's risk model. A retained mint authority means supply can still increase. A retained freeze authority means token accounts can still be frozen. Those powers may be necessary for a managed asset, but users should not have to infer that policy from a token logo.
The SetAuthority instruction can rotate an authority or set it to None. Revoking an authority is permanent for that role. Confirm the target account, authority type, current signer, and intended replacement before building the transaction.
Build a safer checked transfer
A token transfer moves base units between two token accounts for the same mint. The source token authority or an approved delegate signs.
Prefer TransferChecked for application transfers. It includes the mint and expected decimals, allowing the Token Program to verify both before moving tokens. Token-2022 deprecates the unchecked Transfer instruction in favor of checked variants.
A robust transfer flow looks like this:
- Accept the intended mint, recipient wallet, and human-readable amount.
- Fetch the mint from the expected cluster and token program.
- Decode its decimals and authority state.
- Derive the sender and recipient ATAs with the correct token program.
- Fetch existing accounts and validate their program owner, decoded mint, and token authority.
- Add an idempotent create-ATA instruction if the recipient account is absent.
- Convert the amount string into integer base units.
- Add TransferChecked with the mint and verified decimals.
- Simulate the complete transaction, then show the asset, amount, recipient, and fees.
- Submit once, retain the signature, and track confirmation or expiry.
Creating the ATA and transferring in the same Solana transaction makes the state change atomic. If account creation or transfer validation fails, neither state change lands, though the transaction fee can still be charged.
Validate every address by decoded state
An address can be syntactically valid and still be wrong for the operation. Before transferring, verify:
- The mint account is owned by the expected token program.
- Both token accounts decode under that program.
- Source and destination reference the intended mint.
- The source authority or delegate matches the signer policy.
- The source is initialized, not frozen, and has enough base units.
- The destination matches the recipient policy, such as its canonical ATA.
These checks improve the error message and wallet preview. The Token Program remains the final authority when the transaction executes.
Run the Token Transfer Preflight
Use this review before asking for a signature:
- Cluster: Are the RPC, mint, accounts, and explorer links on the same cluster?
- Program: Is this mint using the original Token Program or Token-2022?
- Identity: Does the verified mint address match the asset shown to the user?
- Accounts: Do source and destination decode as token accounts for that mint?
- Recipient: Is the destination owned by the intended wallet or allowed by product policy?
- Amount: Was a decimal string converted to exact integer base units?
- Authority: Is the signer the source authority or an approved delegate with enough allowance?
- State: Is the source initialized, unfrozen, and sufficiently funded?
- Creation: If the recipient ATA is absent, is its creation included and funded?
- Instruction: Does the transaction use TransferChecked with verified decimals?
- Preview: Does the wallet-facing copy show mint, amount, recipient, and all SOL costs?
- Recovery: Will the app retain the signature and handle failure, expiry, and confirmation?
Most token transfer failures map to one item on this list. InvalidAccountData often means the address is the wrong type or program. A mint mismatch means the accounts represent different assets. Insufficient funds can refer to token units, transaction fees, or the lamports needed to create an ATA.
FAQ
Is an SPL token stored in my wallet account?
No. The balance is stored in a token account whose token authority is usually the wallet. The wallet can control several token accounts for the same mint.
Is every token account an associated token account?
No. An ATA is the deterministic default for one wallet, mint, and token program. Other token accounts can be created and used when an application needs them.
Can I send tokens to a wallet with no token account?
Yes, if the transaction creates the recipient's ATA before transferring. The payer must fund that new account's rent-exempt balance.
Do token decimals ever change?
The base mint stores the decimals configured at creation. Applications should still fetch and verify the mint rather than relying on a symbol or hard-coded assumption.
Does revoking mint authority make supply fixed?
It prevents future minting through that revoked role. Setting the authority to None is permanent, so the signer should verify the mint and authority type carefully.
Primary references
Review a production token flow
Run the Token Transfer Preflight against one production transfer. If mint identity, authority policy, or recovery remains unclear, bring the flow to the Builderz project fit form.



