fix(recurring): reject zero-amount legs - #1756
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Greptile SummaryThe PR adds batch recurring-payment execution and rejects zero-amount legs during settlement, while preserving the existing single-payment fee-proxy path.
Confidence Score: 4/5The partial-execution path for schedules with malformed recurring legs should be fixed before merging. A multi-payment permit can settle its valid initial payment before the contract discovers a zero-amount recurring leg, leaving the signed schedule permanently unable to complete. Files Needing Attention: packages/smart-contracts/src/contracts/ERC20RecurringPaymentProxy.sol; packages/smart-contracts/test/contracts/ERC20RecurringPaymentProxy.test.ts Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Relayer submits signed batch permit] --> B{Index 1 and initial legs non-empty?}
B -->|Yes| C[Validate and settle initial legs]
B -->|No| D[Validate and settle recurring legs]
C --> E[Mark payment completed]
D --> E
C -. Recurring legs remain unchecked .-> F[Later recurring trigger]
F --> D
Reviews (1): Last reviewed commit: "fix(recurring): reject zero-amount legs" | Re-trigger Greptile |

Add
triggerRecurringPaymentBatchwith multi-leg settlement supportIntroduces a new
triggerRecurringPaymentBatchentry point onERC20RecurringPaymentProxythat allows a single scheduled payment cycle to fan out token transfers across multiple recipients ("legs") in one atomic transaction.Key changes
Multi-leg settlement
SchedulePermitBatchpermit carries two leg arrays —initialLegs(used only on the first payment) andrecurringLegs(used for all subsequent payments) — each capped atMAX_LEGS = 8._settleLegsiterates the active leg array and dispatches each transfer through the fee proxy with zero fee and a zero fee address._sumAndAssertLegsvalidates that no leg has a zero amount or a zero recipient address, and returns the total to pull from the subscriber.Due-time enforcement
dueTimesmust be a strictly-increasing array whose length equalstotalPayments. The contract validates monotonicity on every call and reverts withERC20RecurringPaymentProxy__InvalidDueTimesif the invariant is broken.Atomicity guarantees
Error surface
ERC20RecurringPaymentProxy__IndexTooLarge(theuint8parameter type already enforces the 0–255 range at the ABI level); the corresponding test was relocated and reframed.ERC20RecurringPaymentProxy__InvalidDueTimes,ERC20RecurringPaymentProxy__TooManyLegs,ERC20RecurringPaymentProxy__EmptyLegs, andERC20RecurringPaymentProxy__ZeroAmount._approveFeeProxygeneralisedIERC20FeeProxyargument so the batch path can pass the cached proxy reference without re-reading storage.Test helpers
ERC20BlockRecipienttest token added; it allows a specific recipient to be blocked so that mid-batch transfer failures can be simulated.