Skip to content

Support parsed-chain secrets in x402 payments - #77

Merged
senamakel merged 14 commits into
mainfrom
wallet-x402-multisecret
Oct 11, 2026
Merged

senamakel merged 14 commits into
mainfrom
wallet-x402-multisecret

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

x402 can now authorize and sign a payment on the chain selected by the parsed payment challenge, even when the wallet’s primary secret is for another chain. The request adds an optional list of additional chain secrets; an empty or omitted list keeps the prior serialized JSON unchanged, and the TinyBus method arity is unchanged. The bus contract version advances to 1.12.

The module rejects duplicate secrets for one chain, selects the secret only after parsing the payment rail, and zeroizes supplied and scoped secret material on error and completion. Existing admission, approval, and signing behavior remains in the module.

API and compatibility

Public wire change: additive additional_secrets, defaulting to empty and omitted during serialization when empty. Existing requests deserialize and serialize to the same JSON. No TinyBus member arity changed.

Validation

  • Reproduced the cross-chain failure against the prior compiled module; the Solana challenge with only the EVM secret failed with the expected chain mismatch.
  • cargo +stable test --locked --all-features passed.
  • cargo +stable build --locked --all-targets --all-features passed.
  • cargo +stable clippy --locked --all-targets --all-features -- -D warnings passed.
  • cargo +stable fmt --all -- --check and git diff --check passed.
  • Rebuilt the release cdylib and ran the actual compiled-module/TinyBus broker lifecycle fixture with EVM and Solana secrets; it passed and confirmed the Solana rail reached the host approval callback.

Detailed local logs are retained in the owner worktree under .migration-validation/.

senamakel and others added 14 commits October 11, 2026 18:57
…nywallet-module/src/service/mod

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…s,crates/tinywallet-module/src/

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the end-to-end lifecycle test with a Solana x402 challenge so the
module must pick the matching secret from the additional secrets and
report the parsed chain through the approval callback. The test host now
answers getLatestBlockhash so the Solana path can be exercised.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Move the Solana challenge preparation and approval assertions later in the
end-to-end test so the EVM payment flow is exercised first. The block is
unchanged in content, only relocated to keep the chain coverage in a
consistent order.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test asserting that an x402 prepare-payment request carrying only the
legacy secret field deserializes with empty additional secrets and round-trips
back to the original JSON, guarding the extension against breaking existing
payloads.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The Solana USDC mint used by the end-to-end signing test was wrong, so the
fixture did not match the real token. It now uses the canonical mint address.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a task-local slot holding the host-provided secrets for module-selected
x402 payment rails, scoped alongside the quote owner, and expose a helper that
clones only the secret matching the chain chosen by the parsed challenge. The
slot zeroizes every mnemonic and derivation path on drop, and the existing
single-secret slot now zeroizes its derivation path too.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…es/tinywallet-module/src/servic

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Raise CONTRACT_VERSION to 1.12 to reflect the current wire contract, and update the compatibility test to expect the new minor version.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Document contract 1.12, which adds optional additional_secrets to the
confidential X402PreparePayment request while preserving the prior JSON shape
when it is omitted or empty. A test now covers rejection of duplicate secrets
for the same chain.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The secret cloned for account derivation is now bound as mutable so it can be zeroized after use, while the secret passed into the signing request is no longer marked mutable since it is moved rather than wiped.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Moved the duplicate-chain validation and secret gathering out of the
prepare-payment path into a take_request_secrets helper that also drains
the secrets from the request. This keeps the secrets owned by the caller
so they can be wiped on every exit path instead of being left behind in
the request struct.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The legacy serialization test no longer asserts that additional_secrets is
empty, since the round-trip equality check already covers the field. The
secrets binding in the x402 runtime is now mutable so it can be consumed
later in the request flow.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T16:28:32.862305Z 35f2c9e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 11, 2026

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a8f4468b-9df5-4b2c-a190-a6bd24281032

📥 Commits

Reviewing files that changed from the base of the PR and between 0ad4220 and 35f2c9e.


📒 Files selected for processing (12)
  • crates/tinywallet-bus/README.md
  • crates/tinywallet-bus/src/lib_tests.rs
  • crates/tinywallet-bus/src/version/mod.rs
  • crates/tinywallet-bus/src/wire/host.rs
  • crates/tinywallet-bus/src/wire/mod_tests.rs
  • crates/tinywallet-module/src/service/mod.rs
  • crates/tinywallet-module/src/service/mod_tests.rs
  • crates/tinywallet-module/src/service/web3.rs
  • crates/tinywallet-module/src/service/x402.rs
  • crates/tinywallet-module/src/service/x402_lifecycle_tests.rs
  • crates/tinywallet-module/src/service/x402_tests.rs
  • crates/tinywallet-module/tests/lifecycle_e2e.rs

 _______________________________________________________
