UNPKG

solana-token-extension-boost

Version:

SDK for Solana Token Extensions with wallet adapter support

191 lines (190 loc) 8.22 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TokenAccountBuilder = void 0; const web3_js_1 = require("@solana/web3.js"); const spl_token_1 = require("@solana/spl-token"); /** * TokenAccountBuilder - Helper class for creating token accounts with extensions * * ImmutableOwner and other extensions for token accounts */ class TokenAccountBuilder { /** * Initialize builder with connection * * @param connection - Connection to Solana cluster */ constructor(connection) { this.extensions = []; this.connection = connection; } /** * Set basic information for token account * * @param mint - Mint address of the token * @param owner - Owner of the token account */ setTokenAccountInfo(mint, owner) { this.mint = mint; this.owner = owner; return this; } /** * Add ImmutableOwner extension * ImmutableOwner prevents changing the owner of the token account */ addImmutableOwner() { this.extensions.push(spl_token_1.ExtensionType.ImmutableOwner); return this; } /** * Add DefaultAccountState extension * * @param state - Default state of the account (frozen or unlocked) */ addDefaultAccountState(state) { this.extensions.push(spl_token_1.ExtensionType.DefaultAccountState); this.defaultAccountState = state; return this; } /** * Create instructions for standard (non-associated) token account * * @param payer - Public key of the transaction fee payer * @returns Instructions, signers and token account address */ async buildStandardAccountInstructions(payer) { if (!this.mint || !this.owner) { throw new Error("Mint and owner are required. Call setTokenAccountInfo first."); } try { // 1. Create new keypair for token account const tokenAccountKeypair = web3_js_1.Keypair.generate(); const tokenAccount = tokenAccountKeypair.publicKey; // 2. Calculate account size and rent const accountLen = (0, spl_token_1.getAccountLen)(this.extensions); const lamports = await this.connection.getMinimumBalanceForRentExemption(accountLen); console.log(`Token account size: ${accountLen} bytes`); // 3. Create instructions array const instructions = []; // Correct initialization order: // 1. Create account // 2. Initialize extensions // 3. Initialize token account // Create account instruction instructions.push(web3_js_1.SystemProgram.createAccount({ fromPubkey: payer, newAccountPubkey: tokenAccount, space: accountLen, lamports, programId: spl_token_1.TOKEN_2022_PROGRAM_ID, })); // Add extensions if (this.extensions.includes(spl_token_1.ExtensionType.ImmutableOwner)) { instructions.push((0, spl_token_1.createInitializeImmutableOwnerInstruction)(tokenAccount, spl_token_1.TOKEN_2022_PROGRAM_ID)); } if (this.extensions.includes(spl_token_1.ExtensionType.DefaultAccountState) && this.defaultAccountState) { instructions.push((0, spl_token_1.createInitializeDefaultAccountStateInstruction)(tokenAccount, this.defaultAccountState, spl_token_1.TOKEN_2022_PROGRAM_ID)); } // Finally, initialize token account instructions.push((0, spl_token_1.createInitializeAccountInstruction)(tokenAccount, this.mint, this.owner, spl_token_1.TOKEN_2022_PROGRAM_ID)); return { instructions, signers: [tokenAccountKeypair], tokenAccount }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to create token account instructions: ${errorMessage}`); } } /** * Create standard (non-associated) token account * * @param payer - Transaction fee payer * @returns Information about the created token account */ async buildStandardAccount(payer) { if (!this.mint || !this.owner) { throw new Error("Mint and owner are required. Call setTokenAccountInfo first."); } try { const { instructions, signers, tokenAccount } = await this.buildStandardAccountInstructions(payer.publicKey); const tokenAccountKeypair = signers[0]; // Create and send transaction const transaction = new web3_js_1.Transaction().add(...instructions); const transactionSignature = await (0, web3_js_1.sendAndConfirmTransaction)(this.connection, transaction, [payer, tokenAccountKeypair], { commitment: 'confirmed' }); console.log(`Token account created: ${tokenAccount.toBase58()}`); console.log(`Transaction signature: ${transactionSignature}`); return { tokenAccount, tokenAccountKeypair, transactionSignature }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to create token account: ${errorMessage}`); } } /** * Create instructions for Associated Token Account with extensions * * Note: Not all extensions work with ATA * * @param payer - Public key of the transaction fee payer * @returns Instructions and token account address */ buildAssociatedAccountInstructions(payer) { if (!this.mint || !this.owner) { throw new Error("Mint and owner are required. Call setTokenAccountInfo first."); } try { // 1. Get ATA address const tokenAccount = (0, spl_token_1.getAssociatedTokenAddressSync)(this.mint, this.owner, true, // allowOwnerOffCurve spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID); // 2. Create instruction // Can only add normal ATA, cannot add extensions directly // Some extensions like ImmutableOwner are automatically applied to ATA const instructions = [ (0, spl_token_1.createAssociatedTokenAccountInstruction)(payer, tokenAccount, this.owner, this.mint, spl_token_1.TOKEN_2022_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID) ]; return { instructions, tokenAccount }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to create Associated Token Account instructions: ${errorMessage}`); } } /** * Create Associated Token Account with extensions * * Note: Not all extensions work with ATA * * @param payer - Transaction fee payer * @returns Information about the created token account */ async buildAssociatedAccount(payer) { try { const { instructions, tokenAccount } = this.buildAssociatedAccountInstructions(payer.publicKey); // Create and send transaction const transaction = new web3_js_1.Transaction().add(...instructions); const transactionSignature = await (0, web3_js_1.sendAndConfirmTransaction)(this.connection, transaction, [payer], { commitment: 'confirmed' }); console.log(`Associated Token Account created: ${tokenAccount.toBase58()}`); console.log(`Transaction signature: ${transactionSignature}`); return { tokenAccount, transactionSignature }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); throw new Error(`Failed to create Associated Token Account: ${errorMessage}`); } } } exports.TokenAccountBuilder = TokenAccountBuilder;