Solana 交易是以原子方式執行的指令列表。了解交易結構——指令、帳戶、簽署者、計算預算——是使用 Raydium 進行任何開發、除錯或最佳化的前提條件。本頁涵蓋了交易結構、限制條件,以及兩個手續費類別(Solana 網路手續費、Raydium 協議手續費)如何在實際交換中堆疊。
交易解剖
Solana 交易有三個核心元件:
- 訊息:指令的有序列表、它們參考的帳戶,以及最近的區塊雜湊。
- 簽名:每個簽署者一個,認證交易獲得授權。
- 最近的區塊雜湊:證明交易是最近的;具有陳舊區塊雜湊(>150 個槽位舊)的交易會被拒絕。
一個指令指定:
program_id — 要呼叫的程式。
accounts — 程式可能觸及的帳戶(及其可寫入/簽署者旗標)。
data — 程式解釋的不透明位元組。
單一交易可包含多個指令。它們按順序執行;如果任何指令失敗,所有先前的指令都會被回滾(原子性)。
典型的 Raydium 交換交易包括:
ComputeBudget::SetComputeUnitLimit — 提高預設 CU 限額。
ComputeBudget::SetComputeUnitPrice — 設定優先費用。
- 可選的
CreateAssociatedTokenAccount — 如果使用者沒有輸出 ATA,建立它。
Raydium::SwapBaseInput — 執行交換。
- 可選的
CloseAccount — 關閉 wrapped SOL ATA。
SDK 透過 raydium.trade.swap() 自動打包這些指令。
交易中的帳戶
交易中任何指令觸及的每個帳戶都必須列在交易的帳戶鑰匙中。每個帳戶都有旗標:
- 簽署者 / 非簽署者:帳戶的擁有者必須簽署交易嗎?
- 可寫入 / 唯讀:交易可以修改帳戶嗎?
執行時強制執行這些旗標:程式嘗試寫入不可寫入帳戶會失敗,執行時會拒絕缺少必需簽署者的交易。
對於 CPMM 交換,帳戶列表有約 13 個條目(參閱 solana-fundamentals/account-model)。具有多個刻度陣列交叉的 CLMM 交換可能有 20+。
交易大小限制
Solana 將交易上限設為 1232 位元組,包括簽名、訊息和標頭。這是複雜交易最常見的障礙——Raydium 的 CLMM 與多路由路由經常推著這個限制。
典型 ~1000 位元組 Raydium 交換的分解:
| 元件 | 大小 |
|---|
| 簽名 | 64 B |
| 簽名計數 | 1 B |
| 訊息標頭 | 3 B |
| 區塊雜湊 | 32 B |
| 帳戶鑰匙 (13 × 32 B) | 416 B |
| 指令 (4 × ~100-150 B) | 400–600 B |
| 總計 | ~900–1100 B |
位址查閱表 (ALT)
ALT 讓交易透過已發佈表中的 1 位元組索引參考帳戶,而不是完整的 32 位元組公鑰。這大幅壓縮交易:
- 直接參考 20 個帳戶的交易:~640 B 的公鑰。
- 同一筆交易使用 ALT:~20 B 的索引 + ALT 參考。
Raydium 在主網上為 CPMM/CLMM 交換路徑維護 ALT。SDK 自動使用它們。構建多路由的聚合商大量依賴它們。
import { VersionedTransaction } from "@solana/web3.js";
// SDK builds a v0 (versioned) tx with ALT references
const { transaction } = await raydium.trade.swap({ /* ... */ });
// transaction is a VersionedTransaction, not a legacy Transaction.
計算預算
每筆交易都有一個計算單位 (CU) 預算。超過預算會終止執行並使交易失敗。
- 預設值:每筆交易 200,000 CU。
- 最大值:每筆交易 1,400,000 CU(透過
ComputeBudget::SetComputeUnitLimit 提高)。
- 每區塊上限:每區塊 48M CU(協議層級)。
典型 Raydium CU 消耗(完整表格參閱 integration-guides/priority-fee-tuning):
| 指令 | CU |
|---|
| CPMM 交換 | ~140,000 |
| CLMM 交換(無刻度交叉) | ~170,000 |
| CLMM 交換(4 次刻度交叉) | ~320,000 |
| Farm v6 質押 | ~130,000 |
| CPMM 池建立 | ~250,000 |
總是透過 ComputeBudget 設定明確的 CU 限額;否則你會得到 200k 預設值,對大多數 Raydium 指令來說太低了。
import { ComputeBudgetProgram } from "@solana/web3.js";
tx.add(
ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 }),
);
如果你設定的 CU 限額太低,交易在達到上限時會失敗;設定過高你會冒著在壅塞下被降級優先度的風險(根據定價模式,你可能為從未使用的計算付費)。
優先費用
除了基礎交易費用(每個簽名 5000 lamports),驗證者越來越多地優先處理支付優先費用的交易:每 CU 的微 lamport 小費。
priority_fee = compute_unit_price (micro-lamports) × compute_unit_limit
例子:10,000 µL/CU × 300,000 CU = 3,000,000 µL = 0.003 SOL。
優先費用是本地的——它們只影響區塊內的排序;它們不會改善你被納入而非不被納入的機會。在壅塞期間設定合理的優先費用至關重要。
tx.add(
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 10_000 }),
);
參閱 integration-guides/priority-fee-tuning 了解如何動態調整這一點。
指令計數與帳戶計數限制
除了 1232 位元組的總限制之外:
- 每筆交易的最大帳戶數:128。
- 每個指令的最大帳戶數 (CPI):64。
- 每筆交易的最大指令數:無硬性限制,僅受大小限制限制。
- 最大 CPI 深度:4(一個程式可以呼叫另一個程式,它可以呼叫另一個程式,4 層深)。
交叉多個刻度陣列的 Raydium CLMM 交換可能會嚴重推著帳戶限制——單次交換觸及池、輸入/輸出金庫、輸入/輸出 ATA、多個刻度陣列、可能是傳輸鉤子程式的額外帳戶,加上強制計算預算 / 系統 / token 程式參考。透過 CPI 組成 Raydium 的設計(例如自動複合器)需要考慮到這一點。
Raydium 交換中的手續費類別
使用者交換交易在兩個類別中支付手續費:
Solana 網路手續費
支付給驗證者的 SOL。
這些費用進入驗證者,與 Raydium 無關,甚至對失敗的交易也會收取(除了某些優先費用邊界情況)。
Raydium 協議手續費
從交換金額中扣除。
這些費用是 Raydium 帳務內部的——使用者看到的是比零費用池產生的輸出量更小的金額。
範例:$1000 USDC → SOL 透過 CPMM 0.25% 層
| 手續費類別 | 金額 | 流向 |
|---|
| 基礎簽名費 | 0.000005 SOL (~$0.0007) | 驗證者 |
| 優先費用 (10k µL × 300k CU) | 0.003 SOL (~$0.45) | 驗證者 |
| CPMM 交換費 (0.25%) | $2.50 | LP + 協議 |
| 使用者總成本 | ~$2.95 | |
滑點(價格影響 + 市場波動)不是手續費,但會影響相同的最終結果。
版本化交易
Solana 有兩種交易格式:
- 遺留:原始格式,不支援 ALT。
- v0 (版本化):支援 ALT,可擴展至未來版本。
所有現代 Solana 工具都使用 v0。Raydium SDK 預設發出 v0 交易。
// Building a v0 tx directly
import { VersionedTransaction, TransactionMessage } from "@solana/web3.js";
const msg = new TransactionMessage({
payerKey: owner.publicKey,
recentBlockhash: blockhash,
instructions: [ /* ... */ ],
}).compileToV0Message([lookupTableAccount]);
const tx = new VersionedTransaction(msg);
tx.sign([owner]);
區塊雜湊新鮮度
交易必須包含來自最後約 150 個槽位(約 60 秒)內的區塊雜湊。超出該視窗,驗證者會拒絕它。
對於重試迴圈,在每次重試時取得新的區塊雜湊:
async function sendWithRetry(tx, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash();
tx.message.recentBlockhash = blockhash;
tx.sign([owner]);
try {
return await connection.sendRawTransaction(tx.serialize());
} catch (e) {
if (i === maxRetries - 1) throw e;
}
}
}
參閱 integration-guides/priority-fee-tuning 了解完整的逐步升級費用重試模式。
平行執行
Solana 在多核驗證者上平行執行不衝突的交易。兩筆交易衝突當且僅當它們都寫同一個帳戶。
對 Raydium 的影響:
- 同一池上的兩筆交換無法平行執行——兩者都寫池狀態。
- 池 A 上的交換和池 B 上的交換在帳戶列表不重疊時平行執行。
- 唯讀交易從不阻止同一帳戶的寫入者(唯讀與自己併發,但與寫入不併發)。
這就是為什麼 Solana 儘管有單池序列化,仍能維持高 DEX 吞吐量。
交易確認層級
提交交易時,你選擇一個確認層級:
| 層級 | 等待 | 最終性 |
|---|
processed | ~400 ms | 未最終確認;可能回滾 |
confirmed | ~1 s | 超級多數投票 |
finalized | ~13 s | 超級多數根源化 |
對於交換 UX,confirmed 是標準。對於處理大價值的操作(池建立、獎勵補充),finalized 更安全。
await connection.sendAndConfirmTransaction(tx, [owner], {
commitment: "confirmed",
});
Solana 支援在提交前模擬交易:
const sim = await connection.simulateTransaction(tx);
console.log(sim.value.logs);
console.log(sim.value.unitsConsumed);
Raydium SDK 在計算 getBestSwapInfo 時內部使用模擬來驗證選定的路由確實成功。模擬並非免費的——它消耗 RPC 容量——但它在支付前捕捉錯誤。
來源: