UNPKG

@gorbchain-xyz/chaindecode

Version:

GorbchainSDK V1.3+ - Complete Solana development toolkit with advanced cryptography, messaging, and collaboration features. Build secure applications with blockchain, DeFi, and end-to-end encryption.

399 lines (398 loc) 17.9 kB
/** * Gorbchain SDK - Token and NFT Minting Functions * * This module provides comprehensive token and NFT minting capabilities * for the Gorbchain network, including Token22 program integration and * Metaplex Core NFT minting. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { PublicKey, Transaction, SystemProgram, Keypair, sendAndConfirmTransaction } from '@solana/web3.js'; import { createInitializeMintInstruction, createMintToInstruction, createAssociatedTokenAccountInstruction, getAssociatedTokenAddressSync, getMintLen, ExtensionType, createInitializeMetadataPointerInstruction, createInitializeInstruction, MINT_SIZE } from '@solana/spl-token'; import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'; import { mplCore, createV1 } from '@metaplex-foundation/mpl-core'; import { generateSigner } from '@metaplex-foundation/umi'; import { walletAdapterIdentity } from '@metaplex-foundation/umi-signer-wallet-adapters'; // Token22 Program ID for Gorbchain const TOKEN22_PROGRAM = new PublicKey('FGyzDo6bhE7gFmSYymmFnJ3SZZu3xWGBA7sNHXR7QQsn'); const ASSOCIATED_TOKEN_PROGRAM = new PublicKey('4YpYoLVTQ8bxcne9GneN85RUXeN7pqGTwgPcY71ZL5gX'); // Custom MPL Core Program for Gorbchain const CUSTOM_MPL_CORE_PROGRAM = 'BvoSmPBF6mBRxBMY9FPguw1zUoUg3xrc5CaWf7y5ACkc'; /** * Calculate the required space for token metadata */ function calculateMetadataSpace(name, symbol, uri) { const baseFields = { updateAuthority: 32, mint: 32, name: 4 + name.length, symbol: 4 + symbol.length, uri: 4 + uri.length, additionalMetadata: 4 // Empty vec }; const totalSize = Object.values(baseFields).reduce((a, b) => a + b, 0); const tlvOverhead = 4; // Type-Length-Value overhead const padding = Math.ceil(totalSize * 0.1); // 10% padding for safety return totalSize + tlvOverhead + padding; } /** * Calculate mint account size with extensions */ function calculateMintAccountSize(extensions) { const baseSize = MINT_SIZE; // 82 bytes const extensionSize = extensions.reduce((acc, ext) => { switch (ext) { case ExtensionType.MetadataPointer: return acc + 32; // Metadata pointer size default: return acc; } }, 0); return baseSize + extensionSize; } /** * Validate token parameters */ function validateTokenParameters(params) { if (!params.name || params.name.length > 32) { throw new Error('Token name must be 1-32 characters'); } if (!params.symbol || params.symbol.length > 10) { throw new Error('Token symbol must be 1-10 characters'); } if (params.supply <= 0 || params.supply > 1e15) { throw new Error('Token supply must be between 1 and 1e15'); } if (params.decimals < 0 || params.decimals > 9) { throw new Error('Decimals must be between 0 and 9'); } if (params.uri && !isValidUrl(params.uri)) { throw new Error('Invalid URI format'); } } /** * Validate NFT parameters */ function validateNFTParameters(params) { if (!params.name || params.name.length > 32) { throw new Error('NFT name must be 1-32 characters'); } if (!params.uri || !isValidUrl(params.uri)) { throw new Error('Valid metadata URI is required'); } if (params.royaltyBasisPoints && (params.royaltyBasisPoints < 0 || params.royaltyBasisPoints > 10000)) { throw new Error('Royalty basis points must be between 0 and 10000'); } if (params.creators) { const totalPercentage = params.creators.reduce((sum, creator) => sum + creator.percentage, 0); if (totalPercentage !== 100) { throw new Error('Creator percentages must sum to 100'); } } } /** * Simple URL validation */ function isValidUrl(url) { try { new URL(url); return true; } catch (_a) { return false; } } /** * Send transaction with retry logic */ function sendTransactionWithRetry(connection_1, transaction_1, signers_1) { return __awaiter(this, arguments, void 0, function* (connection, transaction, signers, options = {}) { var _a; const { commitment = 'confirmed', maxRetries = 3, skipPreflight = false } = options; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { // Get fresh blockhash const { blockhash, lastValidBlockHeight } = yield connection.getLatestBlockhash(); transaction.recentBlockhash = blockhash; transaction.lastValidBlockHeight = lastValidBlockHeight; // Simulate transaction first const simulation = yield connection.simulateTransaction(transaction); if (simulation.value.err) { const logs = ((_a = simulation.value.logs) === null || _a === void 0 ? void 0 : _a.join('\n')) || 'No logs available'; throw new Error(`Transaction simulation failed: ${logs}`); } // Send and confirm transaction const signature = yield sendAndConfirmTransaction(connection, transaction, signers, { commitment, skipPreflight }); return signature; } catch (error) { if (attempt === maxRetries) { throw error; } // Wait before retry with exponential backoff yield new Promise(resolve => setTimeout(resolve, 1000 * attempt)); } } throw new Error('Max retries reached'); }); } /** * Create a new Token22 token with metadata (Two-Transaction Approach) * * This is the recommended approach for reliability with complex metadata */ export function createToken22TwoTx(connection_1, payer_1, params_1) { return __awaiter(this, arguments, void 0, function* (connection, payer, params, options = {}) { validateTokenParameters(params); const mintKeypair = Keypair.generate(); const mint = mintKeypair.publicKey; // Transaction 1: Create mint account and initialize const extensions = [ExtensionType.MetadataPointer]; const mintLen = getMintLen(extensions); const rentExemption = yield connection.getMinimumBalanceForRentExemption(mintLen); const setupTransaction = new Transaction(); setupTransaction.feePayer = payer.publicKey; // Create the mint account setupTransaction.add(SystemProgram.createAccount({ fromPubkey: payer.publicKey, newAccountPubkey: mint, lamports: rentExemption, space: mintLen, programId: TOKEN22_PROGRAM })); // Initialize metadata pointer setupTransaction.add(createInitializeMetadataPointerInstruction(mint, payer.publicKey, mint, TOKEN22_PROGRAM)); // Initialize mint setupTransaction.add(createInitializeMintInstruction(mint, params.decimals, payer.publicKey, payer.publicKey, TOKEN22_PROGRAM)); // const _setupSignature = await sendTransactionWithRetry( // connection, // setupTransaction, // [payer, mintKeypair], // options // ); // Transaction 2: Initialize metadata and mint tokens const mintingTransaction = new Transaction(); mintingTransaction.feePayer = payer.publicKey; // Initialize metadata if URI provided if (params.uri) { const metadataSpace = calculateMetadataSpace(params.name, params.symbol, params.uri); // Get current account info to calculate additional rent const accountInfo = yield connection.getAccountInfo(mint); if (!accountInfo) { throw new Error('Mint account not found after setup'); } const currentSize = accountInfo.data.length; const newSize = currentSize + metadataSpace; const additionalRent = (yield connection.getMinimumBalanceForRentExemption(newSize)) - accountInfo.lamports; // Transfer additional rent if needed if (additionalRent > 0) { mintingTransaction.add(SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: mint, lamports: additionalRent })); } // Initialize metadata mintingTransaction.add(createInitializeInstruction({ programId: TOKEN22_PROGRAM, metadata: mint, updateAuthority: payer.publicKey, mint, mintAuthority: payer.publicKey, name: params.name, symbol: params.symbol, uri: params.uri })); } // Create associated token account const associatedToken = getAssociatedTokenAddressSync(mint, payer.publicKey, false, TOKEN22_PROGRAM, ASSOCIATED_TOKEN_PROGRAM); mintingTransaction.add(createAssociatedTokenAccountInstruction(payer.publicKey, associatedToken, payer.publicKey, mint, TOKEN22_PROGRAM, ASSOCIATED_TOKEN_PROGRAM)); // Mint tokens const mintAmount = BigInt(params.supply) * BigInt(10 ** params.decimals); mintingTransaction.add(createMintToInstruction(mint, associatedToken, payer.publicKey, mintAmount, [], TOKEN22_PROGRAM)); const mintingSignature = yield sendTransactionWithRetry(connection, mintingTransaction, [payer], options); return { signature: mintingSignature, tokenAddress: mint.toString(), associatedTokenAddress: associatedToken.toString(), transactionUrl: `https://explorer.gorbchain.xyz/tx/${mintingSignature}` }; }); } /** * Create a new Token22 token with metadata (Single Transaction Approach) * * Faster execution but may be less reliable for complex metadata */ export function createToken22SingleTx(connection_1, payer_1, params_1) { return __awaiter(this, arguments, void 0, function* (connection, payer, params, options = {}) { validateTokenParameters(params); const mintKeypair = Keypair.generate(); const mint = mintKeypair.publicKey; const extensions = [ExtensionType.MetadataPointer]; const mintLen = getMintLen(extensions); const rentExemption = yield connection.getMinimumBalanceForRentExemption(mintLen); const transaction = new Transaction(); transaction.feePayer = payer.publicKey; // Create the mint account transaction.add(SystemProgram.createAccount({ fromPubkey: payer.publicKey, newAccountPubkey: mint, lamports: rentExemption, space: mintLen, programId: TOKEN22_PROGRAM })); // Initialize metadata pointer transaction.add(createInitializeMetadataPointerInstruction(mint, payer.publicKey, mint, TOKEN22_PROGRAM)); // Initialize mint transaction.add(createInitializeMintInstruction(mint, params.decimals, payer.publicKey, payer.publicKey, TOKEN22_PROGRAM)); // Initialize metadata if URI provided if (params.uri) { transaction.add(createInitializeInstruction({ programId: TOKEN22_PROGRAM, metadata: mint, updateAuthority: payer.publicKey, mint, mintAuthority: payer.publicKey, name: params.name, symbol: params.symbol, uri: params.uri })); } // Create associated token account const associatedToken = getAssociatedTokenAddressSync(mint, payer.publicKey, false, TOKEN22_PROGRAM, ASSOCIATED_TOKEN_PROGRAM); transaction.add(createAssociatedTokenAccountInstruction(payer.publicKey, associatedToken, payer.publicKey, mint, TOKEN22_PROGRAM, ASSOCIATED_TOKEN_PROGRAM)); // Mint tokens const mintAmount = BigInt(params.supply) * BigInt(10 ** params.decimals); transaction.add(createMintToInstruction(mint, associatedToken, payer.publicKey, mintAmount, [], TOKEN22_PROGRAM)); const signature = yield sendTransactionWithRetry(connection, transaction, [payer, mintKeypair], options); return { signature, tokenAddress: mint.toString(), associatedTokenAddress: associatedToken.toString(), transactionUrl: `https://explorer.gorbchain.xyz/tx/${signature}` }; }); } /** * Create a new NFT using Metaplex Core */ export function createNFT(connection_1, wallet_1, params_1) { return __awaiter(this, arguments, void 0, function* (connection, wallet, // Wallet adapter params, _options = {}) { validateNFTParameters(params); try { // Setup UMI context const umi = createUmi(connection.rpcEndpoint) .use(mplCore()) .use(walletAdapterIdentity(wallet)); const assetSigner = generateSigner(umi); // Create NFT with basic parameters for now // Note: Advanced features like royalties and attributes can be added in future versions const result = yield createV1(umi, { asset: assetSigner, name: params.name, uri: params.uri }).sendAndConfirm(umi); // Convert signature from Uint8Array to hex string const signature = Array.from(result.signature, byte => byte.toString(16).padStart(2, '0')).join(''); return { signature, assetAddress: assetSigner.publicKey.toString(), transactionUrl: `https://explorer.gorbchain.xyz/tx/${signature}` }; } catch (error) { // console.error('❌ NFT creation failed:', error); throw new Error(`NFT creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } }); } /** * Check if user has sufficient balance for transaction */ export function checkSufficientBalance(connection, payer, estimatedCost) { return __awaiter(this, void 0, void 0, function* () { const balance = yield connection.getBalance(payer); const requiredBalance = estimatedCost + 5000000; // 0.005 SOL buffer return { sufficient: balance >= requiredBalance, balance, required: requiredBalance }; }); } /** * Estimate cost for token creation */ export function estimateTokenCreationCost(connection, params) { return __awaiter(this, void 0, void 0, function* () { const extensions = [ExtensionType.MetadataPointer]; const mintLen = getMintLen(extensions); const mintRent = yield connection.getMinimumBalanceForRentExemption(mintLen); const ataRent = yield connection.getMinimumBalanceForRentExemption(165); // ATA size // Base transaction fees (estimated) const baseFees = 10000; // 0.00001 SOL // Metadata space if URI provided let metadataRent = 0; if (params.uri) { const metadataSpace = calculateMetadataSpace(params.name, params.symbol, params.uri); metadataRent = yield connection.getMinimumBalanceForRentExemption(metadataSpace); } return mintRent + ataRent + baseFees + metadataRent; }); } /** * Estimate cost for NFT creation */ export function estimateNFTCreationCost(connection, params) { return __awaiter(this, void 0, void 0, function* () { // Base NFT creation cost (estimated) const baseCost = 10000000; // 0.01 SOL // Additional costs for plugins let pluginCost = 0; if (params.royaltyBasisPoints && params.royaltyBasisPoints > 0) { pluginCost += 2000000; // 0.002 SOL for royalty plugin } if (params.attributes && params.attributes.length > 0) { pluginCost += 1000000; // 0.001 SOL for attributes plugin } return baseCost + pluginCost; }); } /** * Get token information from mint address */ export function getTokenInfo(connection, mintAddress) { return __awaiter(this, void 0, void 0, function* () { const mint = new PublicKey(mintAddress); const accountInfo = yield connection.getAccountInfo(mint); if (!accountInfo) { throw new Error('Token mint not found'); } // Parse mint data (simplified - you'd need proper deserialization) const data = accountInfo.data; return { mint: mintAddress, supply: '0', // Would need proper parsing decimals: data[44], // Decimal offset in mint data mintAuthority: null, // Would need proper parsing freezeAuthority: null, // Would need proper parsing metadata: undefined // Would need metadata parsing }; }); } // Export all functions and types export { TOKEN22_PROGRAM, ASSOCIATED_TOKEN_PROGRAM, CUSTOM_MPL_CORE_PROGRAM, calculateMetadataSpace, calculateMintAccountSize, validateTokenParameters, validateNFTParameters, sendTransactionWithRetry };