fix(recurring): emit 8-byte payment references - #1751
Conversation
Greptile SummaryThe PR adds EIP-712 hashing support for a batch recurring-payment permit whose legs contain 8-byte payment references, plus digest-parity tests.
Confidence Score: 4/5The batch permit must be connected to signature verification and payment execution before merging because the intended 8-byte references are currently never emitted. The added batch schema can only produce an EIP-712 digest; no reachable function consumes the signed payload or transfers its legs, while the existing payment path remains unchanged. Files Needing Attention: packages/smart-contracts/src/contracts/ERC20RecurringPaymentProxy.sol and packages/smart-contracts/test/contracts/ERC20RecurringPaymentProxy.test.ts Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[SchedulePermitBatch payload] --> B[hashScheduleBatch]
B --> C[EIP-712 digest]
C -. no verification or execution entry point .-> D[ERC20FeeProxy transfer]
E[Existing SchedulePermit plus arbitrary bytes reference] --> F[triggerRecurringPayment]
F --> D
D --> G[TransferWithReferenceAndFee event]
Reviews (1): Last reviewed commit: "fix(recurring): emit 8-byte payment refe..." | Re-trigger Greptile |
| function hashScheduleBatch(SchedulePermitBatch calldata p) public view returns (bytes32) { | ||
| return _hashScheduleBatch(p); | ||
| } |
There was a problem hiding this comment.
When a relayer submits a signed SchedulePermitBatch, this function can only return its digest; no entry point accepts the batch, verifies its signature, or transfers its legs. Consequently, the newly defined 8-byte references cannot be emitted and the existing recurring-payment path remains unchanged.
Knowledge Base Used:
| it('uses an 8-byte payment reference whose fee-proxy topic is not the 32-byte pad', () => { | ||
| const ref8 = ref(0x0a); | ||
| const ref32 = ethers.utils.hexZeroPad(ref8, 32); | ||
| expect(ref8).to.equal('0x000000000000000a'); | ||
| expect(ethers.utils.keccak256(ref8)).to.equal(ethers.utils.keccak256('0x000000000000000a')); | ||
| expect(ethers.utils.keccak256(ref8)).to.not.equal(ethers.utils.keccak256(ref32)); | ||
| }); |
There was a problem hiding this comment.
Emission path remains untested
This test only compares hashes of locally constructed byte values; it never calls triggerRecurringPayment or inspects TransferWithReferenceAndFee. Padding, truncation, or forwarding the wrong reference at the actual event boundary therefore remains undetected.
Knowledge Base Used: Payment lifecycle
|
| Severity | Count | Status |
|---|---|---|
| ✅ High | 0 | Pass |
| ✅ Medium | 0 | Pass |
| 🔵 Low | 0 | Info |
| ℹ️ Informational | 0 | Info |
📄 Full report available in workflow artifacts.
🔍 View detailed findings in the Security tab.
❌ Echidna Fuzzing ResultsMode: ( test sequences) Property Test Results
📄 Full report and corpus available in workflow artifacts. ℹ️ About Echidna FuzzingEchidna is a property-based fuzzer that generates random sequences of transactions Properties tested:
|

EIP-712
SchedulePermitBatchstruct and digest supportAdds a
SchedulePermitBatchsigned permit type toERC20RecurringPaymentProxy, enabling a single subscriber signature to authorize a recurring payment schedule with distinct initial and recurring payment legs.Contract changes
Legstruct (recipient,amount,bytes8 paymentReference) and aSchedulePermitBatchstruct that composes an array of due timestamps alongsideinitialLegsandrecurringLegsarrays._LEG_TYPEHASHand_BATCH_TYPEHASHconstants following EIP-712 referenced-type ordering rules._hashLeg,_hashLegs, and_hashUint32Arrayto produce the correct EIP-712 encoded hashes for dynamic array fields.hashScheduleBatch(SchedulePermitBatch)and the existinghashSchedule(SchedulePermit)as public view functions so off-chain clients and tests can verify digest parity without executing a transaction.Test changes
schedulePermitTypesandschedulePermitBatchTypesEIP-712 type definitions as shared constants and adds aneip712Domainhelper to avoid repetition.hashPermitOffchainandhashBatchOffchainhelpers usingethers._TypedDataEncoderto compute expected digests off-chain.createBatchSignaturehelper that mirrorscreateSignaturefor the batch permit type.EIP-712 digest paritytest suite that verifies the on-chain hash matches the off-chain ethers encoder forSchedulePermit, a fully-populatedSchedulePermitBatch, and a batch with an emptyinitialLegsarray. Also validates thatbytes8payment references hash differently from their zero-paddedbytes32equivalents.