revshare-sdk
Version:
JavaScript SDK for RevShare Public API - Create bonding curve tokens with distribution features
731 lines (692 loc) • 23.9 kB
JavaScript
;
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;