Jede Solana-Transaktion setzt zwei Parameter: ein Compute Unit Limit (maximale CUs, die die Transaktion verbrauchen darf; standardmäßig 200.000 × Anzahl der Instructions bis zu einer Obergrenze pro Transaktion) und eine Priority Fee in Mikro-Lamports pro CU. Unzureichende Werte für beide Parameter führen zum Scheitern — zu niedrige CU-Limits verursachen ProgramFailedToComplete; zu niedrige Priority Fees führen dazu, dass die Transaktion bis zum Ablauf unbestätigt bleibt.
Die zwei Einstellungen
import { ComputeBudgetProgram } from "@solana/web3.js";
const tx = new Transaction()
.add(ComputeBudgetProgram.setComputeUnitLimit({ units: 250_000 }))
.add(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 50_000 }))
.add(yourRaydiumSwapIx);
setComputeUnitLimit(units) — beschränkt die Berechnung; die Transaktion bezahlt für höchstens units CUs.
setComputeUnitPrice(microLamports) — Priority-Fee-Gebot in Mikro-Lamports pro CU. Gesamte Priority Fee = units × microLamports × 1e-6 Lamports.
Kostenberechnung: Ein 250k-CU-Limit mit 50k Mikro-Lamports/CU bietet 250_000 × 50_000 / 1e6 = 12.500 Lamports ≈ 0,0000125 SOL ≈ 0,003 USD bei 200 USD SOL. Priority Fees in dieser Größenordnung sind für die meisten Benutzer-Swaps unbedeutend, aber erheblich für Bots, die täglich 1000 Transaktionen durchführen.
CU-Benchmarks pro Instruction
Benchmarks aus Mainnet-Ausführungsprotokollen, gemittelt über neuere Durchläufe. Die Zahlen sind ungefähr (±15%); nehmen Sie für Ihre spezifischen Abläufe eine Neubewertung vor.
| Instruction | SPL Token | Token-2022 (einfach) | Token-2022 (Transfer Fee) |
|---|
| CPMM initialize_pool | 180.000 | 200.000 | — |
| CPMM swap_base_input | 140.000 | 180.000 | 200.000 |
| CPMM swap_base_output | 150.000 | 185.000 | 205.000 |
| CPMM deposit | 130.000 | 160.000 | 180.000 |
| CPMM withdraw | 120.000 | 150.000 | 170.000 |
| CLMM create_pool | 70.000 | 85.000 | — |
| CLMM open_position_v2 | 120.000 | 140.000 | 160.000 |
| CLMM increase_liquidity_v2 | 150.000 | 175.000 | 195.000 |
| CLMM decrease_liquidity_v2 | 140.000 | 165.000 | 185.000 |
| CLMM swap_v2 (0 Tick-Überquerungen) | 170.000 | 205.000 | 225.000 |
| CLMM swap_v2 (1 Tick-Überquerung) | 220.000 | 255.000 | 275.000 |
| CLMM swap_v2 (3 Tick-Überquerungen) | 320.000 | 355.000 | 375.000 |
| CLMM collect_fee | 80.000 | 95.000 | 105.000 |
| AMM v4 swap_base_in | 140.000 | — | — |
| AMM v4 deposit | 120.000 | — | — |
| AMM v4 withdraw | 110.000 | — | — |
| Farm v6 create_farm | 70.000 | 85.000 | — |
| Farm v6 deposit (1 Reward Slot) | 130.000 | 155.000 | 175.000 |
| Farm v6 deposit (3 Reward Slots) | 220.000 | 255.000 | 275.000 |
| Farm v6 withdraw | entspricht deposit | | |
| Farm v6 harvest | entspricht deposit | | |
| Farm v3/v5 deposit | 100.000 | — | — |
| LaunchLab initialize | 100.000 | — | — |
| LaunchLab buy_exact_in | 140.000 | — | — |
| LaunchLab graduate | 250.000 | — | — |
Die Zeile „Tick-Überquerungen” für CLMM ist die größte CU-Variable. Wenn Sie nicht wissen, wie viele Ticks der Swap überqueren wird, planen Sie für den schlimmsten Fall ein — 8 Überquerungen ist die Hard-Obergrenze (das Programm lädt höchstens 8 Tick-Arrays).
Zusammengesetzte Transaktionen
Summieren Sie die einzelnen Budgets und addieren Sie:
- +1.500 CU pro CPI-Frame — der feste Overhead der Runtime für jeden Cross-Program-Aufruf.
- +20.000 CU pro ATA-Erstellung —
create_associated_token_account ist nicht kostenlos.
- +5.000 CU für
setComputeUnitLimit / setComputeUnitPrice jeweils.
Beispiel: Ein Benutzer-Swap, der das Ausgabe-ATA erstellt und natives SOL umwandelt:
wrap_sol (create_ata + system transfer + sync_native) ≈ 30.000
CPMM swap_base_input (SPL) ≈ 140.000
close_account (unwrap) ≈ 5.000
ComputeBudget Instructions ≈ 10.000
────────────────────────────────────────────────────────
Gesamt ≈ 185.000 → Budget 250.000
Pufferung: Setzen Sie das CU-Limit etwa 25% über der erwarteten Nutzung. Unterschätzung kostet die gesamte Transaktion; Überschätzung erhöht nur die Priority-Fee-Kosten proportional (Priority Fee ist units × microLamports, daher kostet ~25% über-Budget 25% zusätzliche Priority Fee).
Priority-Fee-Schätzung
Solanas lokaler Gebührenmarkt bedeutet, dass Priority Fees pro beschreibbarem Account berechnet werden. Eine Transaktion, die in einen heißen Account (beliebter Pool-Status) schreibt, zahlt mehr als eine Transaktion, die in einen kalten Account schreibt. Das globale Gebührenniveau ist nicht die richtige Metrik für Raydium-Swaps; Sie wollen Gebühren auf die spezifischen Pools, die Sie berühren.
Strategie 1: RPC-Provider-Schätzer
Jeder große RPC-Provider veröffentlicht einen Priority-Fee-Schätzer, der aktuelle Gebühren auf spezifischen Accounts abfragt:
// Helius
const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${apiKey}`, {
method: "POST",
body: JSON.stringify({
jsonrpc: "2.0",
id: "fee-estimate",
method: "getPriorityFeeEstimate",
params: [{
accountKeys: [poolStatePubkey.toBase58()],
options: { priorityLevel: "High" },
}],
}),
});
const { result } = await response.json();
const microLamports = result.priorityFeeEstimate;
Priority-Level über die meisten Provider hinweg: Min / Low / Medium / High / VeryHigh / UnsafeMax. Ordnen Sie sie den Perzentilen zu:
| Ebene | Perzentil | Anwendungsfall |
|---|
| Min | 25. | Hintergrund, nicht dringende Bot-Transaktionen |
| Low | 50. | Normale Benutzer-Swaps |
| Medium | 60. | Standard für Wallet-UIs |
| High | 75. | Zeitempfindliche Arbitrage |
| VeryHigh | 95. | Liquidationen, Notfall-Exits |
Provider: Helius (getPriorityFeeEstimate), Triton (getRecentPrioritizationFees mit Account-Liste), QuickNode (ähnlich).
Strategie 2: Direkte RPC-Abfrage
Verwenden Sie die Standard-getRecentPrioritizationFees RPC:
const fees = await connection.getRecentPrioritizationFees({
lockedWritableAccounts: [poolStatePubkey],
});
// fees: Array<{ slot, prioritizationFee }>
// Letzte N Slots; standardmäßig ~150 Slots.
const median = percentile(fees.map(f => f.prioritizationFee), 0.5);
Dies ist die Vanilla-Solana-RPC-Methode; funktioniert bei jedem Provider. Nachteil: Das Sample ist klein (150 Slots ≈ 60 Sekunden) und verrauscht. Für glattere Schätzungen verwenden Sie die Aggregation eines Providers.
Strategie 3: Historische Selbstoptimierung
Für Bots mit ständigem Durchsatz verfolgen Sie Ihre eigenen Landungs- gegenüber Ablauf-Raten:
Pro-Pool-Ziel: 80% Landungsrate bei <30s
Wenn current_land_rate < 80%: priorityFee += 10%
Wenn current_land_rate > 95%: priorityFee -= 5%
Dies korrigiert sich schneller als öffentliche Schätzer und erfasst pro-Pool-Struktur, die öffentliche Schätzer nicht immer sehen.
Umgang mit CU-Erschöpfungsfehlern
Symptom: Transaktion schlägt fehl mit exceeded maximum number of instructions allowed (200000) oder ProgramFailedToComplete.
Diagnose:
solana confirm <tx-sig> -v
# Suchen Sie nach "consumed N of M compute units" und welche Instruction Erschöpfung verursacht hat.
Fixes:
- Erhöhen Sie das CU-Limit. Wenn Ihre Transaktion 195k von einem 200k-Budget verwendete, erhöhen Sie auf 300k.
- Teilen Sie die Transaktion. Wenn Sie die 1,4M-Obergrenze pro Transaktion erreichen, brechen Sie in zwei Transaktionen auf. Farm
harvest then stake ist eine klassische Transaktion zum Aufteilen, wenn Rewards zahlreich sind.
- Trimmen Sie Accounts. Jeder zusätzliche beschreibbare Account addiert ~2.000 CU. Das Prunen ungenutzter Accounts hilft bei Grenzfällen.
- Verwenden Sie Lookup-Tabellen. LUT-Lookups sind ~50 CU pro aufgelöstem Address, sparen 5.000 CU einer vollständigen Account-Referenz pro Eintrag.
Umgang mit steckengebliebenen Transaktionen
Symptom: Transaktion eingereicht, bestätigt sich nie, läuft schließlich ab mit BlockhashNotFound.
Diagnose:
getSignatureStatuses([sig]) gibt null zurück → Leader hat es nie gesehen.
- Gibt
{ confirmationStatus: null } zurück → Leader sah es, aber nahm es nicht auf.
Fixes:
- Erhöhen Sie die Priority Fee. Reichen Sie mit der 2-fachen aktuellen Gebühr erneut ein.
- Bauen Sie mit neuem Blockhash auf. Blockhash-Lebensdauer liegt bei ~60 Sekunden; danach ist die Transaktion ungültig, unabhängig von Gebühren.
- Multi-RPC-Broadcast. Einige RPCs haben bessere Leader-Verbindungen als andere. Reichen Sie parallel zu 3–5 ein.
- Wechseln Sie zu Jito Bundles. Siehe
integration-guides/routing-and-mev. Bundles umgehen öffentliche Paket-Warteschlangen.
Retry-Logik-Skelett:
async function submitWithRetry(buildTx, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const tx = await buildTx({
priorityFee: basePriorityFee * Math.pow(1.5, attempt),
blockhash: (await connection.getLatestBlockhash()).blockhash,
});
try {
const sig = await connection.sendRawTransaction(tx.serialize(), {
skipPreflight: attempt > 0, // skip after first try to save latency
});
const result = await connection.confirmTransaction(sig, "confirmed");
if (result.value.err) {
// Logic error; don't retry.
throw result.value.err;
}
return sig;
} catch (e) {
if (isExpiredError(e)) continue; // retry
if (isRevertError(e)) throw e; // don't retry; deterministic failure
throw e;
}
}
throw new Error("submit: exhausted retries");
}
Bei Netzwerküberlastung
Wenn das Netzwerk überlastet ist (Jupiter / Jito Bundle Dashboards zeigen Rückstau, RPC-Latenz spitzt zu, Transaktionsablauf-Raten klettern), passen Sie an:
| Parameter | Normale Bedingungen | Überlastete Bedingungen |
|---|
| CU-Limit | +25% über Schätzung | +25% über Schätzung (unverändert) |
| Priority-Fee-Perzentil | 50. | 75.–95. |
| Retry-Anzahl | 3 | 5–7 |
| Retry-Backoff | 500ms | 1000ms |
| Jito Bundles verwenden | Optional | Stark empfohlen |
| Blockhash-Refresh bei Retry | Ja | Ja, obligatorisch |
Überwachung von Überlastungssignalen:
- Priority-Fee 75. Perzentil > 500k Mikro-Lamports: Überlastung.
- Jito 50. Perzentil Tip > 0,001 SOL: Überlastung.
- RPC-Antwort p99 > 2s: RPC-spezifisches Problem oder Überlastung.
Gebührenbudgetierung für Bots
Ein Trading-Bot, der täglich ~1000 Transaktionen durchführt, benötigt ein Priority-Fee-Budget. Schätzung:
Durchschnittliche CU pro Transaktion: ~250.000
50. Perzentil-Gebühr: ~20.000 Mikro-Lamports/CU
Kosten pro Transaktion: 250_000 × 20_000 × 1e-6 = 5.000 Lamports = 5e-6 SOL
Tägliche Kosten (1000 Transaktionen): 5e-3 SOL ≈ 1 USD @ 200 USD SOL
Monatliche Kosten: ~30 USD
Das ist das Minimum. Bei Überlastung mit 5–10× multiplizieren. Planen Sie für ~150–300 USD/Monat an Priority Fees für einen Bot mit gleichmäßigem Durchsatz ein.
Bots, die in spezifische Slots landen müssen (Liquidationen, Arbitrage), zahlen kontinuierlich das 95. Perzentil und geben ~10× mehr aus. Jito-Bundle-Tips dominieren in dieser Größenordnung — oft 1000+ USD/Monat — aber die Alternative (Front-Run oder Ablauf) ist schlechter.
Fallstricke
1. CU-Limit vergessen
Der Standard ist 200k CUs × (Instructions in Transaktion). Ein Single-Instruction-Swap standardisiert auf 200k; das ist für CPMM auf SPL Token ausreichend, aber nicht für CLMM mit Tick-Überquerungen oder alles Token-2022. Setzen Sie es immer explizit.
2. Priority Fee auf dem falschen Account
Wenn Sie die Priority Fee gegen die Token-Mint schätzen, aber der heiße Account der Pool-Status ist, ist Ihre Schätzung zu niedrig. Der Pool-Status ist der richtige beschreibbare Account zum Ziel für Raydium.
3. Gebühren skalieren mit CU-Limit
total_priority_fee = units × microLamports. Die Erhöhung von units von 200k auf 1M bei 50k Mikro-Lamports/CU multipliziert Priority Fee um das 5-fache. Über-budgetieren Sie nicht einfach für den Fall; messen Sie.
4. Standard-Transaktionsversion
Legacy-Transaktionen haben niedrigere Account-Limits; V0-Transaktionen mit Address-Lookup-Tabellen ermöglichen größere Routen. Das SDK verwendet standardmäßig V0 in txVersion: TxVersion.V0. Wechseln Sie nicht zu Legacy, es sei denn, Sie benötigen Wallet-Kompatibilität.
5. skipPreflight verbirgt CU-Fehler
skipPreflight: true sendet die Transaktion ohne lokale Simulation. Sie sparen ~100ms, verlieren aber das frühe Feedback zur CU-Erschöpfung. Verwenden Sie es nur bei Retries, nicht beim ersten Versuch.
Verweise
Quellen: