coinbase / coinbase/cdp-sdk

Bazaar: settlements missing from quality/lastCalledAt on an indexed resource, causing early removal

Open
#816 8 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
200
Forks
191
Avg merge
6h 16m
Merged PRs (30d)
17

Description

## Summary

Two payments to my resource settled on Base mainnet on 2026-09-04. They do not
appear in its `quality` block or its `lastCalledAt` in the
`/platform/v2/x402/discovery/resources` index. The settlements are verifiable
on-chain; the index reads as if they never happened.

The two missing payments are the only ones this resource has received from a
third party, made with their own client. The two that *were* recorded came from
my own client. The likeliest difference is whether the buyer's client echoed the
bazaar declaration back in its payment payload. If that is the cause, the
question for CDP is whether it is intended — because it would make a listing's
liveness depend on buyers' client libraries, which sellers cannot control.

It matters because no route in the index survives past 30 days after its
`lastCalledAt`. Unrecorded settlements therefore schedule my resource for removal
on **2026-09-18** rather than 2026-10-04.

## The case

| | |
|---|---|
| resource | `https://dappgrade.vercel.app/v1/score` |
| network | `eip155:8453` |
| payTo | `0x4a0AEd4ce878e44eF922941f2B06137bc0b69B69` |
| price | $0.01 USDC |

**What the discovery index reports** (unchanged across reads on 2026-09-12 and 2026-09-13):

```
lastCalledAt 2026-08-19T14:50:39.283Z
lastUpdated 2026-08-19T14:50:39.545Z
l30DaysTotalCalls 2
l30DaysUniquePayers 1
```

**What settled in the same 30-day window:** 4 settlements from 2 distinct payers.

The two the index counts are self-payments from my own client key
(`0x493F4E13820302283B89c58ef0909D0f2062d341`):

```
tx 0x794f67df4fe58ab955bb6b98fbbb32e215e945df8754753b132bfb5b37f7f88d block 50144546 2026-08-18T18:40:39Z
tx 0x3bd45552a56bebb2bb4fccdae74b41882eef666d740d3a743eff2f62135ee5b0 block 50180847 2026-08-19T14:50:41Z
```

On the second of these, the index's `lastCalledAt` (14:50:39.283Z) is about two
seconds *before* the block time (14:50:41Z). The call is stamped before the
settlement lands, so the gap below is not about block inclusion timing.

The two the index is missing are both from 2026-09-04, payer
`0x8d2A47CBd262e1bC5eE0645C81899F9896957d37`:

```
tx 0x56001cc1f9639a3b59e47206fbedc86f70edb6b1258e55b4eba5e7885e5aa307
block 50866830 · 2026-09-04T11:56:47Z · 0.01 USDC · status success
nonce 0xc8d05a35ecfb920c716f1646b052efb8ed9daaf28aa4202284b03c289b3422aa
authorizationState(0x8d2A47CB…957d37, nonce) = true

tx 0x8eaa316bfef6f2b2f7105d3d549b9b5cdd6f50be125f43dd6f9f51326cc88faf
block 50866867 · 2026-09-04T11:58:01Z · 0.01 USDC · status success
nonce 0xeb43f0d3250c669ca777beb76fd90cd9da2c26280cd3052bceb779c2e8b3c61b
authorizationState(0x8d2A47CB…957d37, nonce) = true
```

My server routes all mainnet settlement through the CDP facilitator
(`@coinbase/x402` facilitator config), and both of these returned a successful
settle response, which my server recorded. Both transactions emit
`AuthorizationUsed`, and USDC `authorizationState(authorizer, nonce)` returns
`true` for each, so the EIP-3009 authorizations were consumed on-chain.

## Relation to [#764](https://github.com/coinbase/cdp-sdk/issues/764)

That thread is mostly *"my service was never indexed."* Mine is different: **the
resource is indexed, and some settlements against it are not being counted.**

