The pattern everywhere
- ComputeBudgetProgram.setComputeUnitLimit({ units: 1_400_000 }) — the max possible limit, set as a default. It never runs out of compute, but it overpays on fees, since priority fee cost scales with the limit you ask for, not what you actually use.
- A blockhash fetched once, early in the flow, and reused minutes later when the transaction finally gets sent — well past the point where it's still valid.
- setComputeUnitPrice set to a flat constant, written once and never revisited, while the actual fee market moves by the block.
- A confirmation loop that's really just setTimeout(30000) and a single status check, which either gives up too early or hangs past the point the blockhash has already expired.
Turning the fix into a skill
- Simulate first. Run the transaction through simulateTransaction before you ever decide how much compute it needs. The simulation tells you the real unitsConsumed, not a guess.
- Size from the simulation. Set the compute unit limit to that real number plus a small margin, not the network maximum.
- Price fees from live data. Pull getRecentPrioritizationFees for the accounts you're about to write to, not a hardcoded constant, and compute a percentile-based recommendation.
- Fetch the blockhash last. Get it immediately before signing, not earlier in the flow, and poll confirmation against lastValidBlockHeight instead of a fixed timeout.
- Cap before you escalate. If a transaction needs to retry with a higher fee, the ceiling is explicit and agreed up front, never an unbounded multiply-and-pray loop.