Security tokens can’t trade on Solana DEXs. Not because of technical limitations, but because the compliance layer and the transfer layer are coupled in a way that breaks composability.
The problem traces back to how Token2022’s transfer hooks work. A security token needs KYC verification before transfers. Transfer hooks let you enforce that by attaching custom validation logic that runs during every transfer. This sounds right—until you try to integrate with a DEX.
The DEX needs to construct a swap transaction. The swap calls transfer_checked on both tokens. But if either token has a transfer hook, the DEX must include additional accounts in remaining_accounts—accounts that the hook program needs to validate the transfer. An oracle address. A registry PDA. A KYC verification account.
These requirements are token-specific. Token A’s hook needs accounts X and Y. Token B’s hook needs accounts Z and W. For a DEX to support both, it needs custom integration for each token. That doesn’t scale. Most protocols looked at this and decided not to support transfer hook tokens at all.
Token ACL solves this by moving compliance enforcement from the transfer layer to the access layer. Tokens start frozen. Anyone can call thaw_permissionless or freeze_permissionless, but these operations only succeed if the caller satisfies the issuer-defined rules. Once thawed, the token behaves like any other SPL token—no hooks, no extra accounts, no special handling.
The compliance check happens once. The transfer happens normally. DeFi infrastructure works unchanged.
For institutions, this unlocks something else: the ability to manage compliance without complex signing ceremonies for every operation. A sanctioned wallet appears? Add it to the blocklist through your Fireblocks multi-sig approval process once, then run a script with a hot wallet to freeze all related token accounts. No need to route every freeze operation through multiple signers.
Why This Matters: Tokens as Contracts vs. Tokens as Accounts
On Ethereum and other EVM chains, tokens are implemented as stateful smart contracts. Each token deployment contains both the balance mappings and the transfer logic within a single contract.
This design makes it trivial to enforce custom behavior: want to block certain addresses? Add a check. Need compliance verification? Insert a hook. The proliferation of security token standards (ERC-1400, ERC-1404, ERC-3643) is a direct consequence of this flexibility. Every project can fork and modify token logic to suit their needs.
Solana took a different approach. Programs are stateless: the SPL Token program contains only logic, while token balances live in separate accounts.
This separation has significant benefits. Any token created through the SPL Token program inherits identical, battle-tested behavior so wallets, DEXs, and lending protocols can integrate once and support every token.
But this design creates a constraint. When sophisticated workflows demand custom transfer logic—security tokens requiring KYC verification, real-world assets needing regulatory compliance, institutional products with accredited investor restrictions—there is no native mechanism to enforce them.
The shared program model that enables composability also prevents customization.
Transfer Hooks: The First Attempt
Token2022’s transfer hook extension was designed to address this gap. When a transfer occurs on a token with a transfer hook configured, the token program invokes a developer-specified program, allowing arbitrary validation or side effects.
In theory, this unlocked the programmability that Solana’s token model previously lacked while preserving the shared program architecture.
In practice, transfer hooks introduced friction that undermined their value proposition.
The core issue is account dependencies. Transfer hooks can require additional accounts to function—oracles, registries, configuration PDAs, or any other state the hook logic needs to read.
These accounts must be passed as remaining_accounts to the transfer_checked instruction and these requirements are token-specific: Token A might need accounts X and Y, while Token B needs accounts Z and W.
For a protocol to support transfer hook tokens, it must know which additional accounts each token requires before constructing the transaction.
This doesn’t scale and requires custom instruction for every token to allow CPIs. Rather than building and maintaining token-specific integrations, most protocols simply don’t support transfer hook tokens. The feature that was supposed to enable programmable transfers instead created tokens that can’t participate in the broader DeFi ecosystem.
Token ACL: A Standard for Permissionless Freeze and Thaw
Token ACL approaches the problem differently. Instead of enforcing compliance during transfers, it defines a standard interface for controlling when token accounts can be frozen or thawed and by extension, when they can participate in transfers.
The architecture has three components:
Token ACL Program: Holds delegated freeze authority from the mint. Receives freeze/thaw requests and coordinates with the gate program to determine if they should succeed.
Gate Program: Issuer-defined program implementing a standardized interface. When Token ACL receives a request, it CPIs to the gate program to ask “should this operation be allowed?” The gate program implements the actual compliance logic.
MintConfig Account: PDA storing the gate program address and configuration flags for each mint.
The standard doesn’t prescribe what rules a gate program should enforce. It defines how Token ACL and gate programs communicate.
This separation means:
- Issuers choose or build gate programs that match their compliance requirements
- Gate programs can be reused across multiple token issuers
- The interface is consistent regardless of the underlying rules
A gate program for accredited investor verification looks the same to Token ACL as a gate program for jurisdiction-based restrictions. The standard handles the plumbing; the gate program handles the policy.
How It Works
Permissionless Operations
Token ACL defines four core operations:
thaw_permissionless: Anyone can request to thaw an accountfreeze_permissionless: Anyone can request to freeze an accountthaw_permissionless_idempotent: Same as above, returns early if already thawedfreeze_permissionless_idempotent: Same as above, returns early if already frozen
“Permissionless” means anyone can call these instructions. Whether they succeed depends entirely on the gate program’s response.
The Validation Flow
When someone calls thaw_permissionless:
- Token ACL creates a temporary flag account (security measure explained below)
- Token ACL CPIs to the gate program’s
can-thaw-permissionlessinstruction - Gate program evaluates its rules against the caller and account
- If validation passes, Token ACL thaws the account
- Flag account is destroyed
The gate program receives:
[caller, token_account, mint, account_owner, flag_account, ...extra_metas]
From these inputs, the gate program can implement whatever logic the issuer requires. Check an on-chain allowlist. Verify a merkle proof of KYC completion. Query an oracle for accredited investor status. Call an external API.
The standard doesn’t constrain the implementation.
Gate Program Logic
Gate programs implement two methods with standardized discriminators:
// For thaw validation // Hash of "efficient-allow-block-list-standard:can-thaw-permissionless" const CAN_THAW_DISCRIMINATOR: [u8; 8] = [8, 175, 169, 129, 137, 74, 61, 241]; // For freeze validation // Hash of "efficient-allow-block-list-standard:can-freeze-permissionless" const CAN_FREEZE_DISCRIMINATOR: [u8; 8] = [214, 141, 109, 75, 248, 1, 45, 29];
The logic for thaw and freeze typically inverts. For an allowlist-based gate:
Thaw logic: Is the caller on the allowlist? If yes, allow thaw.
Freeze logic: Is the caller on the blocklist? If yes, allow freeze.
This means compliant users can thaw their accounts to trade, while blocked addresses can be frozen by anyone who notices they shouldn’t have access.
The Flag Account Pattern
The flag account is a security measure. When Token ACL calls the gate program, it first creates a PDA containing a single byte [1]. The gate program must verify:
- The flag account exists
- It’s owned by Token ACL
- Its data is
[1]
This proves the call originated from Token ACL, not from an attacker trying to spoof the validation flow. Without this check, a malicious program could call the gate program directly and potentially manipulate its state.
The flag account exists only for the duration of the CPI and is destroyed after the operation completes.
In Practice
Example 1: Trading on a DEX
For trading security tokens on a DEX, the pattern sandwiches the swap between thaw and freeze operations:
Transaction: 1. thaw_permissionless_idempotent(user_token_account_A) 2. thaw_permissionless_idempotent(user_token_account_B) 3. swap(pool, amount_in, min_amount_out) 4. freeze_permissionless_idempotent(user_token_account_A) 5. freeze_permissionless_idempotent(user_token_account_B)
The thaw instructions verify the user’s compliance status by calling the gate program. If the user satisfies the KYC requirements (or whatever rules the issuer has defined), the thaw succeeds.
The swap instruction is completely standard. The AMM doesn’t know or care that these are security tokens. It sees two unfrozen token accounts and executes the swap normally. No extra accounts. No custom integration. The DEX doesn’t even need to know Token ACL exists.
The idempotent variants handle cases where accounts are already thawed, avoiding unnecessary failures.
The freeze at the end is optional—some issuers may want accounts to remain thawed after the first compliance check. The standard supports both models.
Example 2: Managing Sanctioned Wallets
For institutions with complex signing ceremonies, Token ACL decouples compliance policy updates from enforcement actions.
Consider a financial institution that discovers a wallet has been sanctioned. The traditional flow requires routing every freeze operation through a multi-signature approval process—Fireblocks with multiple signers, daily approval windows, dozens of related token accounts that need freezing.
With Token ACL, the signing ceremony happens once:
# Multi-sig transaction (through Fireblocks) add_to_blocklist(sanctioned_wallet_address)
Once the sanctioned address is on the blocklist, anyone can freeze the related accounts using a hot wallet:
# Script using hot wallet (no multi-sig needed) for account in related_token_accounts: freeze_permissionless(account)
Each freeze_permissionless call asks the gate program “should this be allowed?” The gate program checks the blocklist, sees the wallet owner is sanctioned, and returns true. Token ACL executes the freeze.
The policy update (adding to blocklist) requires organizational approval. The enforcement (freezing accounts) can be automated. This separation makes compliance operations practical at scale.
What This Means for Institutions
For institutions issuing security tokens on Solana, Token ACL resolves the tension between compliance and liquidity.
Compliance logic is customizable: The gate program is issuer-controlled. KYC verification, accredited investor checks, jurisdiction restrictions, transfer limits—implement whatever rules regulators require. Update the rules by updating the gate program, not the token itself.
DeFi infrastructure works unchanged: Thawed tokens are standard SPL tokens. They trade on any AMM, collateralize any lending protocol, integrate with any DeFi application. No protocol needs to add special support.
The standard is composable: Multiple issuers can use the same gate program if they have the same compliance requirements. A third-party compliance provider could operate a gate program that checks against their KYC database, with multiple issuers pointing their mint configs to that provider. Specialization becomes possible: issuers focus on their asset, compliance providers focus on verification.
Operations are permissionless: Users don’t wait for manual issuer intervention. If they satisfy the gate program’s conditions, they can thaw their accounts and trade. If they don’t, the operation fails. The issuer defines the rules; the system enforces them automatically.
Conclusion
Transfer hooks attempted to bring programmable transfer logic to Solana while preserving the shared token program model. The account dependency requirements made these tokens impractical for most DeFi applications—a feature designed for composability ended up breaking it.
Token ACL solves this by moving compliance enforcement from the transfer layer to the access layer. The standard defines how freeze and thaw operations are validated; gate programs define what rules to enforce. Once thawed, tokens behave normally. Existing infrastructure works without modification.
Programmable transfer restrictions on Solana need three things: a standard interface, customizable rules, and full DeFi composability. Token ACL delivers all three.