< Coding ain't done 'til all the Tests run. 'Nuff said. >
 -------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@senamakel
senamakel merged commit 549bc15 into main Oct 11, 2026
28 of 29 checks passed
@tinysweeper

tinysweeper Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper completed its review; deterministic results follow.

State: Incomplete
Priority: none
Reviewed head: 35f2c9ec5706
Updated: 2026-10-11T23:17:35Z

Review snapshot

Change surface Files Review signal Count
Production 5 Active findings 0
Tests 6 Noted findings 0
Documentation 1 Resolved findings 0
Configuration 0 Pending checks/questions 10

Completeness: Incomplete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

Contract 1.12 introduces optional `additional_secrets` on the confidential `X402PreparePayment` request (`crates/tinywallet-bus/src/wire/host.rs#pub struct X402PreparePaymentRequest`, `crates/tinywallet-bus/README.md#Contract 1.2 adds stateless ConstructEvmTransaction for native/ERC-20/contract`); the bus contract version advances to 1.12 with matching tests (`crates/tinywallet-bus/src/version/mod.rs#pub const CONTRACT_VERSION`, `crates/tinywallet-bus/src/lib_tests.rs#async fn assert_admission_is_held_through_durable_commit`). The module takes and wipes the request secrets, rejects duplicate per-chain secrets, and runs the x402 flow inside a task-local secret scope that selects the secret matching the parsed challenge chain (`crates/tinywallet-module/src/service/x402.rs#impl PaymentSigner for ModulePaymentSigner`, `crates/tinywallet-module/src/service/web3.rs#pub(crate) async fn with_execution_context<F: Future>`, `crates/tinywallet-module/src/service/web3.rs#pub(super) fn clone_secret() -> Result<SecretMaterial, String> {`). The signer now derives the expected chain from the payment chain and clones only the matching x402 secret. Secret wiping is extended to zeroize `derivation_path` alongside the mnemonic.

Features

None identified with supported citations.

