191 lines
4.9 KiB
JavaScript
191 lines
4.9 KiB
JavaScript
![]() |
// src/tools/launch_pumpfun_token.ts
|
||
|
import { Keypair, VersionedTransaction } from "@solana/web3.js"
|
||
|
|
||
|
async function uploadMetadata(
|
||
|
tokenName,
|
||
|
tokenTicker,
|
||
|
description,
|
||
|
imageUrl,
|
||
|
options
|
||
|
) {
|
||
|
// Create metadata object
|
||
|
const formData = new URLSearchParams()
|
||
|
formData.append("name", tokenName)
|
||
|
formData.append("symbol", tokenTicker)
|
||
|
formData.append("description", description)
|
||
|
|
||
|
formData.append("showName", "true")
|
||
|
|
||
|
if (options?.twitter) {
|
||
|
formData.append("twitter", options.twitter)
|
||
|
}
|
||
|
if (options?.telegram) {
|
||
|
formData.append("telegram", options.telegram)
|
||
|
}
|
||
|
if (options?.website) {
|
||
|
formData.append("website", options.website)
|
||
|
}
|
||
|
|
||
|
const imageResponse = await fetch(imageUrl)
|
||
|
const imageBlob = await imageResponse.blob()
|
||
|
const files = {
|
||
|
file: new File([imageBlob], "token_image.png", { type: "image/png" })
|
||
|
}
|
||
|
|
||
|
// Create form data with both metadata and file
|
||
|
const finalFormData = new FormData()
|
||
|
// Add all metadata fields
|
||
|
for (const [key, value] of formData.entries()) {
|
||
|
finalFormData.append(key, value)
|
||
|
}
|
||
|
// Add file if exists
|
||
|
if (files?.file) {
|
||
|
finalFormData.append("file", files.file)
|
||
|
}
|
||
|
|
||
|
const metadataResponse = await fetch("https://pump.fun/api/ipfs", {
|
||
|
method: "POST",
|
||
|
body: finalFormData
|
||
|
})
|
||
|
|
||
|
if (!metadataResponse.ok) {
|
||
|
throw new Error(`Metadata upload failed: ${metadataResponse.statusText}`)
|
||
|
}
|
||
|
|
||
|
return await metadataResponse.json()
|
||
|
}
|
||
|
|
||
|
async function createTokenTransaction(
|
||
|
agent,
|
||
|
mintKeypair,
|
||
|
metadataResponse,
|
||
|
options
|
||
|
) {
|
||
|
const payload = {
|
||
|
publicKey: agent.wallet_address.toBase58(),
|
||
|
action: "create",
|
||
|
tokenMetadata: {
|
||
|
name: metadataResponse.metadata.name,
|
||
|
symbol: metadataResponse.metadata.symbol,
|
||
|
uri: metadataResponse.metadataUri
|
||
|
},
|
||
|
mint: mintKeypair.publicKey.toBase58(),
|
||
|
denominatedInSol: "true", // API expects string "true"
|
||
|
amount: options?.initialLiquiditySOL || 0.0001,
|
||
|
slippage: options?.slippageBps || 5,
|
||
|
priorityFee: options?.priorityFee || 0.00005,
|
||
|
pool: "pump"
|
||
|
}
|
||
|
|
||
|
const response = await fetch("https://pumpportal.fun/api/trade-local", {
|
||
|
method: "POST",
|
||
|
headers: {
|
||
|
"Content-Type": "application/json"
|
||
|
},
|
||
|
body: JSON.stringify(payload)
|
||
|
})
|
||
|
|
||
|
if (!response.ok) {
|
||
|
const errorText = await response.text()
|
||
|
throw new Error(
|
||
|
`Transaction creation failed: ${response.status} - ${errorText}`
|
||
|
)
|
||
|
}
|
||
|
|
||
|
return response
|
||
|
}
|
||
|
|
||
|
async function signAndSendTransaction(kit, tx, mintKeypair) {
|
||
|
try {
|
||
|
// Get the latest blockhash
|
||
|
const {
|
||
|
blockhash,
|
||
|
lastValidBlockHeight
|
||
|
} = await kit.connection.getLatestBlockhash()
|
||
|
|
||
|
// Update transaction with latest blockhash
|
||
|
tx.message.recentBlockhash = blockhash
|
||
|
|
||
|
// Sign the transaction
|
||
|
const signedTx = await kit.wallet.signTransaction(tx)
|
||
|
signedTx.sign([mintKeypair])
|
||
|
|
||
|
// Send and confirm transaction with options
|
||
|
const signature = await kit.connection.sendTransaction(signedTx, {
|
||
|
skipPreflight: false,
|
||
|
preflightCommitment: "confirmed",
|
||
|
maxRetries: 5
|
||
|
})
|
||
|
|
||
|
// Wait for confirmation
|
||
|
const confirmation = await kit.connection.confirmTransaction({
|
||
|
signature,
|
||
|
blockhash,
|
||
|
lastValidBlockHeight
|
||
|
})
|
||
|
|
||
|
if (confirmation.value.err) {
|
||
|
throw new Error(`Transaction failed: ${confirmation.value.err}`)
|
||
|
}
|
||
|
|
||
|
return signature
|
||
|
} catch (error) {
|
||
|
console.error("Transaction send error:", error)
|
||
|
if (error instanceof Error && "logs" in error) {
|
||
|
console.error("Transaction logs:", error.logs)
|
||
|
}
|
||
|
throw error
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Launch a token on Pump.fun
|
||
|
* @param agent - SolanaAgentKit instance
|
||
|
* @param tokenName - Name of the token
|
||
|
* @param tokenTicker - Ticker of the token
|
||
|
* @param description - Description of the token
|
||
|
* @param imageUrl - URL of the token image
|
||
|
* @param options - Optional token options (twitter, telegram, website, initialLiquiditySOL, slippageBps, priorityFee)
|
||
|
* @returns - Signature of the transaction, mint address and metadata URI, if successful, else error
|
||
|
*/
|
||
|
export async function launchPumpFunToken(
|
||
|
agent,
|
||
|
tokenName,
|
||
|
tokenTicker,
|
||
|
description,
|
||
|
imageUrl,
|
||
|
options
|
||
|
) {
|
||
|
try {
|
||
|
const mintKeypair = Keypair.generate()
|
||
|
const metadataResponse = await uploadMetadata(
|
||
|
tokenName,
|
||
|
tokenTicker,
|
||
|
description,
|
||
|
imageUrl,
|
||
|
options
|
||
|
)
|
||
|
const response = await createTokenTransaction(
|
||
|
agent,
|
||
|
mintKeypair,
|
||
|
metadataResponse,
|
||
|
options
|
||
|
)
|
||
|
const transactionData = await response.arrayBuffer()
|
||
|
const tx = VersionedTransaction.deserialize(new Uint8Array(transactionData))
|
||
|
const signature = await signAndSendTransaction(agent, tx, mintKeypair)
|
||
|
|
||
|
return {
|
||
|
signature,
|
||
|
mint: mintKeypair.publicKey.toBase58(),
|
||
|
metadataUri: metadataResponse.metadataUri
|
||
|
}
|
||
|
} catch (error) {
|
||
|
console.error("Error in launchpumpfuntoken:", error)
|
||
|
if (error instanceof Error && "logs" in error) {
|
||
|
console.error("Transaction logs:", error.logs)
|
||
|
}
|
||
|
throw error
|
||
|
}
|
||
|
}
|