The DefaultAccountState
extension provides the option to have all new Token
Accounts to be frozen by default. With this configuration, Token Accounts must
first be thawed (unfrozen) by the Freeze Authority of the mint before they
become usable. This feature grants token creators the ability to have greater
control over token distribution by limiting who can hold the tokens.
In this guide, we'll walk through an example of using Solana Playground. Here is the final script.
Getting Started
Start by opening this Solana Playground link with the following starter code.
// Clientconsole.log("My address:", pg.wallet.publicKey.toString());const balance = await pg.connection.getBalance(pg.wallet.publicKey);console.log(`My balance: ${balance / web3.LAMPORTS_PER_SOL} SOL`);
If it is your first time using Solana Playground, you'll first need to create a Playground Wallet and fund the wallet with devnet SOL.
If you do not have a Playground wallet, you may see a type error within the
editor on all declarations of pg.wallet.publicKey
. This type error will clear
after you create a Playground wallet.
To get devnet SOL, run the solana airdrop
command in the Playground's
terminal, or visit this devnet faucet.
solana airdrop 5
Once you've created and funded the Playground wallet, click the "Run" button to run the starter code.
Add Dependencies
Let's start by setting up our script. We'll be using the @solana/web3.js
and
@solana/spl-token
libraries.
Replace the starter code with the following:
import {Connection,Keypair,SystemProgram,Transaction,clusterApiUrl,sendAndConfirmTransaction,} from "@solana/web3.js";import {ExtensionType,TOKEN_2022_PROGRAM_ID,AccountState,createInitializeDefaultAccountStateInstruction,createInitializeMintInstruction,getMintLen,createAccount,mintTo,updateDefaultAccountState,thawAccount,} from "@solana/spl-token";// Playground walletconst payer = pg.wallet.keypair;// Connection to devnet clusterconst connection = new Connection(clusterApiUrl("devnet"), "confirmed");// Transaction signature returned from sent transactionlet transactionSignature: string;
Mint Setup
First, let's define the properties of the Mint Account we'll be creating in the following step.
// Generate new keypair for Mint Accountconst mintKeypair = Keypair.generate();// Address for Mint Accountconst mint = mintKeypair.publicKey;// Decimals for Mint Accountconst decimals = 2;// Authority that can mint new tokensconst mintAuthority = pg.wallet.publicKey;// Authority that can freeze and thaw token accountsconst freezeAuthority = pg.wallet.publicKey;
Next, let's determine the size of the new Mint Account and calculate the minimum lamports needed for rent exemption.
// Size of Mint Account with extensionconst mintLen = getMintLen([ExtensionType.DefaultAccountState]);// Minimum lamports required for Mint Accountconst lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
With Token Extensions, the size of the Mint Account will vary based on the extensions enabled.
Build Instructions
Next, let's build the set of instructions to:
- Create a new account
- Initialize the
DefaultAccountState
extension - Initialize the remaining Mint Account data
First, build the instruction to invoke the System Program to create an account and assign ownership to the Token Extensions Program.
// Instruction to invoke System Program to create new accountconst createAccountInstruction = SystemProgram.createAccount({fromPubkey: payer.publicKey, // Account that will transfer lamports to created accountnewAccountPubkey: mint, // Address of the account to createspace: mintLen, // Amount of bytes to allocate to the created accountlamports, // Amount of lamports transferred to created accountprogramId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account});
Next, build the instruction to initialize the DefaultAccountState
extension
for the Mint Account.
// Set default account state as Frozenconst defaultState = AccountState.Frozen;// Instruction to initialize the DefaultAccountState Extensionconst initializeDefaultAccountStateInstruction =createInitializeDefaultAccountStateInstruction(mint, // Mint Account addressdefaultState, // Default AccountStateTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);
Lastly, build the instruction to initialize the rest of the Mint Account data. This is the same as with the original Token Program.
// Instruction to initialize Mint Account dataconst initializeMintInstruction = createInitializeMintInstruction(mint, // Mint Account Addressdecimals, // Decimals of MintmintAuthority, // Designated Mint AuthorityfreezeAuthority, // Designated Freeze AuthorityTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);
Send Transaction
Next, let's add the instructions to a new transaction and send it to the
network. This will create a Mint Account with the DefaultAccountState
extension enabled.
// Add instructions to new transactionconst transaction = new Transaction().add(createAccountInstruction,initializeDefaultAccountStateInstruction,initializeMintInstruction,);// Send transactiontransactionSignature = await sendAndConfirmTransaction(connection,transaction,[payer, mintKeypair], // Signers);console.log("\nCreate Mint Account:",`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`,);
Run the script by clicking the Run
button. You can then inspect the
transaction on the SolanaFM.
Default Frozen Token Account
When a Token Account is created, it will be frozen by default. As a result, any transactions interacting with the Token Account will fail.
// Create Token Account for Playground walletconst tokenAccount = await createAccount(connection,payer, // Payer to create Token Accountmint, // Mint Account addresspayer.publicKey, // Token Account ownerundefined, // Optional keypair, default to Associated Token Accountundefined, // Confirmation optionsTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);
In the default frozen state, users are unable to hold or interact with tokens from the mint until the Freeze Authority thaws (unfreezes) the Token Account.
For instance, minting tokens to the new Token Account will fail.
try {// Attempt to Mint tokensawait mintTo(connection,payer, // Transaction fee payermint, // Mint Account addresstokenAccount, // Destination for mintingmintAuthority, // Mint Authority100, // Amount to mintundefined, // Additional signersundefined, // Confirmation optionsTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);} catch (error) {console.log("\nExpect Error:", error);}
Run the script by clicking the Run
button. You can then inspect the error in
the Playground terminal. You should see a message similar to the following:
[Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x11]logs:[ 'Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [1]','Program log: Instruction: MintTo','Program log: Error: Account is frozen','Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 3274 of 200000 compute units','Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb failed: custom program error: 0x11' ]
Unfreeze (Thaw) Token Account
A Token Account can be unfrozen using the thawAccount
instruction in the same
way as with the original Token program.
// Thaw (unfreeze) the Token AccounttransactionSignature = await thawAccount(connection,payer, // Transaction fee payertokenAccount, // Token Account to unfreezemint, // Mint Account addressfreezeAuthority, // Freeze Authorityundefined, // Additional signersundefined, // Confirmation optionsTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);console.log("\nThaw Token Account:",`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`,);
The instruction to thaw the Token Account can be added to more complex transactions, requiring users to perform certain actions to unfreeze their Token Account.
Update Default State
The token creator can choose to relax the initial restriction by using the
updateDefaultAccountState
instruction by passing in the new account state to
set on created accounts.
// Update default account state for new Token AccountstransactionSignature = await updateDefaultAccountState(connection,payer, // Payer of the transaction feemint, // Mint to modifyAccountState.Initialized, // New account state to set on created accountsfreezeAuthority, // Freeze authorityundefined, // Additional signersundefined, // Confirmation optionsTOKEN_2022_PROGRAM_ID, // Token Extension Program ID);console.log("\nUpdate Default Mint Account State:",`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`,);
Run the script by clicking the Run
button. You can then inspect the
transaction on the SolanaFM.
Conclusion
The DefaultAccountState
extension introduces a valuable tool for token
creators to have enhanced control of their token. This feature allows for
controlled token distribution, enabling mechanisms like mandatory KYC
verification for token holders.