@solsdk/tokenflow_sdk
Version:
A simple SDK for interacting with tokenflow
282 lines (278 loc) • 14.2 kB
JavaScript
;
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