solana-token-extension-boost
Version:
SDK for Solana Token Extensions with wallet adapter support
172 lines (171 loc) • 7.2 kB
JavaScript
;
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;