Tests

  • unit — Reserves x402 pending capacity before authorization and releases it afterward.: Covers the pending-ledger admission behavior retained around the new execution scope. (crates/tinywallet-module/src/service/x402_tests.rs#async fn x402_pending_capacity_is_reserved_before_authorization_and_released_on_)

Findings

No active actionable findings.

Could not review: crates/tinywallet-bus/README.md, crates/tinywallet-bus/src/lib_tests.rs, crates/tinywallet-bus/src/wire/mod_tests.rs, crates/tinywallet-module/src/service/mod.rs, crates/tinywallet-module/src/service/web3.rs, crates/tinywallet-module/src/service/x402.rs, crates/tinywallet-module/src/service/x402_tests.rs, crates/tinywallet-module/tests/lifecycle_e2e.rs

Before merge

  • Complete the critique review for crates/tinywallet-bus/README.md, crates/tinywallet-bus/src/lib_tests.rs, crates/tinywallet-module/src/service/mod.rs, crates/tinywallet-module/src/service/web3.rs, crates/tinywallet-module/src/service/x402.rs, crates/tinywallet-module/src/service/x402_tests.rs, crates/tinywallet-module/tests/lifecycle_e2e.rs, crates/tinywallet-bus/src/wire/mod_tests.rs.
  • Complete the security review for crates/tinywallet-module/src/service/x402.rs, crates/tinywallet-module/tests/lifecycle_e2e.rs.

How this fits together

flowchart LR
  n0["..._legacy_json_and_omits_unset_endpoint_url<br/>changed"]:::changed
  n1["X402PreparePaymentRequest<br/>changed"]:::changed
  n2["...ed_sender_and_preserves_default_recipient<br/>changed"]:::changed
  n3["Wallet<br/>changed"]:::changed
  n4["...erations_fail_closed_before_configuration<br/>changed"]:::changed
  n5["X402Runtime<br/>changed"]:::changed
  n6["...fore_authorization_and_released_on_cancel"]:::impacted
  n7["lock"]:::impacted
  n8["json"]:::impacted
  n9["prepare"]:::impacted
  n10["x402_prepare_payment"]:::impacted
  n11["secret"]:::impacted
  n0 -->|calls| n8
  n0 -->|tests| n8
  n2 -->|calls| n8
  n2 -->|tests| n8
  n3 -->|uses| n5
  n4 -->|uses| n3
  n4 -->|calls| n8
  n4 -->|tests| n8
  n6 -->|uses| n1
  n6 -->|uses| n3
  n6 -->|calls| n7
  n6 -->|tests| n7
  n6 -->|calls| n9
  n6 -->|tests| n9
  n6 -->|calls| n10
  n6 -->|tests| n10
  n6 -->|calls| n11
  n6 -->|tests| n11
  n9 -->|uses| n1
  n10 -->|uses| n1
  n10 -->|calls| n9
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: incomplete; unanswered: crates/tinywallet-bus/README.md, crates/tinywallet-bus/src/lib_tests.rs, crates/tinywallet-module/src/service/mod.rs, crates/tinywallet-module/src/service/web3.rs, crates/tinywallet-module/src/service/x402.rs, crates/tinywallet-module/src/service/x402_tests.rs, crates/tinywallet-module/tests/lifecycle_e2e.rs, crates/tinywallet-bus/src/wire/mod_tests.rs
  • Lane summary: Reviewed 4 files; 0 findings. 8 files could not be reviewed: crates/tinywallet-bus/README.md, crates/tinywallet-bus/src/lib_tests.rs, crates/tinywallet-module/src/service/mod.rs, crates/tinywallet-module/src/service/web3.rs, crates/tinywallet-module/src/service/x402.rs, crates/tinywallet-module/src/service/x402_tests.rs, crates/tinywallet-module/tests/lifecycle_e2e.rs, crates/tinywallet-bus/src/wire/mod_tests.rs. _The code index is behind this pull request (indexed at `625aaa366def`), so retrieved context may be out of date._

security

  • Conclusion: Success
  • Scope reviewed: incomplete; unanswered: crates/tinywallet-module/src/service/x402.rs, crates/tinywallet-module/tests/lifecycle_e2e.rs
  • Lane summary: Reviewed 9 files; 0 findings. 2 files could not be reviewed: crates/tinywallet-module/src/service/x402.rs, crates/tinywallet-module/tests/lifecycle_e2e.rs. 1 file was not security-reviewed: crates/tinywallet-bus/README.md (prose or tabular data). _The code index is behind this pull request (indexed at `625aaa366def`), so retrieved context may be out of date._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change adds optional `additional_secrets` to `X402PreparePaymentRequest`, scopes them in a task-local slot that zeroizes on drop, selects the secret matching the parsed challenge's chain in `clone_x402_secret`, and also zeroizes `derivation_path` alongside the mnemonic. Behaviour is covered to a reasonable degree: the bus wire test pins legacy JSON round-tripping when the new field is absent, the module test exercises the duplicate-chain rejection, and the lifecycle E2E test drives a Solana challenge through to the approval callback, so the module-selected-rail path is exercised end to end. I found no defect that meets the bar for reporting. _The code index is behind this pull request (indexed at `625aaa366def`), so retrieved context may be out of date._

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The change adds an optional `additional_secrets` list to the confidential x402 prepare request, selects the secret matching the chain parsed from the challenge, and wipes supplied and scoped secret material (mnemonic and derivation path) on error and completion; the bus contract version advances to 1.12 with matching tests and a new integration case. The description matches the diff and I found no defect that would break a reachable input or contradict the repository rules. _The code index is behind this pull request (indexed at `625aaa366def`), so retrieved context may be out of date._
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.010835
  • Tokens: 1071553 input · 102114 output · 327296 cached · 764 embedding
Head State Pass summary
35f2c9ec5706 incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-11T23:17:35Z)

tinysweeper 0.1.2

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

tinysweeper found nothing blocking, but could not review everything, so this is not an approval: crates/tinywallet-bus/README.md, crates/tinywallet-bus/src/lib_tests.rs, crates/tinywallet-bus/src/wire/mod_tests.rs, crates/tinywallet-module/src/service/mod.rs, crates/tinywallet-module/src/service/web3.rs, crates/tinywallet-module/src/service/x402.rs, crates/tinywallet-module/src/service/x402_tests.rs, crates/tinywallet-module/tests/lifecycle_e2e.rs.

             $0.0108 · 1,071,553 in / 102,114 out · 327,296 cached (31%) · ladder/vectors-oai3, deepseek-v4.1-flash · 764 embedded
critique:    $0.0058 · 398,339 in   / 37,214 out  · 107,392 cached (27%) · deepseek-v4.1-flash
security:    $0.0034 · 535,211 in   / 42,280 out  · 190,720 cached (36%) · deepseek-v4.1-flash
tests:       $0.0010 · 78,823 in    / 15,290 out  · 24,576 cached (31%)  · deepseek-v4.1-flash
description: $0.0003 · 47,919 in    / 4,598 out   · 4,608 cached (10%)   · deepseek-v4.1-flash

@tinysweeper tinysweeper Bot added the priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect. label Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p3 Whenever. Cosmetic, a nicety, or a cleanup with no user visible effect.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant