All SIMDs
SIMD · 0266

p-token: Efficient Token program

ReviewCoreStandardMarch 19, 2025
by febo (Anza), Jon Cinque (Anza)

Summary

Replace the current version of SPL Token (TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA) program by a CU-optimized one (p-token).

Motivation

About ~10% of block compute units is used by the Token program instructions. Decreasing the CUs used by Token program instructions creates block space for other transactions to be executed – i.e., less CUs consumed by Token program, more CUs for other transactions.

As an example, if we reduce the CUs consumed by Token instructions to 1/20th of their current value, 10% of block CUs utilized becomes 0.5%, resulting in a 9.5% block space gain.

Additionally, there are benefits to downstream programs:

  • Better composability since using the Token program instructions require less CUs. - Cheaper (in terms of CU) cross-program invocations.

New Terminology

N/A.

Detailed Design

p-token (repository) is a like-for-like efficient re-implementation of the Token program. It is no_std (no heap memory allocations are made in the program) and uses zero-copy access for instruction and account data. Since it follows the same instructions and accounts layout, it does not require any changes to client code – it works as a drop-in replacement.

Apart from the original SPL Token instructions, this proposal adds three additional instructions to the program:

  1. withdraw_excess_lamports (instruction discriminator 38): allow recovering "bricked" SOL from mint (e.g., USDC mint as ~323 SOL in excess) and multisig accounts. The logic of this instruction is similar to the current SPL Token-2022 instruction: the mint authority must be a signer (for mint accounts) or the multisig (for multisig accounts) to authorize the withdraw. Additionally, for mint accounts that do not have a mint authority set, it is possible to authorize the withdraw using the mint account as the signing authority — the instruction will need to be signed using the mint private key. Note that economic consequences may occur depending on the quantity of "unbricked" SOL; the total amount of SOL that could be freed up in this manner has yet to be calculated.
  2. batch (instruction discriminator 255): enable efficient CPI interaction with the Token program. This is a new instruction that can execute a variable number of Token instructions in a single invocation of the Token program. Therefore, the base CPI invoke units (currently 1000 CU) are only consumed once, instead of for each CPI instruction – this significantly improves the CUs required to perform multiple Token instructions in a CPI context. Almost every DeFi protocol on Solana performs multiple CPIs to the Token program in one instruction. For example, an AMM performs two transfers during swap, or transfers tokens and mints others during an LP deposit. Programs can use the batch instruction for even more CU gains.
  3. unwrap_lamports (instruction discriminator 45): allows transferring out lamports from native SOL token accounts directly to any destination account. This eliminates the need for creating temporary native token accounts for the recipient. The instruction supports transferring a specific amount or the entire amount of the account.

Note that withdraw_excess_lamports discriminator matches the value used in SPL Token-2022, while batch and unwrap_lamports have discriminator values that are not used in either SPL Token nor Token-2022.

The program will be loaded into an account (ptok6rngomXrDbWf5v5Mkmu5CEbB51hzSCPDoj9DrvF) prior to enabling the feature gate that triggers the replacement.

When the feature gate ptokFjwyJtrwCa9Kgo9xoDS59V4QccBGEaRFnRPnSdP is enabled, the runtime needs to:

  • Replace TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA program with ptok6rngomXrDbWf5v5Mkmu5CEbB51hzSCPDoj9DrvF using the Upgradable Loader v3.

Alternatives Considered

As an alternative to replace the current version of SPL Token, p-token could be deployed to a new address and people can be encouraged to transition to that program. This would hinder its adoption and benefits, since people would be very slow to adopt the new program, if they adopt it at all.

Another point considered was to whether keep the current logs on the program or not. Logs are limited to show the name of the instruction being executed, e.g.:

Program Tokenkeg...623VQ5DA invoke [1]
Program log: Instruction: Transfer
Program Tokenkeg...623VQ5DA consumed 192 of 200000 compute units
Program Tokenkeg...623VQ5DA success

Logging the instruction name consumes more than 100 compute units, which in some cases reprensents almost the same amount required to execute the instruction. Since these logs are not necessarily very reliable — e.g., they can be truncated, you could "log-inject" matching messages that can "confuse" parsers — we propose to omit logging.

Sample CU consumption for p-token without logs: | Instruction | p-token - logs (CU) | p-token (CU) | |----------------------|-----------------------|----------------------| | InitializeMint | 105 | 214 | | InitializeAccount | 154 | 264 | | Transfer | 76 | 186 | | TransferChecked | 105 | 218 | | MintTo | 123 | 231 | | Burn | 133 | 237 | | CloseAccount | 125 | 229 |

Impact

The main impact is freeing up block CUs, allowing more transactions to be packed in a block; dapp developers benefit since interacting with the Token program will consume significantly less CUs.

Below is a sample of the CUs efficiency gained by p-token compared to the current SPL Token program, including the percentage of CUs used in relation to the current SPL Token consumption — the lower the percentage, the better the gains in CUs consumption.

Instructionspl tokenp-token% of spl-token
Approve29041244.2%
ApproveChecked44581643.6%
Burn47531262.6%
BurnChecked47541292.7%
CloseAccount29161204.1%
FreezeAccount42651463.4%
InitializeAccount45271543.4%
InitializeAccount243881713.8%
InitializeAccount342402485.8%
InitializeImmutableOwner1404382.7%
InitializeMint29671053.5%
InitializeMint228272288.0%
InitializeMultisig29731936.4%
InitializeMultisig2282631811.2%
MintTo45381192.6%
MintToChecked45451693.7%
Revoke2677973.6%
SetAuthority31671233.8%
SyncNative3045612.0%
ThawAccount42671423.3%
Transfer4645761.6%
TransferChecked62001051.6%

Considering the usage distribution of instructions (shown below), migrating to p-token will significantly reduce the block CUs currently consumed by the token program.

InstructionUsage (%)
TransferChecked36.33%
Transfer13.22%
CloseAccount12.23%
InitializeAccount39.98%
InitializeImmutableOwner9.78%
SyncNative4.53%
InitializeAccount2.58%

Other instructions account for less than 1% each.

Security Considerations

p-token must be guaranteed to follow the same instructions and accounts layout, as well as have the same behaviour than the current Token implementation.

Any potential risk will be mitigated by extensive testing and auditing:

  • [COMPLETED] Existing SPL Token test fixtures
  • [COMPLETED] Fuzzing using Firedancer tooling (solfuzz_agave): this includes executing past mainnet instructions — with or without random modifications amounting to millions of individual instructions — and verifying that the complete program output (i.e., both the program result and accounts' state) matches.
  • [COMPLETED] Audits - Neodyme Audit (2025-06-12) - Zellic Audit (2025-06-30) - Zellic Audit (2025-10-13)
  • [IN PROGRESS] Formal Verification

Since there are potentially huge economic consequences of this change, the feature will be put to a validator vote.

The replacement of the program requires breaking consensus on a non-native program. However, this has been done in the past many times for SPL Token to fix bugs and add new features.

Drawbacks

N/A.

Backwards Compatibility

Fully backwards compatible, no changes are required for users of the program. Note that we are proposing omitting instruction name logs (e.g., "Instruction: <name>") . ````