Why Can a Solana Token Be Read but Not Transferred?
You register a new Solana token. Balance queries work, and the response says A holds 100 tokens. But when you try to send 10 from A to B, there is no transfer API. If the balance can be read, should the token not be transferable too?
Reading a token and building a transaction that follows its rules require different information. Token-2022 makes this distinction particularly clear. Even tokens using the same program can have different transfer conditions because of their extensions.
What to unify and what to keep distinct in a multichain API examined the conditions that must remain specific to each chain. Here, we narrow the scope to Solana and follow how BXB decides which commands to expose for each token. The discussion covers queries and standard write commands in the token proxy. It does not describe the support boundaries of every resource management API.
1. A balance of 100 does not mean a transfer request can proceed
Assume token T has zero decimal places, and A's and B's token accounts hold 100 and 0 tokens respectively. Both accounts already exist, and the requester is authorized to query this network through BXB. The goal is to send 10 of A's tokens to B.
Now assume T includes an extension type whose name the BXB build examined here does not yet recognize. The token is valid on the chain, but the integration software does not yet know that extension. This is a fictional example to explain the code's decisions, not an incident observed with a production token. Assume the basic account data and display information required for registration are available and valid.
BXB can read T's basic information, register it, and query the basic balance of its token account. Omitting address fields, the quantity-related portion of A's balance response looks like this:
{
"exists": true,
"amount": "100",
"decimals": 0
}
amount expresses the ledger's integer quantity as a decimal string.
In this example, applying only the decimal-place conversion gives a basic display amount of 100 tokens.
The response tells us what is recorded in the basic balance field.
It does not establish that all 100 tokens can be transferred without conditions.
BXB exposes no standard write commands for T because it has an unknown extension. The request to transfer 10 therefore never reaches signing or submission to the chain. If no other transactions occur, the result remains A: 100 / B: 0, and this request incurs no on-chain transaction fee. That differs from a transfer that was submitted and then failed.
2. Does Token-2022 require a new program for every token?
It helps to distinguish three roles in Solana's token structure. The token program is the code that executes instructions such as minting, transferring, and burning. A mint account stores a token type's total supply, decimal places, mint authority, and other information. A token account records who holds how much of a particular mint's token. In our example, T is identified by its mint, while A's and B's holdings are read from their respective token accounts.
The original Token Program and Token-2022 use different program addresses. Token-2022 allows extensions to be added to common token functionality. It does not require every token issuer to deploy a new transfer program to obtain those extensions. The official Token-2022 documentation explains that the first 82 bytes of a basic mint and the first 165 bytes of a token account are compatible with the original format.
Extension data after the basic information can express rules such as transfer fees, non-transferability, or calls to another program. Extensions attached to a mint are also distinct from those attached to individual token accounts. Code that can read the common fields has not necessarily understood all the rules added after them.
Finding an account also requires the correct token program. BXB's balance query locates the associated token account, or ATA, for the combination of owner, mint, and token program, then reads its basic quantity. Deriving an ATA using the original Token Program for a Token-2022 token leads to a different address. Even with the same user and token, the program that manages the account must also match.
This query reads the basic balance of that ATA. It should not be treated as a report that sums every arbitrary token account the user owns or incorporates every separate balance and display rule stored in extensions.
3. Basic information can remain readable when an extension is unknown
Extension data records a type and length alongside its contents. This format is called TLV, or Type-Length-Value: the type, the number of content bytes, and the contents themselves appear in that order. An entry in the mint extension area that BXB reads has this structure:
Type: 2 bytes
Length: 2 bytes
Value: the number of bytes specified by Length
Move to the next extension entry
During registration, BXB collects the type numbers of extension entries. Reading the type and length lets it advance to the next entry, so obtaining the extension list does not require interpreting every extension's contents. BXB can therefore read T's basic information while preserving the fact that an unrecognized extension is present.
The extension list stores numbers rather than names. This keeps a number without a name in the current library from being discarded as though it had never existed. Reading the number does not mean understanding the extension's contents. A later step determines whether that build recognizes the number and which commands it restricts.
A small distinction matters here: Checking for extensions and finding none is different from never having recorded an extension list. The BXB implementation examined here stores the former as an empty list and treats the latter as a missing snapshot, withholding standard writes. Missing information is not taken to mean an ordinary token with no extensions.
Successful registration also depends on conditions such as the program that manages the mint, initialization status, basic data, and required display information. Preserving unknown extensions does not mean accepting invalid accounts or every possible data format unconditionally.
4. Non-transferable and unsupported call for different next steps
Now consider extensions whose names are recognized. The following are examples of static decisions for separate token configurations. We are not adding these features to T one after another or combining every extension in one mint. Some extension combinations are incompatible. The Solana extension documentation describes where each extension applies and which combinations are restricted.
First, NonTransferable prohibits token transfers between accounts.
BXB excludes four transfer commands: the ordinary transfer, transferChecked, and their two delegated-transfer variants.
Adding more implementation code to BXB would not make tokens governed by this same rule freely transferable.
At the same time, prohibiting transfers does not prohibit every change, including minting and burning.
The official Non-Transferable documentation
also distinguishes authorized minting and burning from transfers.
Second, TransferHook connects processing by another program to the transfer flow. Building the transaction also requires finding and including the additional accounts that program needs. The Transfer Hook integration guide describes this process. The BXB implementation examined here does not yet provide that additional-account resolution path, so it excludes the four transfer commands. Unlike a token rule that prohibits transfers, the reason here is missing integration support.
Third, TransferFeeConfig excludes the plain transfer and the delegated transferFrom,
but leaves the transferChecked family, which also checks the mint and decimal places.
Transfers are not all unavailable; the caller must choose a command that carries the required information.
Choosing a checked command does not remove the token's transfer fee rules.
The official Transfer Fees documentation
explains that configured fees still apply to TransferChecked.
The table summarizes these decisions for each configuration, assuming no other restrictions.
| Configuration | Excluded commands | Reason |
|---|---|---|
| Non-transferable extension | Four transfer commands | Token rule |
| Transfer hook extension | Four transfer commands | Integration not implemented |
| Transfer fee extension | Two unchecked transfer commands | Token rule |
| Unknown extension type | All standard writes | Interpretation unsupported |
The code distinguishes restrictions imposed by token program rules, labeled PROGRAM_RULE,
from cases this implementation does not yet handle, labeled NOT_IMPLEMENTED.
The former calls for changing the intended operation or command selection.
The latter calls for checking support for the necessary interpretation and transaction construction.
Both may appear as "cannot send," but neither is resolved by simply retrying.
5. Apply token-specific support decisions to both the specification and execution
BXB starts with a catalog of 17 standard write commands for the token proxy, covering minting, transfers, burning, delegation, authority changes, and other operations. It collects the commands each extension restricts and removes them from that catalog. When multiple extensions are present, their restrictions are combined. One extension permitting a command does not restore a command another extension prohibits.
T's unknown extension type leaves this write-command list empty. The read APIs do not disappear with it. The paths for mint information, balances, token account status, and a list of holder accounts are generated separately from the write-command list. Transaction status queries are separate too. The holder account list also has a limit on the number of results returned.
These decisions shape the OpenAPI specification. T's specification retains supported read paths and omits unavailable standard write paths. This reduces the chance that a user selects an unsupported transfer in Swagger as though it were an available feature. The BXB API Factory article explained how an ABI connects to HTTP inputs. Here, token configuration determines which commands belong in the specification in the first place.
Omitting a path from the specification does not complete the execution checks. BXB's execution service also uses the extension list stored with the resource to reassess whether the command is available. This check comes before retrieving the key material used for signing and building the transaction. A transfer request sent directly to the service for T must therefore still pass the support check.
This reassessment uses the extension snapshot stored at registration.
It does not mean every request reads all extension contents and current settings anew from the chain.
Likewise, retaining read paths does not make them publicly accessible to everyone.
BXB's query service checks that the requester's userKey is associated with an authorized wallet on the network.
6. An available command does not guarantee this transaction will succeed
Suppose a token exposes a mintTo path.
The presence of a minting command in the specification does not give the current requester mint authority.
That authority may have changed or been removed.
When preparing such commands, BXB includes paths that read the current authority from the chain and compare it with the signing wallet.
Delegated transfers require a similar distinction.
Even when a token can expose transferFrom, the actual owner must have delegated authority to the requester,
and the delegated allowance must cover the requested amount.
BXB reads the token account to check the delegate address and allowance.
Three questions therefore need separate answers.
| Question | What to check |
|---|---|
| Can this command be offered? | Extension types and BXB's support for each command |
| Can this requester execute it? | Business access policy and current on-chain authority |
| Did this transaction succeed? | The actual transaction result after submission |
Current account state and execution conditions checked by the chain still matter. The support list does not guarantee that every other restriction has been discovered in advance or that every transaction passing this check will succeed. Recognizing an extension's name also does not mean providing every detailed feature of that extension.
7. Describe support through reads, writes, and the reasons for restrictions
Return to T. BXB can read the basic balances and show 100 tokens for A and 0 for B. But an extension that this build does not understand causes it to withhold standard write commands. The transfer of 10 is therefore never submitted, and without other transactions the balances remain A: 100 / B: 0.
Repeating the transfer request simply because the balance query succeeded is not the next step. First identify the unrecognized extension, then examine whether the implementation and validation needed to build the intended command are in place. If the token has a rule prohibiting transfers, the intended use of the token needs to be reconsidered rather than more functionality added.
Describing support using only the token program's name hides these distinctions. Stating what can be read, which commands can be built, and why other commands were excluded helps the business system use query results correctly and decide what to do next.