Skip to content

Build1 publisher3 min readPublished

A feature gate replaced SPL Token's bytecode beneath its hard-coded address

SIMD-0266 activated at the start of epoch 971 on 13 May 2026, putting p-token behind the classic token program ID on the upgradeable loader with its upgrade authority set to none. Nobody redeployed anything.

The Engineer · Build desk

Illustration accompanying A feature gate replaced SPL Token's bytecode beneath its hard-coded address

What happened

  • On 13 May 2026, at the start of epoch 971 and slot 419,472,000, a Solana feature gate swapped the classic SPL Token program's bytecode for a new implementation called p-token under the same address.
  • Agave's bundled copy of the classic token program was already p-token, a roughly 109 KB file named spl_p_token-1.0.0.so, so local test validators had been running it for a while.
  • The write-up leaves per-instruction compute figures to a benchmark and notes the buffer is on chain for anyone who wants to measure the difference themselves.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Any client with a compute-unit limit for token transfers pinned in a constant is now requesting block space the instruction does not use, and only re-measurement recovers the ranking at the same fee.
  • constraint With no upgrade key, a p-token bug cannot be hot-fixed by its authors; the repair path runs through a fresh feature gate and 95% of stake. That is a release cycle measured in epochs.
  • precedent A hard-coded program ID guarantees which account you are calling and no longer implies which code answers, because a feature activation can rewrite the program data underneath it.
  • capability A hot token account can absorb more transfers before it exhausts its share of a block, since the ceiling is spent in cheaper units.

At the epoch boundary the runtime does the write itself. The feature account for `replace_spl_token_with_p_token` names both sides of the swap: the target is the classic token program ID, and the source is a buffer account that already held the p-token binary on chain [4]. From there the runtime copies the buffer into a program-data account, points the program at it, and records the upgrade authority as none [5]. No owner signed a deploy. Activation needs the new binary shipped in the validator plus 95% of stake, and it lands at an epoch boundary [6].

The machinery is familiar. Over the past year the stake, config, address-lookup-table and feature-gate programs went from native code to on-chain bytecode the same way [7]. Agave's own bundled copy of the classic token program is already p-token, a file called `spl_p_token-1.0.0.so` of about 109 KB, so every local test validator and every fresh genesis has been running the replacement for a while [8]. The account this affects holds every Solana token that is not Token-2022 and is the most-called program on the chain [1][22]. A program ID pinned in your client still resolves to the same account, and the bytes under it are whatever the last activated gate wrote [2][5].

The instruction set did not move: the discriminators, account layouts and errors are the original ones, so a wallet, an explorer or a DEX sees no difference in what it sends or reads [10]. Per-call cost moved. The original was written against the standard Rust framework and paid those overheads on every call, while p-token is built in Pinocchio, which has no allocator and minimal runtime overhead, and strips them [9][11]. A leader ranks transactions by reward divided by the block space they request [13]. Set your compute limit from a simulation and it falls on its own, the requested block space falls with it, and the transaction ranks higher at the same fee [14]. Clients that hard-code the limit keep paying the old number [15].

The dev.to write-up leaves per-instruction figures to a benchmark, notes that the buffer is on chain for anyone to measure against, and claims only the direction [12]. For the saving to show up in your transaction, the compute has to have been going into token instructions in the first place, because p-token changed the token program's per-call overhead and nothing else [24]. A swap that spends most of its units in its own curve math will move less than a bare transfer [24].

The per-account share of a block is 30 million compute units at today's 300 ms slots, and 25 million from epoch 1037, when 250 ms slots start [16]. Divide each by its slot time and both come out at 100 million units per second [17]. The extra room for a hot token account comes from transfers costing less [23].

The upgrade authority is none for the reason the validator source gives, according to that write-up: the original program lived under a loader with no concept of an upgrade authority, so the migrated program was given the closest equivalent, which is nobody [18]. There is no key that can push a patch, so the incident response plan is another feature gate, a new buffer, and another 95% of stake [19]. A holder needs to do nothing [20]. Anyone who set a compute limit by hand is buying block space they no longer use until they re-measure [15].

What to watch

  • Published per-instruction compute measurements against the on-chain p-token buffer, since the proposal argued the direction only.
  • Epoch 1037, when 250 ms slots begin and the per-account block limit is set to 25 million compute units.
  • The first p-token bug report: it would test whether a fix can clear 95% of stake as a fresh feature gate.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories