NFT Mint on Solana: Landing a Transaction at T-Zero

A timed mint is the hardest submission case on Solana. What happens in the first seconds, why most bots fail there, and how to prepare before the clock starts.

BoltTx Team··10 min read
solananft-minttransaction-landingcongestioncandy-machinetrading-bot

A timed NFT mint compresses every hard problem in Solana submission into about ten seconds.

Thousands of participants submit within the same window, against the same accounts, competing for the same limited supply. Whatever weakness exists in your submission path shows up there and nowhere else — which is why mint bots that test perfectly fail on the day.

Why T-Zero Is the Worst Case

Ordinary congestion is diffuse. A mint is the opposite: a coordinated spike where everyone knows the exact second.

Three things happen simultaneously:

Block space contention peaks. Every participant submits into the same few slots.

Priority fees spike on specific accounts. Fees are contested per writable account, and everyone is writing to the same candy machine and collection accounts. ★A fee that was generous a minute earlier is nothing at T-zero.★

Supply is finite. Unlike trading, where a late fill is a worse price, a late mint is no mint. There is no partial credit.

This last point changes the optimisation target. In trading you tune for expected value across many attempts. ★In a mint you get one window, and the transaction either lands inside it or the outcome is zero.★

Everything Must Be Ready Before the Clock

The single largest source of avoidable failure is doing work at T-zero that could have been done at T-minus-ten-minutes.

// Refreshed continuously in the background. Never fetched at mint time.
let cached = await connection.getLatestBlockhash("confirmed");
setInterval(async () => {
  cached = await connection.getLatestBlockhash("confirmed");
}, 5_000);

// Warm the connection so no TLS handshake happens in the hot path.
await connection.getSlot();

// Resolve every PDA and derived address in advance.
const mintPdas = await precomputeAllAddresses();

★Every network round trip between "the clock hits zero" and "bytes are on the wire" is a slot you are giving away.★ A getLatestBlockhash call, a DNS resolution, a TLS handshake — each is avoidable with preparation.

What you cannot precompute is the mint keypair if the program requires a fresh one per mint. Generate those in advance too, in a pool, so generation is not on the critical path.

If your preparation is already tight and the last hop is the problem, a free BoltTx key is a one-line change to test against.

Fees at T-Zero

Recent-fee data reflects the moment before the mint, which is not the moment you are submitting into.

import { ComputeBudgetProgram } from "@solana/web3.js";

// Query with the accounts you actually write. During a mint,
// everyone is writing to the same ones you are.
const recent = await connection.getRecentPrioritizationFees({
  lockedWritableAccounts: [candyMachine, collectionMint, yourTokenAccount],
});
const fees = recent.map((r) => r.prioritizationFee).sort((a, b) => a - b);
const median = fees[Math.floor(fees.length / 2)] ?? 0;

instructions.unshift(
  // ★A mint is the most contested moment these accounts will see.
  // Recent median is a floor, not a target.★
  ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: Math.max(median * 5, 50_000),
  }),
  ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 }),
);

★Mint instructions are compute-heavy★ — account creation, metadata, collection verification, often several CPIs. Run simulateTransaction against a real mint on devnet, read unitsConsumed, and set setComputeUnitLimit from that figure plus margin. An underestimated limit fails a transaction that would otherwise have succeeded, and at T-zero there is no second chance.

The Retry Loop Matters More Here

One submission at T-zero is a coin flip. Retry continuously, and stop when the window closes:

const raw = tx.serialize();
const sig = await connection.sendRawTransaction(raw, {
  skipPreflight: true,   // ★no round trip to spare★
  maxRetries: 0,         // we drive this
});

while (await connection.getBlockHeight("confirmed") <= lastValidBlockHeight) {
  const { value } = await connection.getSignatureStatuses([sig]);
  if (value[0]) break;   // landed; check .err for sold-out vs success

  await connection.sendRawTransaction(raw, {
    skipPreflight: true,
    maxRetries: 0,
  });
  await new Promise((r) => setTimeout(r, 400));
}

skipPreflight matters more here than anywhere else. Preflight costs a round trip and simulates against the current slot — and during a mint, the state it simulates against is being changed by thousands of other transactions. ★A passing simulation at T-zero predicts nothing.★

Reading the Failure Correctly

After a mint, transactions fail for reasons that look similar and mean different things:

Result Meaning Was it your fault?
null from status ★never landed★ ★yes — fee, path, retry★
Landed, sold-out error you were late partly
Landed, compute exceeded ★CU limit too low★ ★yes — avoidable★
Landed, insufficient funds balance too low yes
Landed, not on allowlist not eligible no

★The second row is the honest outcome — you competed and lost.★ The first and third are self-inflicted, and they are the ones worth engineering against.

const { value } = await connection.getSignatureStatuses([sig], {
  searchTransactionHistory: true,
});

if (!value[0])         log("never_landed");     // submission problem
else if (value[0].err) log("landed_failed", value[0].err);
else                   log("minted");

