閱讀時間 14 分鐘
本指南已更新,以反映Solana Web3.js 2.0的新名稱——Solana Kit。我們遵循最新的最佳實務,協助您掌握最新動態。點此進一步了解 Solana Kit。
概覽
Pump.fun是一個基於 Solana 的無許可代幣發行與交易平台,採用公平發行(fair-launch)概念(例如:不進行預售,亦無團隊配額)。在本指南中,您將學習如何使用 Solana Kit 與MetisPump.fun API 進行互動,以程式化方式擷取報價並執行交換。我們將建立一個 TypeScript 實作,用以處理 API 請求、交易簽名,以及將交易發送至 Solana 網路。
本指南將引導您了解如何在 Solana Kit 中使用 Metis Pump.fun API。
如果您偏好使用 Solana Web3.js Legacy(v1.x)進行開發,請參閱我們在GitHub 上的範例程式碼。
您將負責的工作內容
- 為 Pump.fun API 請求建立自訂 RPC 傳輸層
- 實作報價擷取與掉期交易執行
- 使用 Solana Kit 處理交易簽署與傳送
- 使用範例腳本測試實作
您需要準備的物品
- 一個已啟用Metis Jupiter Swap API附加功能的Quicknode 帳戶(您可使用我們在JupiterAPI.com 上的公開端點,但該公開端點僅支援特定方法,且速率限制與交易手續費可能較為嚴格)。
- Node.js(建議使用 21.0 版或更高版本)
- 對 TypeScript 及 Solana 開發概念有基本了解
- 建議具備Solana Kit的使用經驗
Metis 概覽
Metis是一款強大的工具,可協助開發者存取 Solana 上的流動性。透過整合 Jupiter 的 V6 Swap API、Jupiter 的限價單 API、Pump.fun 交易功能、交易 WebSocket 等功能,您將能取得所需工具,藉此建構能存取 Solana 平台上眾多 DeFi 協議的強大交易工具。
開始使用
1. 設定您的專案
首先,建立一個新的專案目錄:
mkdir pumpfun-integration && cd pumpfun-integration
初始化您的 Node.js 專案:
npm init -y
安裝所需的依賴項:
npm install @solana/kit dotenv
如果你尚未在全球範圍內安裝 TypeScript,可以將其作為開發依賴項新增至專案中:
npm install --save-dev typescript ts-node @types/node
初始化 TypeScript 設定檔 (tsconfig.json):
tsc --init
建立本專案將使用的檔案:
echo > index.ts && echo > types.ts && echo > .env
更新 package.json 用於執行我們的 TypeScript 檔案的腳本:
"scripts": {
"start": "ts-node index.ts",
}
2. 建立類型定義
如果您之前曾使用過 Solana Kit,您應該知道,在專案的前端進行一些簡單的類型定義,就能為您節省大量時間和精力。我們已為您預先建立了 Pump.fun API 的類型定義。您可以在此處找到完整的類型定義。
請將檔案中的全部內容複製,並貼到您的 types.ts 檔案。這些類型定義了 Pump.fun API 的請求與回應結構,相關內容可參閱我們的 Metis 文件.
3. 設定環境變數
要使用本指南,您需要一個具備 Solana 端點且已啟用 Metis API 的 Quicknode 帳戶。您可在此處註冊 Quicknode 帳戶。
請開啟您的 .env 並加入您的錢包私鑰(可透過執行 solana-keygen new (若您尚未擁有),您的 Metis 端點,以及您的 Solana 主網 HTTP 與 WSS URL:
WALLET_SECRET_KEY=[1, 1, 1, 1, 1, 1 ...等]
METIS_URL='https://jupiter-swap-api.quiknode.pro/YOUR_ENDPOINT'
HTTP_ENDPOINT='https://example.solana-mainnet.quiknode.pro/123456/'
WSS_ENDPOINT='wss://example.solana-mainnet.quiknode.pro/123456/'
請務必將您的 HTTP 和 WSS 端點替換為 Quicknode 儀表板中的 Solana 端點,並將 https://jupiter-swap-api.quiknode.pro/YOUR_ENDPOINT 搭配我們自己的 Metis 端點。您可以在 Quicknode 儀表板的附加元件頁面中找到您的 Metis 地址(https://dashboard.quicknode.com/endpoints/YOUR_ENDPOINT/add-ons):


JupiterAPI.com 提供 Metis API 的公開端點。此公開端點僅支援 Pump.fun 的以下方法: /pump-fun/swap,且使用費用包含一筆兌換手續費。請查看 JupiterAPI.com 以了解最新的費用及限額。
實施
現在,讓我們建立實作程式與測試腳本,用以與 Pump.fun API 進行互動。請開啟 index.ts,現在讓我們一步一步地實作每個部分。
1. 匯入依賴項並定義類型
首先,導入所需的依賴項,並為 Metis PumpFun API 定義類型:
import {
Rpc,
createRpc,
RpcTransport,
createJsonRpcApi,
address,
getBase64Encoder,
FullySignedTransaction,
TransactionMessageBytes,
getTransactionDecoder,
signTransaction,
createKeyPairFromBytes,
TransactionWithBlockhashLifetime,
getSignatureFromTransaction,
createSolanaRpcSubscriptions,
sendAndConfirmTransactionFactory,
createSolanaRpc,
SolanaRpcApi,
RpcSubscriptions,
SolanaRpcSubscriptionsApi,
getAddressDecoder,
getAddressFromPublicKey
} from "@solana/kit";
import {
HttpRequestMethod,
PumpFunEndpoint,
PumpFunQuoteParams,
PumpFunQuoteResponse,
PumpFunRequest,
PumpFunSwapInstructionsResponse,
PumpFunSwapParams,
PumpFunSwapResponse,
SignAndSendTransactionParams
} from "./types";
import dotenv from 'dotenv';
dotenv.config();
type MetisPumpFunApi = {
pumpfun_quote(params: PumpFunQuoteParams): Promise<PumpFunQuoteResponse>;
pumpfun_swap(params: PumpFunSwapParams): Promise<PumpFunSwapResponse>;
pumpfun_swap_instructions(params: PumpFunSwapParams): Promise<PumpFunSwapInstructionsResponse>;
}
const METHOD_TO_ENDPOINT: Record<string, PumpFunEndpoint> = {
pumpfun_quote: {
path: 'pump-fun/quote',
method: 'GET'
},
pumpfun_swap: {
path: 'pump-fun/swap',
method: 'POST'
},
pumpfun_swap_instructions: {
path: 'pump-fun/swap-instructions',
method: 'POST'
}
};
const PUBLIC_ENDPOINT_URL = 'https://public.jupiterapi.com';
除了在上一步驟中定義的類型之外,我們還導入了必要的 Solana Kit 依賴項,並定義了 MetisPumpFunApi 類型,代表我們即將實作的 Pump.fun API 方法。這是本指南中將使用的 Metis API 的子集。若您對此感到陌生或不熟悉,建議您參閱我們的指南: 使用 Solana Kit 建立自訂 API. 我們同時正在建立方法名稱與 API 端點之間的對應關係,以便在傳輸層中使用,確保能正確處理每個方法。
2. 實作 API 設定
接下來,讓我們新增幾個輔助函式,用來設定 PumpFun API 並處理請求。請將以下程式碼新增至您的 index.ts:
function createPumpFunUrl(metisEndpoint: string, method: string): URL {
const baseUrl = metisEndpoint.replace(/\/$/, ''); // Remove trailing slash if present
const endpointPath = METHOD_TO_ENDPOINT[method].path;
return new URL(`${baseUrl}/${endpointPath}`);
}
function createPumpFunTransport(metisEndpoint: string): RpcTransport {
return async <TResponse>(...args: Parameters<RpcTransport>): Promise<TResponse> => {
const { method, params } = args[0].payload as { method: string; params: PumpFunRequest };
const url = createPumpFunUrl(metisEndpoint, method);
const normalizedParams = Array.isArray(params) ? params[0] : params;
switch (METHOD_TO_ENDPOINT[method].method) {
case 'GET':
return handlePumpFunGET<PumpFunRequest, TResponse>(url, normalizedParams);
case 'POST':
return handlePumpFunPOST<PumpFunRequest, TResponse>(url, normalizedParams);
default:
throw new Error(`Unknown HTTP method for PumpFun method: ${method}`);
}
};
}
function createPumpFunApi(metisEndpoint: string): Rpc<MetisPumpFunApi> {
const api = createJsonRpcApi<MetisPumpFunApi>();
const transport = createPumpFunTransport(metisEndpoint);
return createRpc({ api, transport });
}
以下是各函式功能的詳細說明:
createPumpFunUrl: 根據 Metis 端點和方法名稱,為 Pump.fun API 端點建立一個 URL 物件(並移除任何尾隨斜線)。建立泵送功能傳輸: 建立一個自訂的 RPC 傳輸函式,用以驗證我們的端點/方法、將參數標準化,並根據 HTTP 方法(GET/POST)將請求路由至適當的處理程序——我們將在接下來定義這些處理程序。建立 PumpFun API: 建立一個使用自訂傳輸函式的 PumpFun API RPC 實例。
3. 實作請求處理程式
現在,讓我們來實作核心的請求處理功能。GET 方法將利用 URL 查詢參數來傳遞請求參數,而 POST 方法則會將參數包含在請求正文中。請將以下程式碼加入您的 index.ts:
async function handlePumpFunGET<TParams, TResponse>(
url: URL,
params: TParams
): Promise<TResponse> {
if (typeof params === 'object' && params !== null) {
Object.entries(params as Record<string, unknown>).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
}
const response = await fetch(url.toString(), {
method: 'GET',
redirect: 'follow',
headers: {
'Content-Type': 'application/json',
}
});
if (!response.ok) {
throw new Error(`Error making GET request to ${url}: ${response.statusText}`);
}
return await response.json() as TResponse;
}
async function handlePumpFunPOST<TParams, TResponse>(
url: URL,
params: TParams
): Promise<TResponse> {
try {
const response = await fetch(url.toString(), {
method: 'POST',
redirect: 'follow',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
});
if (!response.ok) {
throw new Error(`Error making POST request to ${url}: ${response.statusText}`);
}
return await response.json() as TResponse;
} catch (error) {
console.error('Error making POST request:', error);
throw error;
}
}
以下是各函式的功能說明:
handlePumpFunGET: 將請求參數附加至 URL 的搜尋參數中,並向 Pump.fun API 端點發送 GET 請求。此操作會回傳 JSON 回應。handlePumpFunPOST: 向 Pump.fun API 端點發送 POST 請求,並在請求正文中包含請求參數。此操作會傳回 JSON 回應。
4. 實作交易處理
現在讓我們新增交易簽署與傳送功能:
async function signAndSendTransaction({
transactionBase64,
signerSecretKey,
solanaRpc,
solanaRpcSubscriptions,
commitment = 'confirmed'
}: SignAndSendTransactionParams): Promise<string> {
// Create signer keypair from secret
const signerKeypair = await createKeyPairFromBytes(
new Uint8Array(signerSecretKey)
);
// Decode the base64 transaction
const transactionBytes = getBase64Encoder().encode(transactionBase64) as TransactionMessageBytes;
const transactionDecoder = getTransactionDecoder();
const decodedTransaction = transactionDecoder.decode(transactionBytes);
// Sign the transaction
const signedTransaction = await signTransaction(
[signerKeypair],
decodedTransaction
);
// Get latest blockhash and prepare transaction with lifetime
const { value: { lastValidBlockHeight, blockhash } } = await solanaRpc.getLatestBlockhash().send();
const signedTransactionWithLifetime: FullySignedTransaction & TransactionWithBlockhashLifetime = {
...signedTransaction,
lifetimeConstraint: {
blockhash,
lastValidBlockHeight,
},
};
// Get transaction signature
const transactionSignature = getSignatureFromTransaction(signedTransactionWithLifetime);
// Create sendAndConfirm function
const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({
rpc: solanaRpc,
rpcSubscriptions: solanaRpcSubscriptions,
});
// Send and confirm transaction
await sendAndConfirmTransaction(signedTransactionWithLifetime, {
commitment,
});
return transactionSignature;
}
此函式將使執行我們以 Base64 編碼的交易變得輕而易舉。該 signAndSendTransaction 該函式負責處理整個交易生命週期:
- 根據提供的私密金鑰建立一組金鑰對
- 解碼以 Base64 編碼的交易
- 簽署該交易
- 取得最新的區塊雜湊值
- 為交易新增有效期限制,以確保其「最新」
- 使用 Solana Kit 的
sendAndConfirmTransaction工廠將發送並確認該筆交易 - 返回用於追蹤的交易簽名
5. ENV 驗證
最後,讓我們寫一個簡單的環境變數驗證函式,以確保所有必填變數都已設定:
function validateEnv() {
const envVars = ['WALLET_SECRET_KEY','HTTP_ENDPOINT','WSS_ENDPOINT']; // 'METIS_URL' validation is not required because we have a public endpoint fallback
envVars.forEach((envVar) => {
if (!process.env[envVar]) {
throw new Error(`${envVar} environment variable is required`);
}
});
}
我們只是檢查每個預期的環境變數,若未設定則拋出錯誤。提醒:切勿在客戶端程式碼中公開您的 Quicknode API 金鑰。請務必將其安全地存放在後端。如需更多關於端點安全的資訊,請參閱我們的指南:《如何保護您的端點——前端最佳實務》。
測試腳本
取得報價
讓我們建立一個新函式, 主頁, 該程式將用來測試我們的 API。 注意:若您使用的是公開端點,此方法將無法生效。 請將以下程式碼加入您的 index.ts:
async function main() {
validateEnv();
const metisUrl = process.env.METIS_URL ?? PUBLIC_ENDPOINT_URL;
const rpcUrl = process.env.HTTP_ENDPOINT as string;
const rpcSubscriptionsUrl = process.env.WSS_ENDPOINT as string;
const signerSecretKey = JSON.parse(process.env.WALLET_SECRET_KEY as string) as number[];
const signerKeypair = await createKeyPairFromBytes(new Uint8Array(signerSecretKey));
const wallet = await getAddressFromPublicKey(signerKeypair.publicKey);
const targetMint = address("8gXN67Nmw9FZQjunJZzRoi2Qf1ykZtN9Q3BqxhCypump");
const pumpFunApi = createPumpFunApi(metisUrl);
const solanaRpc: Rpc<SolanaRpcApi> = createSolanaRpc(rpcUrl);
const solanaRpcSubscriptions: RpcSubscriptions<SolanaRpcSubscriptionsApi>
= createSolanaRpcSubscriptions(rpcSubscriptionsUrl);
try {
const pumpFunQuote = await pumpFunApi.pumpfun_quote({
type: 'BUY',
mint: targetMint,
amount: 1_000_000,
}).send();
console.log(`PumpFun Quote:\n ${JSON.stringify(pumpFunQuote.quote, null, 2)}`);
} catch (error) {
console.error('Error getting PumpFun quote:', error);
}
}
main().catch(console.error);
在此,我們正在宣告環境變數、建立 PumpFun API 實例,並從 PumpFun API 擷取一句名言。我們使用的是 pumpfun_quote 用於查詢購買 0.001 SOL 指定代幣的報價的方法。您可以根據需求調整相關參數。
請在終端機中執行以下指令以啟動該腳本:
npm start
如果一切設定正確,您應該會在控制台看到類似以下的報價回應記錄:
PumpFun Quote:
{
"mint": "8gXN67Nmw9FZQjunJZzRoi2Qf1ykZtN9Q3BqxhCypump",
"bondingCurve": "6TWadAgufwkQqZfKm1fqLgSQBUGKoMup7x5uC8fHUtnZ",
"type": "BUY",
"inAmount": "1000000",
"inAmountUi": 0.001,
"inTokenAddress": "So11111111111111111111111111111111111111112",
"outAmount": "33339932484",
"outAmountUi": 33339.932484,
"outTokenAddress": "8gXN67Nmw9FZQjunJZzRoi2Qf1ykZtN9Q3BqxhCypump",
"meta": {
"isCompleted": false,
"outDecimals": 6,
"inDecimals": 9,
"totalSupply": "1000000000000000",
"currentMarketCapInSol": 29.993096666
}
}
做得真好!
執行掉期交易
如果您已準備好開始交易,可以透過在您的程式中加入以下程式碼來建立一筆交易,並將其發送至網路: 主頁 函式`:`
try {
const pumpFunQuote = await pumpFunApi.pumpfun_swap({
wallet,
type: 'BUY',
mint: targetMint,
inAmount: 1_000_000,
priorityFeeLevel: 'high', // optionally set priority fee level
}).send();
const sig = await signAndSendTransaction({
transactionBase64: pumpFunQuote.tx,
signerSecretKey: JSON.parse(process.env.WALLET_SECRET_KEY as string) as number[],
solanaRpc,
solanaRpcSubscriptions,
});
console.log(`Transaction Signature: ${sig}`);
} catch (error) {
console.error('Error getting PumpFun quote:', error);
}
警告:此程式碼將在 Solana 網路上執行真實交易,並導致資金不可逆轉地轉移。執行前請務必了解其影響。
結論
現在,您已擁有一個透過 Solana Kit 與 Metis Pump.fun API 進行互動的穩健實作方案。此實作方案提供了一種類型安全且抗錯誤的方式,讓您能在 Pump.fun 平台上擷取報價並執行交換交易。若您想繼續開發,請參閱我們的指南:《如何在 Solana 上建置 Jupiter 交易機器人》,以獲取更多將此專案提升至更高層次的靈感。
請務必在部署至生產環境之前,先於開發環境中進行徹底的測試,並確保已採取適當的安全措施來處理私鑰及 Quicknode 憑證。
若想進一步了解 Solana 開發相關資訊,請參閱我們在Quicknode Guides 上發布的其他指南。如需支援或有任何疑問,歡迎加入我們的Discord 社群,或透過Twitter 與我們聯繫。
我們 ❤️ 您的回饋!
如果您有任何意見回饋或對新主題的建議,請告訴我們。我們非常樂意聽取您的意見。