| diagnosis in #764 | this resource |
|---|---|
| endpoint returns 307 instead of 402 | returns a clean 402; verified against production 2026-09-12 |
| payload missing `extensions.bazaar` or `resource` | **possible for these two payments specifically** — though note the reply in #764 frames this as *indexing never being attempted*, whereas this record exists and was indexed. The index was populated by payments from my own client, which echoes the declaration back. The two missing payments came from a third-party client, and I don't log inbound payment payloads, so I can't confirm what they carried. If the echo gates ongoing counting as well as initial indexing, that may not be documented anywhere. |
| response time >10s | not measured — my server does not log request latency, and nothing recorded it on 2026-09-04 |
| wrong query param (`resource_server` vs `urlSubstring`) | not applicable; read directly from the paged index |

## Reproduction

1. Page `GET /platform/v2/x402/discovery/resources?limit=1000&offset=N` (no auth
required) and find the `dappgrade.vercel.app/v1/score` record. Read `quality`,
`lastCalledAt` and `lastUpdated`.
2. Pull USDC transfers into the payTo address on Base for 2026-08-13 → 2026-09-12.
There are four settlement credits, each emitting `AuthorizationUsed`.
3. The two 2026-09-04 settlements are on-chain but not reflected in the record's
counters or `lastCalledAt`.

## Consequence

The `quality` block is documented behaviour — the
[Bazaar docs](https://docs.cdp.coinbase.com/x402/bazaar) state that each result's
`quality` field "reports its call count and unique payer count over the last 30
days, plus when it was last called" — so an undercount is a deviation from what
the docs describe.

Removal after 30 days without a settlement is documented in the [seller discovery guide](https://docs.cdp.coinbase.com/x402/seller/get-discovered), and my own sweeps match it: across two full passes of the index on 2026-09-12 I found no route with a lastCalledAt older than 30.0 days (oldest: 29.986). Routes also leave earlier for other reasons, but none survive past that boundary.

So an uncounted settlement doesn't just understate a counter — it brings a
removal date forward. Had these two been recorded, my clock would read
2026-10-04 rather than 2026-09-18.

The guide documents removal after 30 days without settlement but doesn't establish that the public lastCalledAt is the clock driving it. It also lives on the seller guide rather than the Bazaar page, which may be why several open questions in #764 read as though it isn't documented at all.

## Hypothesis

The two recorded settlements came from my own client. The two missing ones are
the only payments from a third party, made with their own client. The bazaar
extension reads the declaration from `paymentPayload.extensions` — the client's
echo — and my server forwards that payload to the facilitator unmodified. So the
likeliest distinguisher is whether the buyer's client echoed the declaration.

The same pair is missing from both `l30DaysTotalCalls` and `lastCalledAt`, which
fits one ingest path keyed on that echo more readily than two independent
failures.

**If that is right: is it intended that a settlement without the bazaar echo does
not count toward a listing's `quality` or `lastCalledAt`?** If it is, sellers
cannot keep a listing alive by being paid, only by being paid through
cooperating clients.

A cheap check on your side: the same payer settled with roughly 187 other x402
endpoints in the same period. If their payments are uncounted there too, that
points at the client rather than at my resource.

## What I can supply

- full transaction hashes, block numbers, and the decoded settlement responses
- my server-side request log for the window (per-request outcome classification
only exists from 2026-09-12; earlier rows record the request, not its outcome)
- daily snapshots of my own index record since 2026-09-12, and the raw paged
index responses behind the sweep figures above

Happy to run any check that would help narrow it.

## Environment

- Base mainnet, `eip155:8453`, USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`
- Server: `@x402/core` resource server accepting x402 v2 envelopes with CAIP-2
network identifiers; settlement via the CDP facilitator. The envelope version
and shape sent by the third-party client on 2026-09-04 is not logged.
- EIP-712 domain `{"name":"USD Coin","version":"2"}`, verified against the token
contract

Contributor guide

Open the contributing guide

Research direction

Start by reproducing the paged discovery-index query for dappgrade.vercel.app/v1/score and compare its quality fields with the four listed Base settlement transactions. Inspect the facilitator settlement responses and paymentPayload.extensions.bazaar data, if available, to test whether the buyer's bazaar echo distinguishes the recorded and missing payments. Done means the counting behavior is explained and corrected or documented, with lastCalledAt and quality matching the intended settlement policy.

Written by the indexing model from the issue text.

Assessment

Domain
api, blockchain, payments
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.