UNPKG

revshare-sdk

Version:

JavaScript SDK for RevShare Public API - Create bonding curve tokens with distribution features

731 lines (692 loc) 23.9 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var web3_js = require('@solana/web3.js'); /** * RevShare SDK - JavaScript client for RevShare Public API * * This SDK provides a clean interface to interact with the RevShare API * for creating bonding curve tokens with distribution features. * * @example * import { RevShareSDK } from 'revshare-sdk'; * * const revshare = new RevShareSDK(); * * // All-in-one token creation with automatic funding * const result = await revshare.createToken({ * keypair: myKeypair, * name: 'MyToken', * ticker: 'MTK', * description: 'My awesome token', * imageUrl: 'https://example.com/image.png' * }); */ /** * Custom error class for RevShare API errors */ class RevShareError extends Error { constructor(message, status = 0, details = null, originalError = null) { super(message); this.name = 'RevShareError'; this.status = status; this.details = details; this.originalError = originalError; } } class RevShareSDK { /** * Create a new RevShare SDK instance * @param {Object} options - Configuration options * @param {number} options.timeout - Request timeout in milliseconds (default: 30000) * @param {Object} options.headers - Additional headers to include in requests */ constructor(options = {}) { this.baseUrl = 'https://public-api.revshare.dev'; // Fixed base URL this.timeout = options.timeout || 30000; this.headers = { 'Content-Type': 'application/json', ...options.headers }; } /** * Make an HTTP request to the API * @param {string} endpoint - API endpoint path * @param {Object} options - Request options * @returns {Promise<Object>} API response * @private */ async _request(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; const config = { method: 'GET', headers: this.headers, timeout: this.timeout, ...options }; // Handle query parameters for GET requests if (config.method === 'GET' && options.params) { const searchParams = new URLSearchParams(); Object.entries(options.params).forEach(([key, value]) => { if (value !== undefined && value !== null) { searchParams.append(key, value); } }); const queryString = searchParams.toString(); if (queryString) { config.url = `${url}?${queryString}`; } else { config.url = url; } } else { config.url = url; } try { const response = await fetch(config.url, config); const data = await response.json(); if (!response.ok) { throw new RevShareError(data.error || `HTTP ${response.status}`, response.status, data); } return data; } catch (error) { if (error instanceof RevShareError) { throw error; } throw new RevShareError(error.message || 'Network error', 0, null, error); } } /** * Check if Solana web3 is available * @private */ _checkSolanaWeb3() { if (typeof window !== 'undefined' && window.solana) { return window.solana; } try { return { Connection: web3_js.Connection, PublicKey: web3_js.PublicKey, Transaction: web3_js.Transaction, SystemProgram: web3_js.SystemProgram, LAMPORTS_PER_SOL: web3_js.LAMPORTS_PER_SOL }; } catch (error) { throw new RevShareError('Solana web3.js is required. Please install: npm install @solana/web3.js', 0); } } /** * Get Solana connection * @private */ _getConnection() { const { Connection } = this._checkSolanaWeb3(); return new Connection('https://api.mainnet-beta.solana.com', 'confirmed'); } /** * Check wallet balance and transfer funds if needed * @param {Object} keypair - Solana keypair * @param {string} fundingWallet - Wallet to send funds to * @param {number} amount - Amount in SOL to send * @private */ async _handleFunding(keypair, fundingWallet, amount) { const { Connection, PublicKey, Transaction, SystemProgram, LAMPORTS_PER_SOL } = this._checkSolanaWeb3(); const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed'); const fromPubkey = keypair.publicKey; const toPubkey = new PublicKey(fundingWallet); const lamports = Math.round(amount * LAMPORTS_PER_SOL); // Check current balance const balance = await connection.getBalance(fromPubkey); const requiredBalance = Math.round(lamports + 0.000005 * LAMPORTS_PER_SOL); // Add transaction fee if (balance < requiredBalance) { throw new RevShareError(`Insufficient balance. Required: ${requiredBalance / LAMPORTS_PER_SOL} SOL, Available: ${balance / LAMPORTS_PER_SOL} SOL`, 400); } // Create and send transaction const transaction = new Transaction().add(SystemProgram.transfer({ fromPubkey, toPubkey, lamports })); const signature = await connection.sendTransaction(transaction, [keypair]); await connection.confirmTransaction(signature); return signature; } /** * All-in-one token creation with automatic funding * * @param {Object} params - Token creation parameters * @param {Object} params.keypair - Solana keypair for funding (paying fees) * @param {string} params.developerWallet - Developer wallet address (receives tokens and rewards) * @param {string} params.name - Token name * @param {string} params.ticker - Token symbol/ticker * @param {string} params.description - Token description * @param {string} params.imageUrl - Token image URL * @param {string} [params.website] - Project website URL * @param {string} [params.twitter] - Twitter URL * @param {string} [params.telegram] - Telegram URL * @param {number} [params.visible=0] - Token visibility (0=visible, 1=hidden) * @param {number} [params.decimals=9] - Token decimals * @param {number} [params.taxTier=0] - Tax tier percentage * @param {number} [params.initialBuy=0] - Initial buy amount in SOL (will be added to funding amount) * @param {string} [params.reward_ca] - Reward token contract address (default: SOL) * @param {number} [params.mode=0] - Distribution mode (0=Rewards, 1=Jackpot, 2=Lottery, 3=No Rewards) * @param {number} [params.dev_fee_percentage=50] - Developer fee percentage * @param {number} [params.bondingCurveType=1] - Bonding curve type (1=20 SOL threshold, 2=60 SOL threshold) * @param {string} [params.ref] - Referral wallet address (defaults to 1% fee; configurable for launchpads) * @returns {Promise<Object>} Complete token creation result * * @example * const result = await revshare.createBondingToken({ * keypair: myKeypair, // For paying fees * developerWallet: 'your-wallet-address-here', // Receives tokens and rewards * name: 'MyToken', * ticker: 'MTK', * description: 'My awesome token', * imageUrl: 'https://example.com/image.png', * website: 'https://mytoken.com' * }); */ async createBondingToken(params) { const { keypair, developerWallet, name, ticker, description, imageUrl, website = '', twitter = '', telegram = '', visible = 0, decimals = 9, taxTier = 0, initialBuy = 0, reward_ca = 'So11111111111111111111111111111111111111112', mode = 0, dev_fee_percentage = 50, bondingCurveType = 1, ref } = params; // Validate required parameters if (!keypair || !developerWallet || !name || !ticker || !description || !imageUrl) { throw new RevShareError('Missing required parameters. Required: keypair, developerWallet, name, ticker, description, imageUrl', 400); } try { console.log('🚀 Starting all-in-one token creation...'); // Step 1: Prepare token creation console.log('📋 Step 1: Preparing token creation...'); const prepareResult = await this._request('/prepare', { method: 'POST' }); console.log('✅ Preparation successful!'); console.log('Request ID:', prepareResult.request_id); console.log('Funding wallet:', prepareResult.funding_wallet); console.log('Amount to fund:', prepareResult.amount_to_fund, 'SOL'); // Step 2: Handle funding automatically console.log('💰 Step 2: Handling funding...'); // Calculate total amount needed: service fees + initial buy amount const totalAmount = prepareResult.amount_to_fund + initialBuy; if (initialBuy > 0) { console.log(`📊 Funding breakdown:`); console.log(` - Service fees: ${prepareResult.amount_to_fund} SOL`); console.log(` - Initial buy: ${initialBuy} SOL`); console.log(` - Total: ${totalAmount} SOL`); } const fundingSignature = await this._handleFunding(keypair, prepareResult.funding_wallet, totalAmount); console.log('✅ Funding successful!'); console.log('Funding transaction:', fundingSignature); // Step 3: Create token console.log('🎯 Step 3: Creating token...'); const createResult = await this._request('/create', { method: 'POST', body: JSON.stringify({ request_id: prepareResult.request_id, name, ticker, description, imageUrl, developerWallet, website, twitter, telegram, visible, decimals, taxTier, buyAmount: initialBuy, reward_ca, mode, dev_fee_percentage, bondingCurveType, ref }) }); console.log('✅ Token creation successful!'); return { success: true, prepare: prepareResult, funding: { signature: fundingSignature, amount: totalAmount, serviceFees: prepareResult.amount_to_fund, initialBuy: initialBuy, wallet: prepareResult.funding_wallet }, create: createResult, summary: { request_id: prepareResult.request_id, mintAddress: createResult.mintAddress, txSignature: createResult.txSignature, fundingSignature: fundingSignature, developerWallet: developerWallet } }; } catch (error) { console.error('❌ Token creation failed:', error.message); throw error; } } /** * Manual prepare step (for advanced users) * * @returns {Promise<Object>} Preparation result */ async prepare() { return this._request('/prepare', { method: 'POST' }); } /** * Manual create step (for advanced users) * * @param {Object} params - Token creation parameters * @returns {Promise<Object>} Creation result */ async create(params) { const { request_id, name, ticker, description, imageUrl, developerWallet, website = '', twitter = '', telegram = '', visible = 0, decimals = 9, taxTier = 0, initialBuy = 0, reward_ca = 'So11111111111111111111111111111111111111112', mode = 0, dev_fee_percentage = 50, bondingCurveType = 1, ref } = params; // Validate required parameters if (!request_id || !name || !ticker || !description || !imageUrl || !developerWallet) { throw new RevShareError('Missing required parameters. Required: request_id, name, ticker, description, imageUrl, developerWallet', 400); } return this._request('/create', { method: 'POST', body: JSON.stringify({ request_id, name, ticker, description, imageUrl, developerWallet, website, twitter, telegram, visible, decimals, taxTier, buyAmount: initialBuy, reward_ca, mode, dev_fee_percentage, bondingCurveType, ref }) }); } /** * Check API health status * * @returns {Promise<Object>} Health check result */ async health() { return this._request('/health'); } /** * Get API documentation * * @returns {Promise<Object>} API documentation */ async docs() { return this._request('/docs'); } /** * Get bonding curve progress and pool state information * * @param {Object} params - Progress parameters * @param {string} params.tokenAddress - The token mint address * @param {string} [params.rpcUrl] - Custom RPC URL (falls back to default if invalid) * @returns {Promise<Object>} Bonding curve progress data * * @example * const result = await revshare.getBondingCurveProgress({ * tokenAddress: 'BSAwhpQuJDV6tL27VYsYoCUMcRKeTigTbDX8CyDMwUP', * rpcUrl: 'https://api.mainnet-beta.solana.com' * }); */ async getBondingCurveProgress(params) { const { tokenAddress, rpcUrl } = params; // Validate required parameters if (!tokenAddress) { throw new RevShareError('Missing required parameters. Required: tokenAddress', 400); } try { console.log('📊 Getting bonding curve progress...'); console.log(`Token: ${tokenAddress}`); if (rpcUrl) { console.log(`RPC: ${rpcUrl}`); } const queryParams = { tokenAddress }; if (rpcUrl) { queryParams.rpcUrl = rpcUrl; } const result = await this._request('/bonding-curve-progress', { method: 'GET', params: queryParams }); console.log('✅ Bonding curve progress retrieved successfully!'); return { success: true, tokenAddress, poolId: result.poolId, progress: result.progress, migrationThreshold: result.migrationThreshold, message: result.message }; } catch (error) { console.error('❌ Failed to get bonding curve progress:', error.message); throw error; } } /** * Build a buy transaction for bonding curve tokens (pre-bonding phase) * * This method is specifically for tokens that are in the bonding curve phase * and have not yet bonded. For regular token swaps after bonding, use different methods. * * @param {Object} params - Buy parameters * @param {string} params.tokenAddress - Bonding curve token contract address * @param {number} params.amount - Amount in SOL to spend * @param {string} params.buyerWallet - Buyer's wallet address * @param {number} [params.slippageBps=500] - Slippage in basis points (default: 500 = 5%) * @param {string} [params.rpcUrl] - Custom RPC URL (falls back to default if invalid) * @returns {Promise<Object>} Transaction data * * @example * const result = await revshare.buildBondingCurveBuyTransaction({ * tokenAddress: 'AJuWVNdaFyThTnrhTXUUTufyGG35nSbuqYLAVTV7uoZz', * amount: 0.001, // 0.001 SOL * buyerWallet: 'Bwvt6e5gzsAhU6k6cCWibaBz66Lo3hCXSgvuU9wosYCk', * slippageBps: 500, // 5% slippage * rpcUrl: 'https://api.mainnet-beta.solana.com' * }); */ async buildBondingCurveBuyTransaction(params) { const { tokenAddress, amount, buyerWallet, slippageBps = 500, rpcUrl } = params; // Validate required parameters if (!tokenAddress || !amount || !buyerWallet) { throw new RevShareError('Missing required parameters. Required: tokenAddress, amount, buyerWallet', 400); } try { console.log('🛒 Building bonding curve buy transaction...'); console.log(`Bonding Token: ${tokenAddress}`); console.log(`Amount: ${amount} SOL`); console.log(`Buyer: ${buyerWallet}`); console.log(`Slippage: ${slippageBps} bps`); if (rpcUrl) { console.log(`RPC: ${rpcUrl}`); } const requestBody = { tokenAddress, action: 'buy', amount, buyerWallet, slippageBps }; if (rpcUrl) { requestBody.rpcUrl = rpcUrl; } const result = await this._request('/swap-bonding-curve', { method: 'POST', body: JSON.stringify(requestBody) }); console.log('✅ Bonding curve buy transaction built successfully!'); return { success: true, action: 'buy', tokenType: 'bonding-curve', tokenAddress, amount, buyerWallet, slippageBps, poolId: result.poolId, quote: result.quote, transaction: result.transaction, expectedTokens: result.quote?.amountOutFormatted, transactionBase64: result.transaction }; } catch (error) { console.error('❌ Bonding curve buy transaction build failed:', error.message); throw error; } } /** * Build a sell transaction for bonding curve tokens (pre-bonding phase) * * This method is specifically for tokens that are in the bonding curve phase * and have not yet bonded. For regular token swaps after bonding, use different methods. * * @param {Object} params - Sell parameters * @param {string} params.tokenAddress - Bonding curve token contract address * @param {string} params.amount - Amount of tokens to sell (in token units) * @param {string} params.sellerWallet - Seller's wallet address * @param {number} [params.slippageBps=500] - Slippage in basis points (default: 500 = 5%) * @param {string} [params.rpcUrl] - Custom RPC URL (falls back to default if invalid) * @returns {Promise<Object>} Transaction data * * @example * const result = await revshare.buildBondingCurveSellTransaction({ * tokenAddress: 'AJuWVNdaFyThTnrhTXUUTufyGG35nSbuqYLAVTV7uoZz', * amount: '1000000', // 1,000,000 tokens * sellerWallet: 'Bwvt6e5gzsAhU6k6cCWibaBz66Lo3hCXSgvuU9wosYCk', * slippageBps: 500, // 5% slippage * rpcUrl: 'https://api.mainnet-beta.solana.com' * }); */ async buildBondingCurveSellTransaction(params) { const { tokenAddress, amount, sellerWallet, slippageBps = 500, rpcUrl } = params; // Validate required parameters if (!tokenAddress || !amount || !sellerWallet) { throw new RevShareError('Missing required parameters. Required: tokenAddress, amount, sellerWallet', 400); } try { console.log('💸 Building bonding curve sell transaction...'); console.log(`Bonding Token: ${tokenAddress}`); console.log(`Amount: ${amount} tokens`); console.log(`Seller: ${sellerWallet}`); console.log(`Slippage: ${slippageBps} bps`); if (rpcUrl) { console.log(`RPC: ${rpcUrl}`); } const requestBody = { tokenAddress, action: 'sell', amount, buyerWallet: sellerWallet, // API uses buyerWallet for both buy/sell slippageBps }; if (rpcUrl) { requestBody.rpcUrl = rpcUrl; } const result = await this._request('/swap-bonding-curve', { method: 'POST', body: JSON.stringify(requestBody) }); console.log('✅ Bonding curve sell transaction built successfully!'); return { success: true, action: 'sell', tokenType: 'bonding-curve', tokenAddress, amount, sellerWallet, slippageBps, poolId: result.poolId, quote: result.quote, transaction: result.transaction, expectedSOL: result.quote?.amountOutFormatted, transactionBase64: result.transaction }; } catch (error) { console.error('❌ Bonding curve sell transaction build failed:', error.message); throw error; } } /** * Execute a bonding curve swap transaction (buy or sell) with automatic signing and sending * * This method is specifically for tokens that are in the bonding curve phase * and have not yet bonded. For regular token swaps after bonding, use different methods. * * @param {Object} params - Swap parameters * @param {string} params.tokenAddress - Bonding curve token contract address * @param {string} params.action - 'buy' or 'sell' * @param {number|string} params.amount - Amount (SOL for buy, tokens for sell) * @param {Object} params.keypair - Solana keypair for signing * @param {number} [params.slippageBps=500] - Slippage in basis points (default: 500 = 5%) * @param {string} [params.rpcUrl] - Custom RPC URL (falls back to default if invalid) * @returns {Promise<Object>} Swap result with transaction signature * * @example * // Buy bonding curve tokens * const buyResult = await revshare.executeBondingCurveSwap({ * tokenAddress: 'AJuWVNdaFyThTnrhTXUUTufyGG35nSbuqYLAVTV7uoZz', * action: 'buy', * amount: 0.001, // 0.001 SOL * keypair: myKeypair, * slippageBps: 500, * rpcUrl: 'https://api.mainnet-beta.solana.com' * }); * * // Sell bonding curve tokens * const sellResult = await revshare.executeBondingCurveSwap({ * action: 'sell', * amount: '1000000', // 1,000,000 tokens * keypair: myKeypair, * slippageBps: 500, * rpcUrl: 'https://api.mainnet-beta.solana.com' * }); */ async executeBondingCurveSwap(params) { const { tokenAddress, action, amount, keypair, slippageBps = 500, rpcUrl } = params; // Validate required parameters if (!tokenAddress || !action || !amount || !keypair) { throw new RevShareError('Missing required parameters. Required: tokenAddress, action, amount, keypair', 400); } if (!['buy', 'sell'].includes(action)) { throw new RevShareError('Action must be either "buy" or "sell"', 400); } try { console.log(`🔄 Executing bonding curve ${action} swap...`); console.log(`Bonding Token: ${tokenAddress}`); console.log(`Amount: ${amount} ${action === 'buy' ? 'SOL' : 'tokens'}`); console.log(`Wallet: ${keypair.publicKey.toString()}`); if (rpcUrl) { console.log(`RPC: ${rpcUrl}`); } // Step 1: Build the transaction const buildParams = { tokenAddress, amount, slippageBps, [action === 'buy' ? 'buyerWallet' : 'sellerWallet']: keypair.publicKey.toString() }; if (rpcUrl) { buildParams.rpcUrl = rpcUrl; } const buildResult = action === 'buy' ? await this.buildBondingCurveBuyTransaction(buildParams) : await this.buildBondingCurveSellTransaction(buildParams); // Step 2: Execute the transaction const { Transaction } = this._checkSolanaWeb3(); const connection = this._getConnection(); // Deserialize the transaction const transaction = Transaction.from(Buffer.from(buildResult.transactionBase64, 'base64')); // Sign with the keypair transaction.sign(keypair); // Send to network const signature = await connection.sendTransaction(transaction, [keypair]); await connection.confirmTransaction(signature); console.log('✅ Bonding curve swap executed successfully!'); console.log(`Transaction signature: ${signature}`); return { success: true, action, tokenType: 'bonding-curve', tokenAddress, amount, wallet: keypair.publicKey.toString(), slippageBps, poolId: buildResult.poolId, quote: buildResult.quote, transactionSignature: signature, expectedOutput: action === 'buy' ? buildResult.expectedTokens : buildResult.expectedSOL }; } catch (error) { console.error(`❌ Bonding curve ${action} swap failed:`, error.message); throw error; } } } exports.RevShareError = RevShareError; exports.RevShareSDK = RevShareSDK; exports.default = RevShareSDK;