Skip to content
This repository was archived by the owner on Sep 8, 2026. It is now read-only.

solana-client-tools: add a payload budget and a checked encoder for Squads vaults - #409

Merged
karl-dz merged 4 commits into
mainfrom
claude/happy-austin-df32fd
Aug 14, 2026
Merged

solana-client-tools: add a payload budget and a checked encoder for Squads vaults#409
karl-dz merged 4 commits into
mainfrom
claude/happy-austin-df32fd

Conversation

@karl-dz

@karl-dz karl-dz commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Closes malbeclabs/doublezero#4184

Summary

  • Add vault_transaction_payload_budget, reporting the bytes a payload of a given instruction count may serialize to, for a caller that grows a single instruction until it stops fitting
  • Add try_encode_vault_transaction, which measures the payload and refuses one that cannot work. print_vault_transaction becomes try_print_vault_transaction and routes through it
  • Derive the reserve as const arithmetic rather than in a comment, one term per line, from the vault_transaction_create envelope plus the compute budget pair, proposal_create, and proposal_approve the Squads app bundles into that same transaction. Compile-time assertions pin each block's subtotal and the 384 total, so a changed term fails the build instead of leaving a stale literal. A comment records the three app behaviors the derivation assumes away and what each costs
  • Refuse a payload naming a signer other than the vault, and one carrying more than 48 instructions. Both import and collect approvals before failing at execute, unlike an oversized payload, which fails at import
  • Check the vault_transaction_execute transaction as well, at 329 bytes plus 33 per payload account key. The issue specifies 277, which omits the compute budget pair the app bundles there too
  • Record the conventions this change was written under in CLAUDE.md, covering numeric literals, prose in comments and documentation, the try_ prefix, and where workspace dependency features belong

Testing

  • Hand-computed payload lengths at 3, 127 and 128 instruction-data bytes, spanning the legacy length prefix boundary
  • A payload landing exactly on the budget and one byte over it, at one instruction and at two
  • The wrapper Squads builds around a budget-sized payload, assembled and measured at 1,229 bytes of the 1,232 available
  • The widest payload the budget accepts, at 24 account keys and 1,121 bytes to execute
  • Refusals for an empty payload, a foreign signer, and 49 instructions
  • A downstream caller's vault print path against devnet, run before and after the change, byte-identical

@karl-dz
karl-dz marked this pull request as ready for review August 14, 2026 16:22
@karl-dz
karl-dz requested a review from bgm-malbeclabs August 14, 2026 16:22

@bgm-malbeclabs bgm-malbeclabs left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the derivations by hand and ran the crate tests (17 squads tests pass) and cargo clippy --all-targets (clean).

What checks out: all five const _: () = assert!(...) blocks add up, and the Squads v4 account lists they encode are right (VaultTransactionCreate 5 accounts, ProposalCreate 5, ProposalVote 3, VaultTransactionExecute 4). The one byte per instruction charge is a correct upper bound, since Squads writes instruction data length as SmallVec<u16, u8> against legacy shortvec, and every other length prefix over-charges rather than under-charges. The claim that create binds before execute holds: create allows 24 zero-data account keys against execute's 27, and execute does not grow with instruction count, so the execute check really is unreachable. No callers are left on the removed print_vault_transaction and encode_vault_transaction.

No correctness bug found. Two nits below, neither a blocker.

Comment thread crates/solana-client-tools/src/squads.rs
Comment thread crates/solana-client-tools/src/squads.rs Outdated
@karl-dz
karl-dz enabled auto-merge (squash) August 14, 2026 16:58
@karl-dz
karl-dz merged commit 8236fce into main Aug 14, 2026
6 checks passed
@karl-dz
karl-dz deleted the claude/happy-austin-df32fd branch August 14, 2026 17:04
bgm-malbeclabs pushed a commit to malbeclabs/doublezero that referenced this pull request Aug 28, 2026
…quads vaults (malbeclabs/doublezero-offchain#409)

Closes #4184

## Summary
- Add `vault_transaction_payload_budget`, reporting the bytes a payload
of a given instruction count may serialize to, for a caller that grows a
single instruction until it stops fitting
- Add `try_encode_vault_transaction`, which measures the payload and
refuses one that cannot work. `print_vault_transaction` becomes
`try_print_vault_transaction` and routes through it
- Derive the reserve as const arithmetic rather than in a comment, one
term per line, from the `vault_transaction_create` envelope plus the
compute budget pair, `proposal_create`, and `proposal_approve` the
Squads app bundles into that same transaction. Compile-time assertions
pin each block's subtotal and the 384 total, so a changed term fails the
build instead of leaving a stale literal. A comment records the three
app behaviors the derivation assumes away and what each costs
- Refuse a payload naming a signer other than the vault, and one
carrying more than 48 instructions. Both import and collect approvals
before failing at execute, unlike an oversized payload, which fails at
import
- Check the `vault_transaction_execute` transaction as well, at 329
bytes plus 33 per payload account key. The issue specifies 277, which
omits the compute budget pair the app bundles there too
- Record the conventions this change was written under in `CLAUDE.md`,
covering numeric literals, prose in comments and documentation, the
`try_` prefix, and where workspace dependency features belong

## Testing
- Hand-computed payload lengths at 3, 127 and 128 instruction-data
bytes, spanning the legacy length prefix boundary
- A payload landing exactly on the budget and one byte over it, at one
instruction and at two
- The wrapper Squads builds around a budget-sized payload, assembled and
measured at 1,229 bytes of the 1,232 available
- The widest payload the budget accepts, at 24 account keys and 1,121
bytes to execute
- Refusals for an empty payload, a foreign signer, and 49 instructions
- A downstream caller's vault print path against devnet, run before and
after the change, byte-identical
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

solana-client-tools: add Squads vault transaction bundling to the squads module

2 participants