UNPKG

@solsdk/tokenflow_sdk

Version:

A simple SDK for interacting with tokenflow

282 lines (278 loc) 14.2 kB
'use strict'; var bn = require('./_virtual/bn.cjs'); var anchor = require('@coral-xyz/anchor'); var splToken = require('@solana/spl-token'); var web3_js = require('@solana/web3.js'); var util = require('./util.cjs'); var bondingCurveAccount = require('./bondingCurveAccount.cjs'); var globalAccount = require('./globalAccount.cjs'); var events = require('./events.cjs'); var happy_pump_program = require('./IDL/happy_pump_program.json.cjs'); // const MPL_TOKEN_METADATA_PROGRAM_ID = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"; const GLOBAL_ACCOUNT_SEED = "global"; const MINT_AUTHORITY_SEED = "mint-authority"; const BONDING_CURVE_SEED = "bonding-curve"; const METADATA_SEED = "metadata"; const DEFAULT_DECIMALS = 6; class HappyPumpSDK { program; connection; constructor(provider) { this.program = new anchor.Program(happy_pump_program.default, provider); this.connection = this.program.provider.connection; } async create(creator, mint, createTokenMetadata, tradeAuthority, priorityFees, commitment = util.DEFAULT_COMMITMENT, finality = util.DEFAULT_FINALITY) { const globalAccount = await this.getGlobalAccount(commitment); const tokenMetadata = await this.createTokenMetadata(createTokenMetadata); const instructions = await this.getCreateInstructions(creator.publicKey, mint, createTokenMetadata.name, createTokenMetadata.symbol, tokenMetadata.uri, globalAccount.feeRecipient, tradeAuthority); return await util.sendTx(this.connection, instructions, creator.publicKey, [creator, mint], priorityFees, commitment, finality); } async createAndBuy(creator, mint, createTokenMetadata, buyAmountSol, slippageBasisPoints = BigInt(500), tradeAuthority, priorityFees, commitment = util.DEFAULT_COMMITMENT, finality = util.DEFAULT_FINALITY) { const globalAccount = await this.getGlobalAccount(commitment); const instructions = []; const tokenMetadata = await this.createTokenMetadata(createTokenMetadata); const createIxs = await this.getCreateInstructions(creator.publicKey, mint, createTokenMetadata.name, createTokenMetadata.symbol, tokenMetadata.uri, globalAccount.feeRecipient, tradeAuthority?.publicKey); instructions.push(...createIxs); if (buyAmountSol > 0) { const globalAccount = await this.getGlobalAccount(commitment); const buyAmount = globalAccount.getInitialBuyPrice(buyAmountSol); const buyAmountWithSlippage = util.calculateWithSlippageBuy(buyAmountSol, slippageBasisPoints); const buyIxs = await this.getBuyInstructions(creator.publicKey, mint.publicKey, globalAccount.feeRecipient, creator.publicKey, buyAmount, buyAmountWithSlippage, tradeAuthority?.publicKey, commitment); instructions.push(...buyIxs); } const signers = [creator, mint]; if (!!tradeAuthority) { signers.push(tradeAuthority); } return await util.sendTx(this.connection, instructions, creator.publicKey, signers, priorityFees, commitment, finality); } async setBondingCurveCfg(user, mint, tradeAuthority, priorityFees, commitment = util.DEFAULT_COMMITMENT, finality = util.DEFAULT_FINALITY) { const instructions = await this.getSetBondingCurveCfgInstructions(user.publicKey, mint, tradeAuthority); return await util.sendTx(this.connection, instructions, user.publicKey, [user], priorityFees, commitment, finality); } async buy(buyer, mint, buyAmountSol, slippageBasisPoints = BigInt(500), tradeAuthority, priorityFees, commitment = util.DEFAULT_COMMITMENT, finality = util.DEFAULT_FINALITY) { const instructions = await this.getBuyInstructionsBySolAmount(buyer.publicKey, mint, buyAmountSol, slippageBasisPoints, tradeAuthority?.publicKey, commitment); const signers = [buyer]; if (!!tradeAuthority) { signers.push(tradeAuthority); } return await util.sendTx(this.connection, instructions, buyer.publicKey, signers, priorityFees, commitment, finality); } async sell(seller, mint, sellTokenAmount, slippageBasisPoints = 500n, priorityFees, commitment = util.DEFAULT_COMMITMENT, finality = util.DEFAULT_FINALITY) { const sellIxs = await this.getSellInstructionsByTokenAmount(seller.publicKey, mint, sellTokenAmount, slippageBasisPoints, commitment); return await util.sendTx(this.connection, sellIxs, seller.publicKey, [seller], priorityFees, commitment, finality); } async getCreateInstructions(creator, mint, name, symbol, uri, feeRecipient, tradeAuthority) { /* const mplTokenMetadata = new PublicKey(MPL_TOKEN_METADATA_PROGRAM_ID); const [metadataPDA] = PublicKey.findProgramAddressSync( [ Buffer.from(METADATA_SEED), mplTokenMetadata.toBuffer(), mint.publicKey.toBuffer(), ], mplTokenMetadata, ); */ const createIx = await this.program.methods .create({ name, symbol, uri, // @ts-ignore tradeAuthority: tradeAuthority ?? null, creator, }) .accounts({ program: this.program.programId, mint: mint.publicKey, creator, feeRecipient, }).instruction(); return [createIx]; } async getBuyInstructionsBySolAmount(buyer, mint, buyAmountSol, slippageBasisPoints = 500n, tradeAuthority, commitment = util.DEFAULT_COMMITMENT) { const globalAccount = await this.getGlobalAccount(commitment); const bondingCurveAccount = await this.getBondingCurveAccount(mint, commitment); if (!bondingCurveAccount) { throw new Error(`Bonding curve account not found: ${mint.toBase58()}`); } const totalFeeBasisPoints = globalAccount.systemFeeBasisPoints + globalAccount.creatorFeeBasisPoints; const effectiveBuyAmountSol = buyAmountSol - (buyAmountSol * totalFeeBasisPoints) / 10000n; const buyAmount = bondingCurveAccount.getBuyPrice(effectiveBuyAmountSol); const buyAmountWithSlippage = util.calculateWithSlippageBuy(buyAmountSol, slippageBasisPoints); console.log(`buyAmountSol: ${buyAmountSol}, effectiveBuyAmountSol: ${effectiveBuyAmountSol}, buyAmount: ${buyAmount}, buyAmountWithSlippage: ${buyAmountWithSlippage}`); return await this.getBuyInstructions(buyer, mint, globalAccount.feeRecipient, bondingCurveAccount.creator, buyAmount, buyAmountWithSlippage, tradeAuthority, commitment); } async getBuyInstructions(buyer, mint, feeRecipient, creatorFeeRecipient, amount, solAmount, tradeAuthority, commitment = util.DEFAULT_COMMITMENT) { const instructions = []; const associatedUser = await splToken.getAssociatedTokenAddress(mint, buyer, false); try { await splToken.getAccount(this.connection, associatedUser, commitment); } catch (e) { // console.log('catch createAssociatedTokenAccountInstruction: ', e); instructions.push(splToken.createAssociatedTokenAccountInstruction(buyer, associatedUser, buyer, mint)); } console.log("amount: ", amount); console.log("solAmount: ", solAmount); instructions.push(await this.program.methods.buy({ tokenAmount: new bn.bnExports.BN(amount.toString()), maxSolCost: new bn.bnExports.BN(solAmount.toString()), }).accounts({ user: buyer, mint, feeRecipient, creatorFeeRecipient, // 换掉报错否 // @ts-ignore tradeAuthority: tradeAuthority ?? null, }).instruction()); return instructions; } // sell async getSellInstructionsByTokenAmount(seller, mint, sellTokenAmount, slippageBasisPoints = 500n, commitment = util.DEFAULT_COMMITMENT) { const bondingCurveAccount = await this.getBondingCurveAccount(mint, commitment); if (!bondingCurveAccount) { throw new Error(`Bonding curve account not found: ${mint.toBase58()}`); } const globalAccount = await this.getGlobalAccount(commitment); const minSolOutput = bondingCurveAccount.getSellPrice(sellTokenAmount, globalAccount.systemFeeBasisPoints + globalAccount.creatorFeeBasisPoints); const sellAmountWithSlippage = util.calculateWithSlippageSell(minSolOutput, slippageBasisPoints); console.log(`minSolOutput: ${minSolOutput}, sellTokenAmount: ${sellTokenAmount}`); return await this.getSellInstructions(seller, mint, globalAccount.feeRecipient, bondingCurveAccount.creator, sellTokenAmount, sellAmountWithSlippage); } async getSellInstructions(seller, mint, feeRecipient, creatorFeeRecipient, amount, minSolOutput) { const instructions = []; instructions.push(await this.program.methods .sell({ tokenAmount: new bn.bnExports.BN(amount.toString()), minSolOutput: new bn.bnExports.BN(minSolOutput.toString()), }) .accounts({ program: this.program.programId, user: seller, mint, feeRecipient, creatorFeeRecipient, }) .instruction()); return instructions; } async getSetBondingCurveCfgInstructions(user, mint, tradeAuthority) { const setBondingCurveCfgIx = await this.program.methods.setBondingCurveCfg({ tradeAuthority, }).accounts({ user, mint, }).instruction(); return [setBondingCurveCfgIx]; } async getGlobalAccount(commitment = util.DEFAULT_COMMITMENT) { const [globalAccountPDA] = web3_js.PublicKey.findProgramAddressSync([Buffer.from(GLOBAL_ACCOUNT_SEED)], this.program.programId); /* const tokenAccount = await this.connection.getAccountInfo( globalAccountPDA, commitment, ); */ const res = await this.program.account.global.fetch(globalAccountPDA, commitment); return globalAccount.GlobalAccount.fromObject(res); } getBondingCurvePDA(mint) { return web3_js.PublicKey.findProgramAddressSync([Buffer.from("bonding-curve"), mint.toBuffer()], this.program.programId)[0]; } async getBondingCurveAccount(mint, commitment = util.DEFAULT_COMMITMENT) { const tokenAccount = await this.connection.getAccountInfo(this.getBondingCurvePDA(mint), commitment); if (!tokenAccount) { return undefined; } return bondingCurveAccount.BondingCurveAccount.fromBuffer(tokenAccount.data); } async createTokenMetadata(create) { // Validate file if (!(create.file instanceof Blob)) { throw new Error('File must be a Blob or File object'); } let formData = new FormData(); formData.append("file", create.file, 'image.png'); // Add filename formData.append("name", create.name); formData.append("symbol", create.symbol); formData.append("description", create.description); formData.append("twitter", create.twitter || ""); formData.append("telegram", create.telegram || ""); formData.append("website", create.website || ""); formData.append("showName", "true"); try { const request = await fetch("https://happypump.io/api/ipfs", { method: "POST", headers: { 'Accept': 'application/json', }, body: formData, credentials: 'same-origin' }); if (request.status === 500) { // Try to get more error details const errorText = await request.text(); throw new Error(`Server error (500): ${errorText || 'No error details available'}`); } if (!request.ok) { throw new Error(`HTTP error! status: ${request.status}`); } const responseText = await request.text(); if (!responseText) { throw new Error('Empty response received from server'); } try { return JSON.parse(responseText); } catch (e) { throw new Error(`Invalid JSON response: ${responseText}`); } } catch (error) { console.error('Error in createTokenMetadata:', error); // throw error; return { uri: "http://www.foo.com", }; } } // events addEventListener(eventType, callback) { return this.program.addEventListener(eventType, (event, slot, signature) => { let processedEvent; switch (eventType) { case "createEvent": processedEvent = events.toCreateEvent(event); callback(processedEvent, slot, signature); break; case "tradeEvent": processedEvent = events.toTradeEvent(event); callback(processedEvent, slot, signature); break; case "completeEvent": processedEvent = events.toCompleteEvent(event); callback(processedEvent, slot, signature); break; case "setGlobalCfgEvent": processedEvent = events.toSetGlobalCfgEvent(event); callback(processedEvent, slot, signature); break; default: console.error(`Unhandled event type: ${eventType}`); break; } }); } removeEventListener(eventId) { this.program.removeEventListener(eventId); } } exports.BONDING_CURVE_SEED = BONDING_CURVE_SEED; exports.DEFAULT_DECIMALS = DEFAULT_DECIMALS; exports.GLOBAL_ACCOUNT_SEED = GLOBAL_ACCOUNT_SEED; exports.HappyPumpSDK = HappyPumpSDK; exports.METADATA_SEED = METADATA_SEED; exports.MINT_AUTHORITY_SEED = MINT_AUTHORITY_SEED; //# sourceMappingURL=happy_pump.cjs.map