Skip to content

security: gate pda-mint-authority native and pinocchio mint_to behind the token's original creator - #699

Open
kartsan03 wants to merge 1 commit into
solana-foundation:mainfrom
kartsan03:security/pda-mint-authority-native-pinocchio-admin-gate
Open

security: gate pda-mint-authority native and pinocchio mint_to behind the token's original creator#699
kartsan03 wants to merge 1 commit into
solana-foundation:mainfrom
kartsan03:security/pda-mint-authority-native-pinocchio-admin-gate

Conversation

@kartsan03

Copy link
Copy Markdown

Summary

#692 gated mint_token in tokens/pda-mint-authority anchor behind a MintConfig PDA. That PR's write-up already flagged the leftover on the siblings:

The native/pinocchio siblings have a different, lower-severity, related concern: since create and mint_to are separate instructions, any caller can crank the one-time mint_to before the intended recipient, directing a single NFT to themselves instead.

That leftover is still unfixed. In both variants the mint authority is a program PDA (seeds = [b"mint_authority"]) that invoke_signeds the MintTo CPI for whoever calls mint_to. There is no check that the caller is the wallet that paid create_token. Anyone who sees a freshly created mint can mint the NFT to themselves.

Unlike the anchor bug this is bounded to one NFT per mint (the subsequent Metaplex Master Edition CPI takes over mint/freeze authority), but it is still unauthorized first-mint / front-running of the creator, and it is still the same "PDA signs unconditionally for whoever calls" hole #692 closed on the other variant.

This PR does not change the teaching point (PDA as mint authority + invoke_signed). It only records the create_token payer in a per-mint MintConfig PDA (seeds = [b"mint_config", mint]) and rejects mint_to unless the caller matches that admin.

The PDA is bound to the mint (unlike the anchor variant's global [b"mint_config"]) because native/pinocchio create one mint per create_token call, not a single program-wide mint.

Fix

  • create_token creates [b"mint_config", mint] owned by the program and writes admin = payer.
  • mint_to re-derives that PDA, checks program ownership, and returns InvalidArgument if admin != payer.
  • Native also checks payer.is_signer; pinocchio does the same via payer.is_signer().
  • TS clients pass the extra account. mint_config is writable on create (account is allocated there) and readonly on mint.

Test changes

Added a negative test in both native/tests/test.ts and pinocchio/tests/test.ts.

The existing happy-path mint already creates a Metaplex Master Edition, which would make a second mint_to on the same mint fail even without the admin check. The new tests therefore:

  1. create_token a fresh mint as the original payer
  2. have a second, airdropped wallet call mint_to on that mint
  3. assert FailedTransactionMetadata

The Create tests also assert the new config PDA exists and is owned by the program.

Verification

From WSL Ubuntu (cargo-build-sbf 4.1.0, pnpm 10.33.0):

cd tokens/pda-mint-authority/native
pnpm install
pnpm build-and-test
# NFT Minter
#   ✔ Init Mint Authority PDA
#   ✔ Create an NFT!
#   ✔ Mint the NFT to your wallet!
#   ✔ rejects mint from a wallet that did not create the token
# 4 passing (81ms)

cd ../pinocchio
pnpm install
pnpm build-and-test
# PDA Mint Authority (Pinocchio)
#   ✔ Initialize the mint authority PDA!
#   ✔ Create an NFT!
#   ✔ Mint the NFT to your wallet!
#   ✔ rejects mint from a wallet that did not create the token
# 4 passing (73ms)

cd ../../..
cargo fmt -p pda-mint-authority-native-program -p pda-mint-authority-pinocchio-program
cargo clippy -p pda-mint-authority-native-program -p pda-mint-authority-pinocchio-program -- -D warnings
# Finished `dev` profile

cd tokens/pda-mint-authority/native && pnpm exec tsc --noEmit
cd ../pinocchio && pnpm exec tsc --noEmit
# both exit 0

cd ../../..
pnpm exec prettier --write \
  tokens/pda-mint-authority/native/tests/test.ts \
  tokens/pda-mint-authority/native/ts/instructions/create.ts \
  tokens/pda-mint-authority/native/ts/instructions/mint.ts \
  tokens/pda-mint-authority/pinocchio/tests/test.ts

Out of scope

… the token's original creator

solana-foundation#692 gated the anchor variant. The native and pinocchio siblings still let any
caller mint the NFT because the mint-authority PDA signs unconditionally. Record
the create_token payer in a per-mint config PDA and reject mint_to from anyone
else, before the master edition is created.
@kartsan03
kartsan03 requested a review from dev-jodee as a code owner August 26, 2026 18:50
@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR closes unauthorized first-mint front-running in the native and Pinocchio PDA mint-authority examples.

  • Adds a per-mint MintConfig PDA that records the token creator.
  • Requires mint callers to sign and match the recorded creator before the program signs token and metadata CPIs.
  • Updates native client builders and both test suites for the additional account.
  • Adds negative tests proving a different wallet cannot mint a freshly created token.

Confidence Score: 5/5

The PR appears safe to merge with the new creator authorization consistently enforced across both implementations and their clients.

The per-mint PDA derivation, stored administrator layout, signer checks, ownership checks, account ordering, and client inputs are aligned, and no concrete blocking or non-blocking defect remains.

Important Files Changed

Filename Overview
tokens/pda-mint-authority/native/program/src/instructions/create.rs Creates the mint-bound configuration PDA and serializes the payer as its administrator.
tokens/pda-mint-authority/native/program/src/instructions/mint.rs Validates the caller’s signature and creator authorization before performing mint CPIs.
tokens/pda-mint-authority/native/program/src/state/mod.rs Defines the fixed-size Borsh configuration record containing the creator’s public key.
tokens/pda-mint-authority/pinocchio/program/src/instructions/create.rs Creates and initializes the Pinocchio variant’s per-mint configuration PDA.
tokens/pda-mint-authority/pinocchio/program/src/instructions/mint.rs Enforces signer, PDA ownership, and recorded-admin checks before minting.
tokens/pda-mint-authority/pinocchio/program/src/state.rs Adds matching 32-byte serialization and deserialization for the creator configuration.
tokens/pda-mint-authority/native/ts/instructions/create.ts Adds the writable configuration PDA to the native create instruction contract.
tokens/pda-mint-authority/native/ts/instructions/mint.ts Adds the readonly configuration PDA to the native mint instruction contract.
tokens/pda-mint-authority/native/tests/test.ts Updates the happy path and verifies that an unrelated signer cannot mint.
tokens/pda-mint-authority/pinocchio/tests/test.ts Updates inline account lists and adds equivalent unauthorized-mint coverage.

Reviews (1): Last reviewed commit: "security: gate pda-mint-authority native..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant