Skip to content

fix(token): mark InitializeMultisig member accounts as read-only - #461

Open
ozpool wants to merge 1 commit into
solana-foundation:mainfrom
ozpool:fix/multisig-signer-flags
Open

fix(token): mark InitializeMultisig member accounts as read-only#461
ozpool wants to merge 1 commit into
solana-foundation:mainfrom
ozpool:fix/multisig-signer-flags

Conversation

@ozpool

@ozpool ozpool commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What

InitializeMultisig and InitializeMultisig2, in both programs/token and programs/token-2022, tagged each multisig member account with the signer flag:

inst.Signers = append(inst.Signers, ag_solanago.Meta(signer).SIGNER())

The canonical SPL Token account spec declares those accounts read-only, non-signer:

InitializeMultisig
  0.      [writable] The multisignature account to initialize.
  1.      []         Rent sysvar.
  2..2+N. []         The signer accounts, must equal to N where 1 <= N <= 11.

InitializeMultisig2
  0.      [writable] The multisignature account to initialize.
  1..1+N. []         The signer accounts.

The program only records the member pubkeys into the multisig account data; it does not require them to sign the InitializeMultisig transaction. The struct's own doc comment already states this ("The InitializeMultisig instruction requires no signers ..."), so the builder contradicted its own documentation.

Fix

Drop the spurious .SIGNER() in AddSigners for both instructions in both programs, and sync the [2...] = [SIGNER] / [1] = [SIGNER] annotation headers to []. One-flag change per builder; the multisig account itself stays [WRITE], the rent sysvar stays read-only.

Tests

Adds TestAccountFlags_InitializeMultisig / TestAccountFlags_InitializeMultisig2 to both packages, asserting the members are read-only non-signer and the multisig account is writable. Proven to fail on the current code (member account 0 must not be a signer) and pass with the fix. go build ./..., go vet, and the full programs/token + programs/token-2022 suites are green; gofmt clean.

Closes #460.

Same account-flag correctness class as #452 / #458 (folded into #453). Kept as a separate PR since it is a different program and does not overlap those diffs, but happy to fold it into #453 instead if you prefer a single account-flags PR.

SPL Token InitializeMultisig/InitializeMultisig2 declare the member
(signer) accounts as read-only non-signer ([]); the program records
their pubkeys into the multisig data and does not require them to sign
the setup transaction. AddSigners was tagging each member .SIGNER(),
in both programs/token and programs/token-2022, contradicting the
struct's own doc ("requires no signers"). Drop the signer flag and
sync the account-annotation headers. Adds TestAccountFlags_* asserting
members are read-only non-signer and the multisig account stays
writable.

Closes solana-foundation#460
@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR corrects a spurious .SIGNER() flag on the member accounts in InitializeMultisig and InitializeMultisig2 for both programs/token and programs/token-2022. The SPL Token spec declares those accounts read-only and non-signer — the program only stores the pubkeys in multisig account data and does not require members to co-sign the setup transaction.

  • Removes .SIGNER() from AddSigners in all four instruction builders and syncs the struct-level comment annotations ([SIGNER][]).
  • Adds TestAccountFlags_InitializeMultisig and TestAccountFlags_InitializeMultisig2 to both packages, directly asserting that member accounts have neither IsSigner nor IsWritable set, and that the multisig account remains writable.

Confidence Score: 5/5

Safe to merge — the change corrects a mismatch between the builder output and the on-chain SPL Token account spec, and the new tests directly guard the fixed behaviour.

Each of the four affected builders gets a single, targeted removal of the incorrect .SIGNER() call, and the struct-level comment annotations are updated to match. The new test files cover both instruction variants in both packages and assert the exact flag values expected by the runtime. No other program logic is touched.

No files require special attention.

Important Files Changed

Filename Overview
programs/token/InitializeMultisig.go Removes .SIGNER() from AddSigners and updates the comment annotation from [SIGNER] to []; matches the SPL Token spec.
programs/token/InitializeMultisig2.go Same one-line fix as InitializeMultisig — drops .SIGNER() and corrects the comment annotation.
programs/token-2022/InitializeMultisig.go Mirrors the token/InitializeMultisig.go fix for the Token-2022 program; annotation and builder method both corrected.
programs/token-2022/InitializeMultisig2.go Mirrors the token/InitializeMultisig2.go fix for Token-2022; clean, no issues.
programs/token/InitializeMultisig_flags_test.go New test file covering both InitializeMultisig and InitializeMultisig2; asserts correct writable/signer flags on all account types.
programs/token-2022/InitializeMultisig_flags_test.go Equivalent test file for Token-2022; identical structure and assertions, looks correct.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller
    participant Builder as InstructionBuilder
    participant Instruction as InitializeMultisig(2)
    participant Chain as Solana Runtime

    Caller->>Builder: NewInitializeMultisig(2)InstructionBuilder()
    Builder->>Builder: SetAccount(multisigPubkey) → [WRITE]
    Builder->>Builder: AddSigners(member1, member2, ...) → [] (read-only, non-signer)
    Builder->>Instruction: Build()
    Instruction->>Chain: Submit transaction
    Note over Chain: Program reads member pubkeys<br/>from accounts array and<br/>stores them in multisig data.<br/>No member signatures required.
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Caller
    participant Builder as InstructionBuilder
    participant Instruction as InitializeMultisig(2)
    participant Chain as Solana Runtime

    Caller->>Builder: NewInitializeMultisig(2)InstructionBuilder()
    Builder->>Builder: SetAccount(multisigPubkey) → [WRITE]
    Builder->>Builder: AddSigners(member1, member2, ...) → [] (read-only, non-signer)
    Builder->>Instruction: Build()
    Instruction->>Chain: Submit transaction
    Note over Chain: Program reads member pubkeys<br/>from accounts array and<br/>stores them in multisig data.<br/>No member signatures required.
Loading

Reviews (1): Last reviewed commit: "fix(token): mark InitializeMultisig memb..." | Re-trigger Greptile

@ozpool

ozpool commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

@jacobcreech apologies for the cold tag — you've merged here before, so hoping you can point me in the right direction rather than me guessing.

This one's been open since July 3 with green CI and no human review yet, and I'm not sure who's picking up SDK reviews at the moment. Not chasing a merge — just want to make sure it's in someone's queue rather than quietly lost.

Short version: InitializeMultisig and InitializeMultisig2, in both programs/token and programs/token-2022, tag every multisig member account as SIGNER. The canonical SPL Token spec declares those accounts read-only non-signers — the program only records the member pubkeys, it doesn't require them to sign the instruction. The struct's own doc comment already says "The InitializeMultisig instruction requires no signers", so the builder was contradicting its own documentation. The fix drops .SIGNER() in all four builders and syncs the annotation headers; the added tests assert the flags and fail on current main.

Entirely happy to close it if the existing flags are deliberate for a reason I've missed — #460 has the full write-up.

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.

token/token-2022: InitializeMultisig marks member accounts as [SIGNER] instead of read-only

1 participant