A Solana program implementation of sRFC 37 that provides an Allow/Block List (ABL) system for token access control. This program implements the can_thaw_permissionless and can_freeze_permissionless instructions according to the sRFC037 specification.
This ABL program is written in Pinocchio and generates clients using Codama. It provides a flexible token access control system that allows token issuers to manage which wallets can have their token accounts thawed or frozen through different operational modes.
- Program: Core Solana program written in Pinocchio
- IDL: Committed Codama IDL (
idl/), kept in sync with the program source by CI staleness checks - SDK: Generated TypeScript and Rust clients using Codama
- CLI: Command-line interface for program interaction
The program supports two distinct operational modes:
- Purpose: Blocks wallets in the list from having token accounts thawed
- Behavior: Only wallets NOT in the list can have their token accounts thawed
- Use Case: Blacklist approach for preventing specific wallets from accessing tokens
- Purpose: Only wallets in the list can have their token accounts thawed
- Behavior: Wallets must be explicitly added to the list to access tokens
- Use Case: Whitelist approach for restricted token access
- Create List: Initialize a new allow/block list with specified mode
- Delete List: Remove an existing list (only when empty)
- Add Wallet: Add a wallet address to a specific list
- Remove Wallet: Remove a wallet address from a specific list
- Setup Extra Metas: Configure which lists are used for a given token mint (separately for thaw and freeze)
- Multiple List Support: Token issuers can subscribe to multiple allow or block lists
- Conjunctive Thaw Logic: Wallets must be allowed by ALL configured lists to be thawed
- Disjunctive Freeze Logic: A wallet's token account can be frozen as soon as ANY configured list approves the freeze
| Instruction | Discriminator | Description |
|---|---|---|
can_thaw_permissionless |
08afa981894a3df1 (8 bytes) |
Gate instruction called by Token ACL during permissionless thaw |
can_freeze_permissionless |
d68d6d4bf8012d1d (8 bytes) |
Gate instruction called by Token ACL during permissionless freeze |
create_list |
0x1 |
Create a new list configuration |
add_wallet |
0x2 |
Add wallet to a list |
remove_wallet |
0x3 |
Remove wallet from a list |
setup_extra_metas |
0x4 |
Configure thaw lists for a token mint |
delete_list |
0x5 |
Delete an empty list |
setup_freeze_extra_metas |
0x6 |
Configure freeze lists for a token mint |
The two gate instructions use the complete 8-byte discriminators defined by the token-acl interface.
This program serves as a gate program for the Token ACL system. The Token ACL program calls can_thaw_permissionless / can_freeze_permissionless to determine if a wallet's token account should be allowed to be thawed or frozen.
- Create one or more lists with desired modes
- Add/remove wallets as needed
- Use
setup_extra_metas(andsetup_freeze_extra_metas) to configure which lists apply to a token mint - Create a Token ACL mint config account and define this program as the gate program
- Enable the permissionless thaw and/or freeze operations
- The Token ACL program will call this gate program during thaw/freeze operations
An allow list on the thaw policy only restricts token accounts that are
created frozen. Token-2022 copies the mint's DefaultAccountState into
every new token account, and an account created Initialized is already
usable — Token ACL's thaw path returns before calling the gate, and ordinary
transfers never invoke Token ACL or this program.
- If the mint's
DefaultAccountStateis notFrozen, an installed allow list does not restrict newly created accounts. Anyone (including an allowlisted holder) can create and fund an unlisted wallet's account, which can then transfer onward without any gate decision. The setup path deliberately allows this state so existing mints can migrate to ACL; the CLI prints a warning when it detects it. - Set
DefaultAccountStatetoFrozenbefore relying on an allow list to restrict new holders.
Updating DefaultAccountState to Frozen is not retroactive: it only
affects token accounts initialized after the update. Accounts that already
exist as Initialized never need a gated thaw and keep transferring normally
after ACL adoption.
Before relying on an allow-list policy for pre-existing balances, issuers
must enumerate the mint's token accounts and freeze (or otherwise migrate)
every Initialized account whose owner is not allowed. Until that sweep is
complete, the allow list only protects accounts created after
DefaultAccountState::Frozen was set.
- Rust 1.89 (pinned in rust-toolchain.toml)
- Solana CLI (Agave) 3.x — CI pins 3.1.11, and the committed test fixture must be built with that same version to pass the CI staleness check; the dependency tree requires edition2024 support, so
cargo build-sbfneeds platform-tools v1.52+ (rustc 1.89) - Node.js 20.18.0+
- pnpm 10+
- codama-rs CLI (
cargo install codama-cli --version 0.8.0 --locked) — used bypnpm run generate-idl; the version must match thecodamacrate version in Cargo.toml
# Clone the repository
git clone https://github.com/solana-foundation/token-acl-gate.git
cd token-acl-gate
# Build the program
cargo build-sbf --manifest-path=program/Cargo.toml
# Build CLI
cargo build --manifest-path=cli/Cargo.toml
# Install CLI
cargo install --path cli
# Generate fixed IDL
# This extracts the initial codama IDL from the program source using the
# codama-rs CLI (see Prerequisites), adds additional metadata via visitors
# and places the result in idl/. Fails with a non-zero exit code if any
# step cannot produce a valid IDL.
pnpm run generate-idl
# Generate SDKs
pnpm run generate-sdks
# Copy the built program into the test fixtures
# (the Rust SDK tests run litesvm against this prebuilt binary, so re-run
# this after every program change or the tests exercise a stale program)
pnpm run copy:test:fixtures
# Run tests
cargo test --manifest-path=sdk/rust/Cargo.tomlGATEzzqxhJnsWF6vHRsgtixxSB8PaQdcqGEVTEHWiULz
The CLI provides commands to manage allow/block lists and configure them for token mints.
# Install the CLI from crates.io
cargo install token-acl-gate-cli-C, --config <PATH>- Configuration file to use-k, --payer <KEYPAIR>- Filepath or URL to a keypair [default: client keypair]-v, --verbose- Show additional information-u, --url <URL>- JSON RPC URL for the cluster [default: value from configuration file]
Create a new list:
# Create an allow list
cargo run --bin token-acl-gate-cli -- create-list --mode allow
# Create a block list
cargo run --bin token-acl-gate-cli -- create-list --mode blockDelete a list:
cargo run --bin token-acl-gate-cli -- delete-list <LIST_ADDRESS>Add a wallet to a list:
cargo run --bin token-acl-gate-cli -- add-wallet <LIST_ADDRESS> <WALLET_ADDRESS>Remove a wallet from a list:
cargo run --bin token-acl-gate-cli -- remove-wallet <LIST_ADDRESS> <WALLET_ADDRESS>Apply lists to a mint (permissionless thaw):
# Apply a single list to a mint
cargo run --bin token-acl-gate-cli -- apply-lists-to-mint <MINT_ADDRESS> <LIST_ADDRESS>
# Apply multiple lists to a mint
cargo run --bin token-acl-gate-cli -- apply-lists-to-mint <MINT_ADDRESS> <LIST_ADDRESS_1> <LIST_ADDRESS_2> <LIST_ADDRESS_3>Apply lists to a mint (permissionless freeze):
cargo run --bin token-acl-gate-cli -- apply-lists-to-mint-freeze <MINT_ADDRESS> <LIST_ADDRESS_1> <LIST_ADDRESS_2>Example workflow:
# 1. Create an allow list
cargo run --bin token-acl-gate-cli -- create-list --mode allow
# Output: list_config: <LIST_ADDRESS>, seed: <SEED>
# 2. Add wallets to the list
cargo run --bin token-acl-gate-cli -- add-wallet <LIST_ADDRESS> <WALLET_ADDRESS_1>
cargo run --bin token-acl-gate-cli -- add-wallet <LIST_ADDRESS> <WALLET_ADDRESS_2>
# 3. Configure the list for a token mint
cargo run --bin token-acl-gate-cli -- apply-lists-to-mint <MINT_ADDRESS> <LIST_ADDRESS>MIT License - see LICENSE file for details