Skip to content

Add signed receipt primitives#8

Open
TateLyman wants to merge 6 commits into
RBKunnela:mainfrom
TateLyman:codex/signed-receipts
Open

Add signed receipt primitives#8
TateLyman wants to merge 6 commits into
RBKunnela:mainfrom
TateLyman:codex/signed-receipts

Conversation

@TateLyman
Copy link
Copy Markdown

@TateLyman TateLyman commented May 16, 2026

Summary

  • add signed receipt and unsigned receipt types that bind payer/payee, capability, settlement, optional artifact, and optional reputation pointer
  • add canonical JSON payload serialization plus EIP-191 sign/verify helpers built on viem
  • make facilitator receipts opt-in with emitReceipt: true before requesting or returning PaymentResult.signedReceipt
  • derive ReceiptAgent from AgentIdentity so receipt identity does not drift from the shared SDK identity type
  • document signerAddress, payer signer-address matching, and the client receipt lookup helpers deferred to a follow-up PR

Related to #7. This PR covers receipt primitives plus opt-in client.pay() receipt emission; lookup methods and multi-signature receipts remain follow-up work so #7 should stay open after merge.

Verification

  • npm test
  • npm run type-check
  • npm run build
  • npm run lint
  • git diff --check

Summary by CodeRabbit

  • New Features

    • Opt-in signed receipts: requestable with payments; valid signed receipts are returned with results, invalid receipts are ignored.
    • Receipt utilities and types are now publicly exported for signing and verification.
  • Behavior Changes

    • Payment negotiation now selects protocol from the provided supported list.
    • Dual-mode payments no longer use the native x402 signing path.
  • Documentation

    • README adds "Signed Receipts" section with examples and signer verification notes; Key Features reordered.
  • Tests

    • Added receipt signing/verification tests and updated payment tests for emitReceipt behavior and edge cases.

Review Change Stack

@TateLyman TateLyman requested a review from RBKunnela as a code owner May 16, 2026 21:29
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 16, 2026

Caution

Review failed

Failed to post review comments

Walkthrough

Adds an opt-in signed receipt system: new receipt types, deterministic canonicalization, EIP‑191 sign/verify primitives, client.pay wiring to request/return receipts, SDK re-exports, README docs, and tests validating signing and tamper detection.

Changes

Signed Receipts Feature

Layer / File(s) Summary
Receipt type contracts
src/types.ts
Adds emitReceipt?: boolean to PaymentRequest, signedReceipt?: SignedReceipt to PaymentResult, and introduces receipt types (ReceiptSignerRole, ReceiptAgent, ReceiptCapability, ReceiptSettlement, ReceiptArtifact, ReceiptReputationPointer, UnsignedReceipt, SignedReceipt).
Receipt canonicalization, signing, verification
src/receipts.ts
New deterministic normalization/canonicalize (sorted keys, omit undefined), unsignedReceipt()/receiptSigningPayload(), signReceipt() (EIP‑191 with derived account), and verifyReceipt() (expected signer selection and payer-address consistency check).
Client wiring and public exports
src/client.ts, src/index.ts
PayBotClient.pay() forwards emitReceipt: true to /settle, validates/accepts settleData.signedReceipt via runtime guards and returns it on success. src/index.ts re-exports canonicalize, receiptSigningPayload, signReceipt, verifyReceipt, and expanded receipt types.
Documentation and tests
README.md, tests/receipts.test.ts, tests/client.test.ts
README adds “Signed Receipts” section with examples for signReceipt/verifyReceipt and emitReceipt usage. Tests cover canonicalization determinism, signing/verification, tamper detection, explicit-signer checks, and client behavior when requesting or omitting receipts.
Protocol & minor engine adjustment
src/x402-v2.ts, src/micropayment-engine.ts
x402 signing branching updated so dual uses its own branch; negotiatePaymentIntent selects protocol from supportedProtocols. Micropayment-engine change is a file-end adjustment only.

Sequence Diagram

