Skip to content

Add Sputnik DAO multisig commands for ML-DSA-65 wallets - #172

Merged
illuzen merged 2 commits into
yuvi/ml-dsa-65-cold-nearfrom
yuvi/near-dao
Sep 29, 2026
Merged

illuzen merged 2 commits into
yuvi/ml-dsa-65-cold-nearfrom
yuvi/near-dao

Conversation

@illuzen

@illuzen illuzen commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

Stacked on #171 (base branch is yuvi/ml-dsa-65-cold-near; retarget to main after that merges).

Summary

Implements the NEAR-multisig phase of the plan: a NEAR account solely controlled by a Quantus ML-DSA-65 wallet can now act as a co-signer in a Sputnik DAO treasury — the multisig contract behind Trezu (trezu.org), currently the most popular treasury manager on NEAR. Sputnik authorizes members by account ID and NEAR authenticates the account via its access key, so a post-quantum member needs no changes on the DAO or Trezu side; votes made from this CLI show up in the Trezu UI.

New commands:

  • quantus near dao propose-transfer --dao <dao> --account <member> --wallet <w> --receiver <r> --amount <NEAR> — calls add_proposal with a base-NEAR Transfer kind. The required bond is read from the DAO policy's proposal_bond (overridable with --bond), and the returned proposal id is decoded from the transaction's SuccessValue and printed with a ready-to-copy vote command.
  • quantus near dao vote --dao <dao> --id <n> --vote approve|reject|remove ... — calls act_proposal with 300 Tgas (a threshold-meeting approval executes the proposal in the same call), then shows the proposal's resulting state.
  • quantus near dao proposal --dao <dao> --id <n> — view a proposal's status, votes, and kind (no wallet needed).

Supporting changes:

  • src/near/protocol.rs: Action::FunctionCall is now constructed (was a tag-position placeholder); borsh layout pinned by a hand-built golden test.
  • src/near/rpc.rs: call_view_function (query/call_function with base64 JSON args, byte-array result decoding) and decode_success_value for transaction return values.

Testing

  • 387 lib tests pass (7 new: FunctionCall borsh golden, view-result decoding, SuccessValue decoding, vote-action mapping, Sputnik proposal-args schema, policy bond parsing).
  • ./clippy.sh clean.
  • quantus near dao --help and subcommand help smoke-tested. Not yet exercised against a live DAO on testnet.

Made with Cursor

quantus near dao propose-transfer / vote / proposal let a NEAR account
controlled by a Quantus ML-DSA-65 key act as a co-signer in a Sputnik DAO
treasury (the contract behind Trezu). Adds FunctionCall action encoding,
a call_function view RPC, and SuccessValue decoding for proposal ids.

Co-authored-by: Cursor <cursoragent@cursor.com>
@illuzen illuzen added the bot-review Request automated review from review-bot label Sep 29, 2026

@n13 n13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewer model: GPT-6 Sol

Verdict: Request changes. The new vote command is incompatible with Sputnik DAOs running the current contract.

  • [P1] Supply the proposal kind to act_proposal (src/cli/near.rs:781). The current Sputnik DAO contract requires a proposal: ProposalKind argument and checks it against the stored proposal. This command submits only id and action, so a vote against a DAO using that contract fails argument decoding before it can be recorded. Read get_proposal before signing, extract its kind, and include that value as proposal; reject a missing or malformed kind. The older two-argument interface shown in the repository README does not cover upgraded DAOs.

Validation: SKIP_CIRCUIT_BUILD=1 cargo test --locked --lib near:: passed (22 tests). GitHub format, Clippy/doc, and security checks passed at review time; build matrix and examples were still pending. I did not submit a signed transaction to a live DAO.

@n13 n13 removed the bot-review Request automated review from review-bot label Sep 29, 2026
The current Sputnik contract requires act_proposal(id, action, proposal,
memo) and re-checks the kind against the stored proposal, so the vote
command now reads get_proposal first and includes its kind.

Co-authored-by: Cursor <cursoragent@cursor.com>
@illuzen

illuzen commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the P1: near dao vote now reads get_proposal before signing, extracts the stored kind (rejecting a missing or null kind), and includes it as the proposal argument to act_proposal — matching the current contract's act_proposal(id, action, proposal, memo) signature and its ERR_WRONG_KIND check. I verified the signature against sputnikdao2/src/proposals.rs. memo is omitted, which serde deserializes as None. A side benefit: the voter now sees the proposal's status, description, and kind printed before the vote is signed. Added a unit test for the kind extraction and vote-args shape; ./clippy.sh clean, full lib suite passes (388 tests).

@illuzen
illuzen requested a review from n13 September 29, 2026 11:23

@n13 n13 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewer model: GPT-6 Sol

Verdict: Approve. The follow-up commit resolves the earlier blocker: near dao vote reads the stored proposal kind and passes it as proposal to act_proposal, matching the current Sputnik contract. I found no remaining blocking issues in the full PR diff against the stacked base.

Validation: SKIP_CIRCUIT_BUILD=1 cargo test --locked --lib near:: passed (23 tests); git diff --check passed. All seven PR checks completed successfully at 9e3f0cbd8e318bcefa7ba2559081bc14f3096a54. No signed transaction was submitted to a live DAO.

@illuzen
illuzen merged commit 972fc5d into yuvi/ml-dsa-65-cold-near Sep 29, 2026
7 checks passed
illuzen added a commit that referenced this pull request Sep 29, 2026
* Add Sputnik DAO multisig commands for ML-DSA-65 wallets

quantus near dao propose-transfer / vote / proposal let a NEAR account
controlled by a Quantus ML-DSA-65 key act as a co-signer in a Sputnik DAO
treasury (the contract behind Trezu). Adds FunctionCall action encoding,
a call_function view RPC, and SuccessValue decoding for proposal ids.



* Pass the stored proposal kind to act_proposal

The current Sputnik contract requires act_proposal(id, action, proposal,
memo) and re-checks the kind against the stored proposal, so the vote
command now reads get_proposal first and includes its kind.



---------

Co-authored-by: Cursor <cursoragent@cursor.com>
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.

2 participants