Skip to content

feat(payment): single-approval merkle payments via payForMerkleTrees with legacy fallback - #217

Draft
Nic-dorman wants to merge 1 commit into
mainfrom
feat/single-approval-merkle-payments
Draft

feat(payment): single-approval merkle payments via payForMerkleTrees with legacy fallback#217
Nic-dorman wants to merge 1 commit into
mainfrom
feat/single-approval-merkle-payments

Conversation

@Nic-dorman

Copy link
Copy Markdown
Member

Linear issue

V2-991 — https://linear.app/autonominetwork/issue/V2-991 (parent V2-949)

What

One wallet confirmation per ≤4 merkle trees (≈4 GiB) instead of one per tree, with a runtime fallback that keeps every released vault working:

  • utils/payment.ts: new payForMerkleTrees(config, batches) — single writeContract on the batched entry point, single receipt, all MerklePaymentMade events decoded in log order (the contract emits one per tree in input order, so index i belongs to tree i) → winnerPoolHashes aligned to input; throws on event-count mismatch. extractMerklePaymentEvents is shared with the legacy payForMerkleTree (which previously stopped at the first event).
  • Feature probe batchedMerkleTreesPerTx(): reads the vault's MAX_TREES_PER_PAYMENT() getter once per chain+vault. Old deployments revert → 0 → legacy per-tree loop, cached (definitive). Transport failures are not cached — the current upload degrades to legacy, the next one re-probes. Supported vaults return min(MERKLE_TREES_PER_PAYMENT = 4, on-chain max).
  • stores/files.ts: the merkle payment loop iterates contiguous groups (merklePaymentGroups) — batched call for multi-tree groups, legacy call for singletons (including a trailing remainder group, e.g. 5 trees → 4 batched + 1 legacy). stageDone/stageTotal now count wallet confirmations, so the existing approve_payment_n_of_m / approve_in_wallet label logic and locale strings carry over with correct semantics — no i18n changes needed. Single-tree uploads skip the probe RPC entirely.
  • Partial semantics: the batched call is atomic per group — a rejected/failed group pays nothing and its trees stay null, so the existing k-of-N partial-finalize path (confirm_upload_merkle with nulls) works unchanged at group granularity. ensureAllowanceForMerkleBatches still runs once up front over all batches.
  • ABI: assets/abi/IPaymentVault.json gains payForMerkleTrees + MAX_TREES_PER_PAYMENT (from evmlib's ABI on feat(payment): pay N merkle trees in one tx (payForMerkleTrees) evmlib#15). Harmless against old vaults.

Compatibility / rollout

No Rust/src-tauri changes — this is frontend-only and has no build dependency on the evmlib release chain. Against today's production vaults the probe fails and behavior is byte-identical to 0.9.7 (legacy loop). The batched path activates automatically when the V2-992 vault redeployment lands. Safe in either release order by construction.

Test evidence

  • npx vitest run: 87/87 across 8 files, including new coverage: real-ABI event log round-trip (encode via viem/utils, decode through the production path) asserting log-order alignment and count-mismatch failure; probe clamp/caching/transport semantics; grouping math; and a store-level flow test (5 trees → one batched 4-tree call + one legacy singleton → confirm_upload_merkle receives all five winner hashes on the right positions).
  • npx nuxi typecheck clean.
  • Test-infra note: the global viem shim now forwards a real decodeEventLog (from unmocked viem/utils) — the receipt-decoding path was previously untestable under the identity-shim mock.

Draft until

  1. evmlib#15 lands (parent contract change) and the cap value is frozen — deliberately deferred validation: a real-wallet Arbitrum Sepolia run pushing a ~70–85 KiB batched payload through the WalletConnect relay + MetaMask (the relay/wallet calldata-size behavior is the one thing unit tests can't prove).
  2. On-device sanity pass of the n-of-m confirmation UX with a multi-group upload.

Mitigation / rollback

Revert = back to the per-tree loop. At runtime the probe's transport-failure path already degrades to legacy per upload; a vault without the getter never takes the batched path at all.

🤖 Generated with Claude Code

…with legacy fallback

Multi-tree uploads now settle in groups of up to MERKLE_TREES_PER_PAYMENT
(4) trees per payForMerkleTrees transaction — one wallet confirmation per
~4 GiB instead of one per ~1 GiB tree (V2-949). The vault is feature-probed
once per upload via its MAX_TREES_PER_PAYMENT() getter (definitive answers
cached per chain+vault, transport failures not cached); old deployments
keep the legacy per-tree loop unchanged. Winner hashes align to trees by
log order — the contract emits one MerklePaymentMade per tree in input
order — and a failed group pays nothing (atomic), so the existing
partial-finalize path carries over at group granularity. stageDone/
stageTotal now count wallet confirmations, not trees.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.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.

1 participant