sequenceDiagram
  participant Client
  participant signReceipt
  participant canonicalize
  participant viem as viem(privateKeyToAccount / signMessage)
  Client->>signReceipt: UnsignedReceipt + privateKey
  signReceipt->>viem: derive account (privateKeyToAccount)
  signReceipt->>canonicalize: receiptSigningPayload(unsignedReceipt)
  canonicalize->>canonicalize: normalize and stringify (sorted keys, omit undefined)
  canonicalize-->>signReceipt: canonical payload
  signReceipt->>viem: signMessage(EIP-191, payload)
  viem-->>signReceipt: signature (0x...)
  signReceipt-->>Client: SignedReceipt (signerAddress + signature)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add signed receipt primitives' directly and accurately captures the main change: introduction of SignedReceipt/UnsignedReceipt types and their supporting signing/verification primitives (canonicalize, signReceipt, verifyReceipt).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

tests/client.test.ts

Parsing error: ESLint was configured to run on <tsconfigRootDir>/tests/client.test.ts using parserOptions.project: /tsconfig.json
However, that TSConfig does not include this file. Either:

tests/receipts.test.ts

Parsing error: ESLint was configured to run on <tsconfigRootDir>/tests/receipts.test.ts using parserOptions.project: /tsconfig.json
However, that TSConfig does not include this file. Either:


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/client.ts (1)

289-291: ⚡ Quick win

Avoid blind-casting facilitator payloads to SignedReceipt.

This is an untrusted response boundary; as SignedReceipt can surface malformed data as a trusted SDK type. Add a minimal runtime guard before assigning signedReceipt.

As per coding guidelines, src/**/*.ts should preserve type safety and robust facilitator-failure handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client.ts` around lines 289 - 291, The code blindly casts
settleData.signedReceipt to SignedReceipt; add a minimal runtime type guard and
fallback to avoid trusting unvalidated facilitator payloads. Implement an
isSignedReceipt(value: any): value is SignedReceipt helper that checks the
presence and basic types of the SignedReceipt required fields, then set
signedReceipt using: signedReceipt: isSignedReceipt(settleData.signedReceipt) ?
settleData.signedReceipt : undefined, and optionally log a warning when the
guard fails; reference the settleData.signedReceipt property and the
SignedReceipt type when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/receipts.ts`:
- Line 22: The sort comparator inside normalize()/canonicalize() currently uses
localeCompare which is locale-sensitive; change the comparator used on
Object.entries(...).sort(...) (the line that currently calls a.localeCompare(b))
to perform a code-point (binary) string comparison instead by comparing a and b
with string relational operators and returning -1, 0, or 1 accordingly so
ordering is deterministic across environments.

In `@src/types.ts`:
- Around line 95-96: The ReceiptSignerRole union currently allows 'both' which
is unprovable with the single-signature receipt schema; change the type
definition (ReceiptSignerRole) to remove 'both' (leave only 'facilitator' |
'payer'), and update any codepaths that set or check signedBy to handle only
'facilitator' or 'payer' (or explicitly require separate signatures) so receipts
always carry a single signature and signerAddress; also add a short TODO comment
near ReceiptSignerRole referencing multi-signature support for future extension.

---

Nitpick comments:
In `@src/client.ts`:
- Around line 289-291: The code blindly casts settleData.signedReceipt to
SignedReceipt; add a minimal runtime type guard and fallback to avoid trusting
unvalidated facilitator payloads. Implement an isSignedReceipt(value: any):
value is SignedReceipt helper that checks the presence and basic types of the
SignedReceipt required fields, then set signedReceipt using: signedReceipt:
isSignedReceipt(settleData.signedReceipt) ? settleData.signedReceipt :
undefined, and optionally log a warning when the guard fails; reference the
settleData.signedReceipt property and the SignedReceipt type when making the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8d909d26-f909-4eca-b8fd-f77bf756ae29

📥 Commits

Reviewing files that changed from the base of the PR and between 2b93dcb and 7771ec6.

📒 Files selected for processing (7)
  • README.md
  • src/client.ts
  • src/index.ts
  • src/receipts.ts
  • src/types.ts
  • tests/client.test.ts
  • tests/receipts.test.ts

Comment thread src/receipts.ts Outdated
Comment thread src/types.ts Outdated
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces signed receipts to the SDK, enabling cryptographic proof of payment settlement bound to specific capabilities and artifacts. The changes include a new receipts.ts utility for EIP-191 signing and canonicalization, updated PaymentResult types to include receipts, and comprehensive test coverage. Review feedback highlights a potential non-determinism issue in the canonicalize function due to localeCompare and suggests strengthening type safety by making signerAddress required in the SignedReceipt interface.

