UNPKG

solana-token-extension-boost

Version:

SDK for Solana Token Extensions with wallet adapter support

172 lines (171 loc) 7.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.NonTransferableToken = void 0; const web3_js_1 = require("@solana/web3.js"); const spl_token_1 = require("@solana/spl-token"); const token_1 = require("../../core/token"); /** * NonTransferableToken - Extension for non-transferable tokens * * This extension prevents tokens from being transferred after they've been minted. * Useful for credentials, certificates, soulbound tokens, and other non-transferable assets. */ class NonTransferableToken extends token_1.Token { constructor(connection, mint) { super(connection, mint); } /** * Create instructions for a new NonTransferableToken * * @param connection - Connection to Solana cluster * @param payer - Public key of the transaction fee payer * @param params - Initialization parameters including: * - decimals: Number of decimal places * - mintAuthority: Authority allowed to mint tokens * - freezeAuthority: Optional authority allowed to freeze accounts * @returns Instructions, signers and mint address for the new token */ static async createInstructions(connection, payer, params) { try { const mintKeypair = web3_js_1.Keypair.generate(); const mint = mintKeypair.publicKey; const mintLen = (0, spl_token_1.getMintLen)([spl_token_1.ExtensionType.NonTransferable]); const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); const instructions = [ web3_js_1.SystemProgram.createAccount({ fromPubkey: payer, newAccountPubkey: mint, space: mintLen, lamports, programId: spl_token_1.TOKEN_2022_PROGRAM_ID, }), (0, spl_token_1.createInitializeNonTransferableMintInstruction)(mint, spl_token_1.TOKEN_2022_PROGRAM_ID), (0, spl_token_1.createInitializeMintInstruction)(mint, params.decimals, params.mintAuthority, params.freezeAuthority ?? null, spl_token_1.TOKEN_2022_PROGRAM_ID) ]; return { instructions, signers: [mintKeypair], mint }; } catch (error) { throw new Error(`Could not create NonTransferableToken instructions: ${error.message}`); } } /** * Create instructions to mint to an account * * @param destination - Token account address to receive tokens * @param authority - Authority allowed to mint tokens * @param amount - Amount of tokens to mint * @returns Object containing instructions */ createMintToInstructions(destination, authority, amount) { const instructions = []; // Add mint token instruction instructions.push((0, spl_token_1.createMintToInstruction)(this.mint, destination, authority, amount, [], spl_token_1.TOKEN_2022_PROGRAM_ID)); return { instructions }; } /** * Create instructions to mint to an account (extended version) * * @param owner - Token account owner * @param amount - Amount of tokens to mint * @param mintAuthority - Authority allowed to mint tokens * @returns Instructions and token account address */ async createMintToInstructionsWithAddress(owner, amount, mintAuthority) { try { // Get token account address const tokenAccount = await (0, spl_token_1.getAssociatedTokenAddress)(this.mint, owner, false, spl_token_1.TOKEN_2022_PROGRAM_ID); const instructions = []; // Check if account already exists try { await (0, spl_token_1.getAccount)(this.connection, tokenAccount, "recent", spl_token_1.TOKEN_2022_PROGRAM_ID); } catch (error) { // Account doesn't exist, add instruction to create it instructions.push((0, spl_token_1.createAssociatedTokenAccountInstruction)(owner, // payer tokenAccount, owner, this.mint, spl_token_1.TOKEN_2022_PROGRAM_ID)); } // Add mint token instruction instructions.push((0, spl_token_1.createMintToInstruction)(this.mint, tokenAccount, mintAuthority, amount, [], spl_token_1.TOKEN_2022_PROGRAM_ID)); return { instructions, address: tokenAccount }; } catch (error) { throw new Error(`Could not create mint instructions: ${error.message}`); } } /** * Check if the token is non-transferable * * @returns Promise resolving to boolean */ async isNonTransferable() { try { const mintInfo = await (0, spl_token_1.getMint)(this.connection, this.mint, "confirmed", spl_token_1.TOKEN_2022_PROGRAM_ID); if (!mintInfo.tlvData || mintInfo.tlvData.length === 0) { console.log("No TLV data for mint"); return false; } console.log(`Mint TLV data for ${this.mint.toBase58()}:`, mintInfo.tlvData); const NON_TRANSFERABLE_TYPE = 9; let offset = 0; while (offset < mintInfo.tlvData.length) { if (offset + 4 > mintInfo.tlvData.length) break; const type = mintInfo.tlvData.readUInt32LE(offset); console.log(`Found extension type: ${type}`); if (type === NON_TRANSFERABLE_TYPE) { return true; } if (offset + 8 > mintInfo.tlvData.length) break; const length = mintInfo.tlvData.readUInt32LE(offset + 4); offset += 8 + length; } return false; } catch (error) { console.error("Error checking non-transferable status:", error); throw new Error(`Failed to check if token is non-transferable: ${error.message}`); } } /** * Get mint information * * @returns Mint information */ async getMintInfo() { try { return await (0, spl_token_1.getMint)(this.connection, this.mint, "confirmed", spl_token_1.TOKEN_2022_PROGRAM_ID); } catch (error) { throw new Error(`Failed to get mint info: ${error.message}`); } } /** * Get non-transferable information * * @returns Object with isNonTransferable property */ async getNonTransferableInfo() { const isNonTransferable = await this.isNonTransferable(); return { isNonTransferable }; } /** * Check if tokens can be transferred from a token account * For non-transferable tokens, this will always return false * * @param tokenAccount - Token account to check * @returns Boolean indicating if tokens can be transferred */ async canTransferTokens(tokenAccount) { const isNonTransferable = await this.isNonTransferable(); return !isNonTransferable; } } exports.NonTransferableToken = NonTransferableToken;