Multiple Wallets Do Not Multiply Your Odds

A common assumption worth dismantling.

Submitting from five wallets means five transactions competing in the same contested slots, each paying its own fee. ★Scheduling is per-transaction, so five mediocre submissions do not add up to one good one.★

Worse, they may write the same accounts and serialise against each other. You can end up competing with yourself while paying five times the fees.

If the mint has a per-wallet cap and you genuinely want multiple allocations, that is a different situation. But as a strategy for improving one wallet's odds, it does not work.

What Landing Looks Like

Real transactions through our delivery nodes: median confirmation 336ms — under one slot.

★During a mint that number stretches★ — most transactions are unaffected while a minority take noticeably longer. A mint puts you in that minority by construction, since the mint is the congestion event.

Two slots at T-zero is usually inside the supply. Five is usually not.

A Pre-Mint Checklist

□ Blockhash cached and refreshing on a background timer
□ Connection warm — no TLS handshake in the hot path
□ All PDAs and `getAssociatedTokenAddress` results precomputed
□ Mint keypairs pre-generated in a pool
□ CU limit measured by simulation, plus margin
□ `getRecentPrioritizationFees` queries the specific writable accounts
□ Retry loop bounded by lastValidBlockHeight
□ skipPreflight on, maxRetries 0
□ Failure classification: never_landed vs landed_failed

★Everything above can be verified before the clock starts.★ Doing so is the difference between a bot that fails for a reason you can fix and one that fails for a reason you never learn.

Where BoltTx Fits

We handle the last hop. Not mint feeds, not allowlist management, not indexing.

Submissions route through our own delivery nodes in four regions with stake-weighted routing and no public mempool exposure, so a transaction is not observable in transit before it lands. You sign locally — we never hold funds, never sign, and never modify transaction contents.

The tip travels inside the transaction, paid on chain from your own wallet, and reverts with the transaction if it fails, because that is how Solana handles atomic transactions. You pay only on transactions that reach the chain.

Get a free API key. No monthly fee:

// Pick the region closest to where your bot runs
const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");

Then log slot distance across a real mint and compare.

FAQ

Why did my NFT mint transaction fail? Check whether it landed at all. A null status means it never reached a block — a fee, routing, or retry problem. A landed transaction with an error means you reached the chain and something else stopped you, such as the supply already being gone.

How do I mint an NFT on Solana as fast as possible? Do everything before T-zero: cache a background-refreshed blockhash, warm the connection, precompute PDAs, pre-generate keypairs. At T-zero, submit immediately and retry every slot until the blockhash expires.

What priority fee should I use for an NFT mint? Well above the recent median on the specific accounts involved. A mint is the most contested moment those accounts will ever see, so recent-fee data reflects conditions that no longer apply.

Does using multiple wallets improve my mint odds? No. Scheduling is per-transaction, so several mediocre submissions do not combine into one good one. They may also write the same accounts and serialise against each other while multiplying your fees.

How many compute units does an NFT mint need? More than a simple transfer, since minting involves account creation, metadata, and often several CPIs. Simulate a real mint on devnet to measure, then add margin — an underestimate fails a transaction that would have succeeded.

Should I use skipPreflight for minting? Yes. Preflight costs a round trip you cannot spare and simulates against a state that thousands of other transactions are changing. A passing simulation at T-zero predicts nothing about your outcome.

Why did my transaction land but say sold out? You reached the chain after the supply was exhausted. That is the honest competitive outcome rather than a bug — though a smaller slot distance would have improved your position.

How early should I prepare before a mint? Have everything cached and warm minutes before, not seconds. The only thing that should happen at T-zero is signing and submitting.

Can I pre-sign a mint transaction? Only if the mint address and all accounts are known in advance, which is uncommon. What you can do is pre-generate keypairs and precompute derived addresses so nothing is on the critical path.

Why does my bot work in testing but fail on mint day? Testing happens when the network is calm and every submission path performs identically. A mint is a coordinated spike, and it is the only time the differences appear.

What blockhash should I use for a mint? One cached in the background and refreshed on a short timer. Fetching at T-zero adds a round trip in the one place you cannot afford it, and a blockhash cached at startup will be near expiry.

Is a dedicated RPC node worth it for minting? Dedicated capacity solves rate limits, not landing. During a mint the constraint is how your transaction is routed toward a block producer and the stake weight behind that path, which exclusivity does not change.

How do I know if I lost because of speed or eligibility? Classify the failure. Never landed means submission; landed with a sold-out error means you were late; landed with an allowlist error means eligibility. These have completely different fixes.

Should I keep retrying after the mint sells out? No. Once a transaction lands with a sold-out error, retrying reproduces the same failure and pays another base fee. Stop on that specific error.

What is the realistic slot target for a mint? Landing within a couple of slots of T-zero. Beyond that you are usually competing against a supply that is already allocated, and no amount of retrying changes it.

← Back to all posts