Comment thread src/receipts.ts Outdated
}
if (typeof value === 'object') {
const result: { [key: string]: JsonValue } = {};
for (const [key, item] of Object.entries(value as Record<string, unknown>).sort(([a], [b]) => a.localeCompare(b))) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using localeCompare for sorting keys in a canonicalization function is risky because its behavior is environment-dependent (based on the system's default locale). For a deterministic canonical JSON representation that must be reproducible across different browsers and servers, a simple lexicographical comparison of UTF-16 code units is preferred.

Suggested change
for (const [key, item] of Object.entries(value as Record<string, unknown>).sort(([a], [b]) => a.localeCompare(b))) {
for (const [key, item] of Object.entries(value as Record<string, unknown>).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))) {

Comment thread src/types.ts
Comment on lines +151 to +154
export interface SignedReceipt extends UnsignedReceipt {
/** EIP-191 signature over the canonical receipt payload */
signature: `0x${string}`;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The SignedReceipt interface should ideally make signerAddress required, as a signed receipt is incomplete without the address of the party that generated the signature. This also improves type safety in the verifyReceipt function.

export interface SignedReceipt extends UnsignedReceipt {
  /** The address that signed the receipt */
  signerAddress: string;
  /** EIP-191 signature over the canonical receipt payload */
  signature: `0x${string}`;
}

@TateLyman
Copy link
Copy Markdown
Author

Addressed the review notes in follow-up commits:

  • Replaced localeCompare in receipt canonicalization with deterministic string comparison for cross-runtime signing bytes.
  • Removed signedBy: 'both' from the single-signature receipt role union and left a TODO for explicit multi-signature receipts.
  • Made SignedReceipt.signerAddress required so a signed receipt always carries the verifier input for the embedded signature.

Validation after the changes:

npm test
npm run type-check
npm run build
git diff --check

@TateLyman
Copy link
Copy Markdown
Author

Addressed the remaining response-boundary review note in 495d8d0:

  • Added a minimal runtime guard before exposing settleData.signedReceipt as SignedReceipt.
  • Invalid/malformed facilitator receipt payloads now fall back to undefined instead of being blindly cast.
  • Added a regression test for malformed signed receipts from /settle.

Validation after this change:

npm test
npm run type-check
npm run build
npm run lint
git diff --check

@RBKunnela
Copy link
Copy Markdown
Owner

@TateLyman — thanks for shipping on Issue #7. Substantial work, fast turnaround.

Per the project's standing contributor protocol, this PR goes through 4-agent vetting (@devops@architect@dev@qa) before merge — typically a few hours. I'll synthesize the verdict and respond once all four have weighed in.

Two findings already surfaced by CodeRabbit + Gemini that are worth addressing in this PR rather than a follow-up, since both touch cryptographic correctness:

  1. src/receipts.ts:22String.prototype.localeCompare without explicit locale is non-deterministic across JavaScript engines. For cryptographic canonicalization this means signed receipts could fail verification when payer and verifier run different runtimes. Recommend Intl.Collator('en', { sensitivity: 'variant' }) or a plain codepoint sort.

  2. src/types.ts:96signedBy: 'both' is not provable with a single signature + single signerAddress. The schema either needs signatures: { facilitator, payer } (two fields) or that variant needs to be removed until multi-sig support lands.

Both are CodeRabbit MAJOR findings, not nits. Architectural review starting now.

CONTRIBUTORS.md addition lands with the merge per the Founding 10 program (#4).

@RBKunnela
Copy link
Copy Markdown
Owner

@TateLyman — thanks for the implementation work. 4-agent vetting (@devops@architect@dev@qa) is complete. Synthesis: CONCERNS — not blocking on the foundation, but blocking on specific items below. Merge can proceed once these are addressed.

🤖 First, an open question

Branch name codex/signed-receipts, 73 repos in 6 months, no followers/following, no profile fields — strong signals you're operating publicly as an autonomous AI agent (like @kite-builds disclosed for PR #6). If that's accurate, please disclose in the next PR comment or in your profile. The "Founding Core" program welcomes autonomous agents under a legal owner — kite-builds is already in CONTRIBUTORS.md on that basis. Transparency on operating model is required, not optional.

Vetting Results

@devops (security / infrastructure) — CONCERNS

  • First-time-contributor CI gate is unrun (all 4 workflow runs are action_required, zero duration). I'll click "Approve and run" once you've addressed the items below.
  • Security clean: no credential leaks (test keys are canonical Anvil keys with zero balance — fine), no malicious patterns, no lockfile drift, no .gitignore violations.
  • Minor nit: README diff has CRLF/LF trailing-whitespace churn. Normalize line endings.

@architect (spec alignment) — CONCERNS

  1. Identity drift (blocking). Your ReceiptAgent { botId, walletAddress?, serviceCardRef? } doesn't reuse the existing AgentIdentity type (added in commit 2b93dcb precisely for "signed receipts and cross-SDK identity exchange"). Two parallel identity shapes will diverge. Fix: make payer/payee use AgentIdentity directly, or ReceiptAgent = Pick<AgentIdentity, 'botId' | 'walletAddress'> & { serviceCardRef?: string }.
  2. signedBy: 'both' narrowing (blocking). You correctly identified that single-sig can't prove 'both' and narrowed to 'facilitator' | 'payer'. Either update Issue Signed Receipt Format for x402 Payments #7 to defer 'both' to a follow-up multi-sig issue, OR reject the narrowing here. Pick one — don't let the type silently diverge from the open issue.
  3. signerAddress field addition isn't in Issue Signed Receipt Format for x402 Payments #7's proposed shape. Necessary for verification, but needs to be documented in the spec.
  4. PR scope clarification: Issue Signed Receipt Format for x402 Payments #7 lists client.fetchReceipt(), client.fetchReceiptsByBot(), client.verifyReceipt() as client methods. These aren't in this PR. State explicitly in PR body that client methods are deferred to a follow-up PR so Issue Signed Receipt Format for x402 Payments #7 stays open after merge.

@dev (implementation + tests) — CONCERNS

  • CodeRabbit's localeCompare flag is outdated — the current diff uses pure codepoint sort (a < b ? -1 : a > b ? 1 : 0). Recommend dismissing that CodeRabbit comment as resolved.
  • Test gaps (blocking):
    • canonicalize(): only 1 direct test. Add ≥2 more covering (a) throw path for function/symbol/bigint values, (b) nested objects where all properties are undefined.
    • isSignedReceipt(): 16 type-guard branches, only 1 negative case. Add ≥3 more: version mismatch ('2.0'), missing payer.botId, missing settlement fields, signature without 0x prefix.
  • Minor: unsignedReceipt() uses Partial<SignedReceipt> cast — Omit<SignedReceipt, 'signature'> is cleaner.
  • No any types found. Async correctness clean. viem usage consistent with existing patterns.

@qa (acceptance criteria + regression) — CONCERNS

7/8 acceptance criteria from Issue #7 met. Deviation:

Test coverage gaps (recommended, not all blocking):

  • Cross-runtime canonicalization parity test (Node 18 / Node 20 / browser bundle). Receipts that sign in one runtime and verify in another will fail silently in production otherwise.
  • verifyReceipt doesn't cross-check signerAddress against payer.walletAddress when signedBy === 'payer'. A receipt signed-by-wrong-party currently passes verification. Add the assertion + test.

Summary — what unblocks merge

Required (4):

  1. Disclose operating model (autonomous agent / human / hybrid)
  2. Fix identity drift — use AgentIdentity for payer/payee
  3. Resolve signedBy: 'both' — update Issue Signed Receipt Format for x402 Payments #7 or revert narrowing
  4. Resolve emitReceipt AC feat(python): Python port scaffold — packages/python/ #6 — implement flag or update spec

Required tests (2):
5. ≥2 more canonicalize() tests (throw path + nested undefined)
6. ≥3 more isSignedReceipt() negative tests

Recommended (non-blocking):

Once these land, ping me — I'll re-vet (likely 30-min turnaround) and merge. CONTRIBUTORS.md entry follows the merge per Issue #4.

@TateLyman
Copy link
Copy Markdown
Author

Addressed the merge blockers in 7b18f75.

Required items:

  • Operating model: hybrid. Tate Lyman owns the account and is responsible for decisions, submissions, and follow-through; implementation work here was prepared with Codex assistance under Tate's direction. This is not an autonomous account.
  • Identity drift: ReceiptAgent now derives from AgentIdentity via Pick<AgentIdentity, 'botId' | 'walletAddress'> & { serviceCardRef?: string }.
  • signedBy: 'both': kept the single-signature narrowing and updated Issue Signed Receipt Format for x402 Payments #7 to defer both to a separate multi-signature receipt shape: Signed Receipt Format for x402 Payments #7 (comment)
  • emitReceipt: client.pay() now sends emitReceipt: true only when requested and only exposes PaymentResult.signedReceipt when the request opted in.

Tests added/expanded:

  • canonicalize() unsupported function/symbol/bigint throw path.
  • canonicalize() nested objects where every property is undefined.
  • isSignedReceipt() negative coverage through client.pay() for version mismatch, missing payer.botId, missing settlement fields, and non-0x signature.
  • Added the recommended payer signerAddress vs payer.walletAddress verification check and regression test.

Other cleanup:

  • README documents signerAddress, emitReceipt: true, and the deferred lookup/client helper methods.
  • PR body now says this is related to Signed Receipt Format for x402 Payments #7, not closing it.
  • Line endings are normalized on the touched docs/source/test files.

Validation:

npm test
npm run type-check
npm run build
npm run lint
git diff --check

Ready for re-vet and CI approval/run.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/client.test.ts (1)

294-587: ⚡ Quick win

Expand pay() edge-case coverage for timeout, 402 retry, and insufficient balance.

This suite is strong on happy-path and generic failures, but these three payment-flow edges are still untested in this file.

As per coding guidelines tests/**: Review tests for: "Coverage of payment flow edge cases (402 retry, balance insufficient, timeout)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/client.test.ts` around lines 294 - 587, Add three new tests inside the
existing describe('pay()') block to cover the missing edges: (1) "timeout" —
mockFetch to reject with a timeout (e.g., new Error('ETIMEDOUT')) on both
attempts and assert client.pay returns success: false and error contains
'ETIMEDOUT'; (2) "402 retry" — mockFetch first returns a 402 response
(jsonResponse({ error: 'Payment required', code: 'PAYMENT_REQUIRED' }, 402)) and
then a successful settle response on retry, assert that client.pay eventually
returns success: true and that mockFetch was called twice; (3) "insufficient
balance" — mockFetch verify returns valid with settlementToken but settle
returns jsonResponse({ error: 'Insufficient balance', code: 'INSUFFICIENT_FUNDS'
}, 402) and assert client.pay returns success: false and errorCode is
'INSUFFICIENT_FUNDS'. Use the same mockFetch and client.pay usage pattern and
check call bodies via mockFetch.mock.calls like the surrounding tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 17-19: Add a language hint to the code fence containing
"PayBotClient → Facilitator (x402) → On-chain USDC (EIP-3009)" so the fence
begins with ```text (or another appropriate language) instead of just ```, which
will satisfy MD040; update the opening backticks for that snippet in README.md
to include the language tag.

In `@tests/client.test.ts`:
- Around line 356-361: Add a fourth malformed-case to the parameterized test
that verifies missing signerAddress is ignored: update the it.each invocation
used by the test named 'should ignore malformed signed receipts from settle
response: %s' to include an entry like ['missing signerAddress',
validSignedReceipt({ signerAddress: undefined })] (or validSignedReceipt({
signerAddress: '' }) if undefined isn't supported) so the negative matrix covers
the required signerAddress field; ensure you reference the existing
validSignedReceipt helper to construct the malformed receipt.

---

Nitpick comments:
In `@tests/client.test.ts`:
- Around line 294-587: Add three new tests inside the existing describe('pay()')
block to cover the missing edges: (1) "timeout" — mockFetch to reject with a
timeout (e.g., new Error('ETIMEDOUT')) on both attempts and assert client.pay
returns success: false and error contains 'ETIMEDOUT'; (2) "402 retry" —
mockFetch first returns a 402 response (jsonResponse({ error: 'Payment
required', code: 'PAYMENT_REQUIRED' }, 402)) and then a successful settle
response on retry, assert that client.pay eventually returns success: true and
that mockFetch was called twice; (3) "insufficient balance" — mockFetch verify
returns valid with settlementToken but settle returns jsonResponse({ error:
'Insufficient balance', code: 'INSUFFICIENT_FUNDS' }, 402) and assert client.pay
returns success: false and errorCode is 'INSUFFICIENT_FUNDS'. Use the same
mockFetch and client.pay usage pattern and check call bodies via
mockFetch.mock.calls like the surrounding tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6a4ffba0-2557-4656-9dd7-b6902ae7494e

📥 Commits

Reviewing files that changed from the base of the PR and between 7771ec6 and 7b18f75.

📒 Files selected for processing (6)
  • README.md
  • src/client.ts
  • src/receipts.ts
  • src/types.ts
  • tests/client.test.ts
  • tests/receipts.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/client.ts
  • src/receipts.ts
  • src/types.ts

Comment thread README.md Outdated
Comment thread tests/client.test.ts
@TateLyman
Copy link
Copy Markdown
Author

Pushed a0d18a1 for the latest CodeRabbit notes.

Changes:

  • Added the README fence language hint for MD040.
  • Added missing signerAddress to the malformed signed-receipt matrix.
  • Added client.pay() edge coverage for repeated timeout errors, verify-time 402 PAYMENT_REQUIRED, and settle-time 402 INSUFFICIENT_FUNDS.

One clarification: I kept the verify-time 402 behavior as no-retry for client.pay(). _request() intentionally does not retry 4xx client/facilitator policy responses; the auto-retry-after-402 flow belongs to createX402Handler and is already covered in tests/x402-handler.test.ts.

Validation after this commit:

npm test
npm run type-check
npm run build
npm run lint
git diff --check

RBKunnela
RBKunnela previously approved these changes May 18, 2026
Copy link
Copy Markdown
Owner

@RBKunnela RBKunnela left a comment

Choose a reason for hiding this comment

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

LGTM — ready to merge.

What's in: canonical JSON (sorted keys + undefined-dropped), EIP-191 signing via viem, signedBy: 'facilitator' | 'payer' role, optional artifact + reputation pointers, runtime guard before exposing settleData.signedReceipt. The negative matrix in tests/receipts.test.ts covers version mismatch, missing signerAddress, missing payer botId, missing settlement fields, and signature without 0x prefix. CodeRabbit's last actionable note (the signerAddress malformed case in tests/client.test.ts) is marked addressed in a0d18a1.

Outstanding nitpick — follow-up, not a blocker: the pay() edge-case coverage CodeRabbit asked for (timeout / 402 retry / insufficient balance) is genuinely missing in tests/client.test.ts but doesn't belong in the receipt PR. I'll open a separate issue for it.

Implementation model: keeping the hybrid arrangement — you own the account and day-to-day code; signature/treasury actions go through me as principal. Founding contributor entry going into CONTRIBUTORS.md with this merge.

Thank you for the fast turnaround on every review round. Merging shortly.

@TateLyman
Copy link
Copy Markdown
Author

TateLyman commented May 18, 2026

Merge-state note: the PR still shows mergeStateStatus=BLOCKED even though CodeRabbit latest status context is passing and your owner review is approved. The blocker appears to be the stale CodeRabbit CHANGES_REQUESTED review decision on an earlier commit, not CI. If branch protection treats bot review state as required, this may need the stale CodeRabbit review dismissed or a maintainer override. No code change from my side unless you want another commit.

@TateLyman
Copy link
Copy Markdown
Author

Rebased onto current main and pushed dbe4c89 to clear the dirty merge state.\n\nWhat changed during the rebase:\n- kept the signed-receipt API on top of the new x402 v2 and micropayment batching exports;\n- fixed the newly exposed x402 v2 type/lint issues from current main: typed MPP recipient as an EVM address, removed the unused PaymentIntentHeader import, made dual-mode signing reachable, used supportedProtocols in negotiation, and aligned the batching recipient map type;\n- no behavior change to the receipt contract beyond the already reviewed branch.\n\nValidation after rebase:\n- npm test -> 119 passed\n- npm run type-check\n- npm run lint\n- npm run build\n- git diff --check\n\nThe branch should be mergeable once CI catches up.

@TateLyman TateLyman force-pushed the codex/signed-receipts branch from dbe4c89 to f12ad8c Compare May 22, 2026 01:00
@TateLyman
Copy link
Copy Markdown
Author

Rebased onto current origin/main after the new CI/type-fix commits and pushed f12ad8c.

Conflict resolved in src/x402-v2.ts by keeping the current supportedProtocols negotiation behavior from main and preserving the signed-receipt branch on top.

Local validation after the rebase:

npm test          # 119 passed
npm run type-check
npm run lint
npm run build
git diff --check

CI/CodeRabbit is running on the refreshed branch now.

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