Skip to content

managed-oracle: add one-signature partial-success proposal batches - #79

Open
chrismaree wants to merge 2 commits into
chrismaree/signed-proposer-uupsfrom
chrismaree/signed-proposer-batch
Open

managed-oracle: add one-signature partial-success proposal batches#79
chrismaree wants to merge 2 commits into
chrismaree/signed-proposer-uupsfrom
chrismaree/signed-proposer-batch

Conversation

@chrismaree

@chrismaree chrismaree commented Sep 8, 2026

Copy link
Copy Markdown
Member

What Changed

  • Stacks directly onto managed-oracle: add UUPS upgradeability to SignedProposer #78 (chrismaree/signed-proposer-uups), which is based on managed-oracle: add Permit2 SignedProposer relay with partial-success batching #77. This diff adds batching to the upgradeable SignedProposer.
  • Adds proposeBatch for one proposer and one collateral token, using a single Permit2 witness signature, nonce, and upfront transfer.
  • Reuses the existing Proposal structure inside signed BatchProposal items. Each item adds a bond-plus-payment maxAmount; the ordered item budgets must sum exactly to the permit amount.
  • Executes each funded item in a restricted external self-call so failures roll back independently. Only successful bonds and payments are charged, with one final refund of unused funds.
  • Returns per-item success flags and emits bounded failure metadata and a batch summary.
  • Documents why the witness/budget pass must precede funding and execution; retains both loops inline. Uses a memory payment array to avoid a Solidity stack-depth error without changing the ABI.
  • Establishes the maximum-size calldata boundary: 15 proposals with 8,139-byte ancillary data fit (128,612 bytes); 16 exceed Bor’s 128 KiB transaction limit (137,156 bytes).
  • Adds real-Permit2 and unit coverage plus a standard Permit2 SDK/ethers signing example in the deployment documentation. No storage fields or initializer changes are introduced.

Why

  • A user proposing on several markets should approve one typed-data signature rather than a separate Permit2 signature for every market.
  • Partial success lets available proposals succeed when another proposal collides, has a mismatched currency, exceeds its spending/payment limits, or otherwise reverts.
  • Individual signed budgets preserve the spending isolation previously supplied by individual permits. No Merkle tree or persistent fill tracking is needed.

Impact

  • Existing propose and tryMulticall entry points and the single-proposal witness remain available. tryMulticall still accepts only propose calls.
  • The existing delegated-proposer role gates proposeBatch. The funded execution helper rejects external callers, including calls routed through public multicall entry points.
  • A completed batch consumes its nonce even if every item fails. Failed-item retries require a fresh signature and unused nonce.
  • Invalid signatures, malformed aggregate budgets/array lengths, funding failures, final-refund failures, or an outer out-of-gas revert undo the entire transaction, including the shared Permit2 nonce and successful children.
  • Per-item request currency mismatches and excessive payments fail only that item. The final refund excludes pre-existing contract balances.

High risk Sections to review with detail

  • Typed-data authorization: check the nested EIP-712 type ordering, array hashing, unchanged proposal fields, signed individual budgets, and Permit2 binding of token, total amount, spender, nonce, and deadline.
  • Accounting and isolation: review bounded per-oracle allowances, actual balance-delta bond accounting, rollback of failed child transfers/whitelist changes/approvals, payment retention only on success, and final refunds without spending old balances.
  • Call boundary and reentrancy: the helper is self-call-only and the outer batch keeps the reentrancy guard active. Single-proposal execution retains its immediate refund while funded batch execution defers refunds.
  • Gas behavior: as with tryMulticall, child gas and revert data are not capped. A child can starve later work or cause an outer revert. The new capacity test verifies 15 successful proposals at maximum ancillary size, including temporary whitelist changes. The 16-item encoding exceeds Bor’s transaction-size limit. These sizes assume a 65-byte EOA signature and an ordinary transaction envelope; contract-wallet signatures and access lists require separate sizing.
  • Compatibility: no new persistent storage; single-proposal witness hashing and refund semantics are preserved, but shared execution code changes require regression validation.

Validation

  • Foundry 1.3.6: forge test --match-contract 'SignedProposer(Test|Permit2Test)' -vv81 passed, 0 failed, including 256 fuzz cases for signed-field/budget/order tampering.
  • forge test --isolate --match-contract SignedProposerPermit2Test --match-test test_proposeBatch_maxAncillaryData_polygonCapacity -vvpassed with separate transaction contexts.
  • Capacity coverage uses 8,139 all-nonzero ancillary bytes per proposal, a 65-byte signature, real Permit2 bytecode, the UUPS relay, Managed OO, and temporary whitelist insertion/removal. All 15 proposals execute within the 53,902,641 reference gas limit after reserving intrinsic gas. Calldata is 128,612 bytes for 15 and 137,156 for 16; 15 leaves 2,460 bytes for the transaction envelope.
  • Existing single-proposal, tryMulticall, upgrade, authorization, and reentrancy regression coverage passes alongside the new batch tests. New coverage includes successful/mixed/all-failed batches, replay, individual budgets, currency mismatch, exact receipt, refund rollback, and a gas-exhausting child.
  • Fixed an ambiguous expectRevert overload uncovered by compilation. No test assertions were weakened.
  • Passed pinned formatting checks and git diff --check.
  • Root dependencies initialized with recursive submodules and yarn install --frozen-lockfile.
  • Capacity validation is local against the repository’s pinned Polygon gas reference, not a live Polygon transaction or node acceptance test. The UI SDK example has not been runtime-tested. CI has not been awaited.

TL;DR: One user signature funds an ordered same-token batch; successful proposals persist, failed attempts are refunded, and the PR targets #78.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 8, 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-09-08T17:22:19.313869Z 3332f8a 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.

@md0x md0x 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 batching design, EIP-712 witness encoding, budget/refund accounting, reentrancy surface, and test coverage at a4003bc. The witness type strings compose correctly with Permit2, per-item budgets are enforced via scoped allowances with balance-diff accounting, and the only-self execution boundary plus the shared reentrancy guard hold across the paths I could construct. Tests are thorough, including real-Permit2 signature verification, tamper fuzzing, refund-failure atomicity, gas exhaustion, and Polygon capacity bounds. No blockers found.

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