@zkp2p/cash 0.6.3-rc.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,10 +6,9 @@ fills and ETA work, how unwinding works, and why every order survives a crash.
6
6
  ## The model: you are the maker
7
7
 
8
8
  A Peer Cash order is a **deposit** in the ZKP2P protocol. When you
9
- `cashout()`, Base USDC becomes protocol-held funds priced from Chainlink with
10
- zero spread. Existing corridors bind the on-chain oracle at intent signal;
11
- Alipay/CNY fixes a fresh Ethereum Chainlink snapshot as the maker floor during
12
- deposit preparation. A buyer (a standard protocol taker) _signals an
9
+ `cashout()`, Base USDC becomes protocol-held funds priced from on-chain oracles with
10
+ zero spread. Every corridor, including Alipay/CNY and UPI/INR, binds the
11
+ on-chain Base oracle at intent signal. A buyer (a standard protocol taker) _signals an
13
12
  intent_ against your deposit, pays you fiat offchain (Venmo, Revolut, Wise,
14
13
  ...), and proves the payment via TEE-TLS. The protocol then releases your USDC
15
14
  to them.
@@ -19,6 +18,71 @@ special-cased.
19
18
  Because your deposit is priced at market with no spread, it is the best price
20
19
  a rational maker can offer. That is the fill incentive.
21
20
 
21
+ ## INR/CNY oracle migration (breaking)
22
+
23
+ With the SDK dependency pinned to `@zkp2p/sdk` 0.14.5, new Express Cash
24
+ UPI/INR and Alipay/CNY deposits float with the Base oracle plus
25
+ `MARKET_SPREAD_BPS` (zero), exactly like every other supported currency.
26
+ They no longer fix a creation-time snapshot. Each buyer's intent signal binds
27
+ its rate. Existing deposits keep their on-chain pricing; upgrading does not
28
+ reprice them. Historical fixed-rate Cash orders remain readable and withdrawable
29
+ when indexed attribution and pricing evidence identify them as Cash.
30
+
31
+ With `@zkp2p/sdk` 0.14.5, Express Cash intentionally also offers
32
+ Wise/INR, Wise/CNY, and Revolut/CNY because corridor support derives from
33
+ oracle availability and each platform's currency catalog. The exact INR/CNY
34
+ corridor set in production, preproduction, and staging is UPI/INR, Wise/INR,
35
+ Alipay/CNY, Wise/CNY, and Revolut/CNY. All five use zero-spread oracle pricing
36
+ bound at intent signal; currencies without an oracle config remain unsupported.
37
+
38
+ This is a hard API cutover, with no deprecated aliases:
39
+
40
+ - Remove `creationRateTransport`, `creationRateRpcUrl`,
41
+ `upiCreationRateTransport`, and `upiCreationRateRpcUrl` from client options.
42
+ Use the existing Base `transport` / `rpcUrl` for oracle reads.
43
+ - Remove imports of `CREATION_RATE_MAX_STALENESS_SECONDS`,
44
+ `isCreationRateCorridor`, `readAlipayCnyCreationRate`, `CreationRateReader`,
45
+ and `CreationRateSnapshot`. The internal `readCashCreationRate` reader and
46
+ `prepareCashDepositParams`' fourth reader argument are also removed.
47
+ - Estimates now expose only `binding: 'intent-signal'`; capability pricing
48
+ exposes only `kind: 'oracle-at-intent-signal'`. Their types and codecs reject
49
+ the removed `deposit-creation` / `fixed-at-deposit-creation` variants.
50
+ Refresh persisted estimates and capabilities when upgrading. Historical
51
+ order payout codecs retain fixed-rate evidence for recovery.
52
+
53
+ For `zkp2p-clients` consumers (verified at `origin/main` commit `004abbc`),
54
+ `clients/web/src/components/PeerCash/usePeerCashExpressFlow.ts:278-288` passes
55
+ both `creationRateTransport` and `upiCreationRateTransport` through conditional
56
+ object spreads. TypeScript will **not** flag these leftover options after the
57
+ SDK bump; they will be silently ignored. Remove both spreads, their obsolete
58
+ imports, and the Polygon RPC proxy plumbing (`rpcProxyPolygonUrl` in
59
+ `clients/web/src/helpers/rpcProxy.ts`, its helper tests, and the Express test
60
+ mock). Keep shared Ethereum proxy plumbing that other features still use.
61
+ Remove `clients/web/src/components/PeerCash/peerCashCreationRate.test.ts`: it
62
+ sets both removed options and expects `binding: 'deposit-creation'`, so it will
63
+ fail after the bump. Replace that obsolete test with Base oracle transport
64
+ coverage asserting `binding: 'intent-signal'`. Do not rely on the compiler to
65
+ complete this migration.
66
+
67
+ INR/USD and CNY/USD are ZKP2P-operated, 8-decimal AggregatorV3-compatible
68
+ Base feeds consumed by the existing Chainlink adapter with `invert: true`.
69
+ Oracle availability comes from the SDK catalog; there are no special INR/CNY
70
+ support branches or Ethereum/Polygon rate readers. A failing estimate returns
71
+ the same oracle errors as other currencies; stale estimates are display-flagged,
72
+ and the on-chain adapter enforces `maxStaleness` at signal.
73
+
74
+ The published SDK rc.1 → rc.2 diff also adds Microsoft OAuth seller-credential
75
+ uploads (including PKCE `codeVerifier`) and shares the Google upload path's
76
+ implementation. Cash's hosted Venmo Gmail connector is unchanged. The contracts
77
+ pin moves from 0.4.2 to 0.4.3-rc.1, adding FxRateStore/feed ABIs and addresses
78
+ and provider metadata to the oracle catalog. Existing payment-method catalog
79
+ content, escrow/guardian addresses, and active dispute-stack selections are
80
+ unchanged. Indexer schema remains 0.22.0. The prepared stable SDK 0.14.5
81
+ pins stable contracts 0.4.3.
82
+
83
+ Release history remains in PR titles per the contributor guide; this change
84
+ does not bump the Cash package version or publish a release.
85
+
22
86
  ## Source routing
23
87
 
24
88
  The cashout destination is always canonical Base USDC. The minimal/default path
@@ -189,9 +253,8 @@ then read that pair from `fillStats()` without coupling the two loading states.
189
253
  - **Buyer arrival time is market-driven.** A deposit at market rate should
190
254
  fill fast, but the ETA is only a recent historical sample.
191
255
  - **The binding point is explicit.** `estimate().binding` is `intent-signal`
192
- for existing on-chain oracle corridors. Alipay/CNY returns
193
- `deposit-creation`: its fresh Ethereum Chainlink snapshot becomes the
194
- on-chain maker floor when the deposit is prepared.
256
+ for every corridor, including Alipay/CNY and UPI/INR. Each buyer's signal
257
+ binds the live Base oracle rate.
195
258
  - **The label is display-ready.** Use `eta.label` in simple UIs; use
196
259
  `eta.seconds` only if you need your own formatting.
197
260
 
@@ -237,17 +300,22 @@ same ceil-to-cent math, and the decode is verified against live production
237
300
  receipts.
238
301
 
239
302
  Orders also carry their `payouts` legs reconstructed from the chain - platform,
240
- currency, payee hash, and indexed pricing evidence. Existing corridors expose
241
- `spreadBps: 0`, an oracle `kind`, and `marketRate: true`; Alipay/CNY exposes
242
- `fixedAtCreation: true` and its `fixedRate`. An order created with several
303
+ currency, payee hash, and indexed pricing evidence. New orders expose
304
+ `spreadBps: 0`, an oracle `kind`, and `marketRate: true`, including Alipay/CNY
305
+ and UPI/INR. Historical fixed-rate orders expose `fixedAtCreation: true` and
306
+ their `fixedRate`. An order created with several
243
307
  payout platforms (`receive` as an array of legs) surfaces one entry per
244
308
  platform-currency pair.
245
309
 
246
310
  Reconstruction is fail-closed: every payment method on the indexed deposit
247
- must resolve through the active SDK catalog. Oracle-priced rows retain the
248
- historical structural classification. Fixed Alipay/CNY rows must also carry the
249
- indexed `peer-cash` ERC-8021 attribution so unrelated Advanced Sell deposits
250
- cannot be mistaken for Cash orders. `orders()` excludes unsupported or mixed
311
+ must resolve through the active SDK catalog. Zero-spread oracle payouts are
312
+ classified structurally, without requiring the `peer-cash` ERC-8021 attribution
313
+ marker. This includes INR/CNY, consistently with other oracle currencies: a
314
+ qualifying oracle deposit created via Advanced Sell from the same wallet can
315
+ appear in `cash.orders(owner)` and be returned by `cash.order(depositId)`.
316
+ Historical fixed Alipay/CNY and UPI/INR rows instead require both positive
317
+ fixed-rate evidence and indexed `peer-cash` attribution; unrelated fixed-rate
318
+ Advanced Sell deposits do not qualify. `orders()` excludes unsupported or mixed
251
319
  rows; `order()` returns `ORDER_NOT_FOUND` rather than partially reclassifying
252
320
  them.
253
321
 
@@ -329,7 +397,7 @@ explicit override.
329
397
  | Code | Retryable | What happened / what to do |
330
398
  | --------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
331
399
  | `ORACLE_UNSUPPORTED_CURRENCY` | no | Platform/currency corridor is unavailable. Pick a pair from `capabilities()`. |
332
- | `ORACLE_READ_FAILED` | yes | A Chainlink read failed. Retry through the configured Base or creation-rate Ethereum RPC; do not present a cached value as fresh. |
400
+ | `ORACLE_READ_FAILED` | yes | A Chainlink read failed. Retry through the configured Base RPC; do not present a cached value as fresh. |
333
401
  | `UNSUPPORTED_PLATFORM` | no | Platform is absent from this environment's catalog. Pick from `capabilities()`. |
334
402
  | `UNSUPPORTED_PLATFORM_CURRENCY` | no | The platform does not support that currency. Use its `capabilities()` currencies. |
335
403
  | `AMOUNT_BELOW_MINIMUM` | no | Amount is below the $0.01 hard floor. The recommended minimum is 1 USDC. |
@@ -4,7 +4,7 @@
4
4
  "type": "module",
5
5
  "description": "Peer Cash Demo: the express sell flow stored entirely onchain on Base, after zSwap by z0r0z",
6
6
  "dependencies": {
7
- "@zkp2p/cash": "0.6.1",
7
+ "@zkp2p/cash": "0.7.0",
8
8
  "esbuild": "0.28.2",
9
9
  "solc": "0.8.36",
10
10
  "viem": "2.55.18"
@@ -103,7 +103,7 @@ select option{background:var(--c);color:var(--f)}
103
103
  <div id="error" class="warn hide"></div>
104
104
 
105
105
  <div id="details" class="hide">
106
- <div class="drow"><span title="The estimate shows its rate-binding point. Most corridors bind when a buyer signals; Alipay/CNY binds when the deposit is prepared.">Market rate</span><b id="rate">…</b></div>
106
+ <div class="drow"><span title="All corridors, including INR and CNY, bind the live Base oracle rate when a buyer signals.">Market rate</span><b id="rate">…</b></div>
107
107
  <div class="drow"><span title="Median time recent cash-outs on this platform and currency waited for their first fill, from the last 30 days of onchain history. Historical, not a guarantee.">Time to fill</span><b id="eta">Varies</b></div>
108
108
  </div>
109
109
 
@@ -1,4 +1,4 @@
1
- // UPI pricing reads Polygon mainnet; configure upiCreationRateRpcUrl for a dedicated provider.
1
+ // UPI pricing uses the Base oracle; configure transport or rpcUrl for a dedicated provider.
2
2
  import { createCashClient, usdc } from '@zkp2p/cash';
3
3
  import type { WalletClient } from 'viem';
4
4
 
package/llms.txt CHANGED
@@ -3,7 +3,7 @@
3
3
  > Offramp-only SDK for the ZKP2P protocol: route Relay-supported EVM assets or
4
4
  > NEAR Intents 1Click external deposits to Base USDC, then cash out to fiat
5
5
  > (Venmo, Revolut, Wise, Alipay, Zelle, ...)
6
- > at a zero-spread Chainlink market rate with no centralized
6
+ > at a zero-spread on-chain oracle market rate with no centralized
7
7
  > off-ramp provider. The user is the maker, a buyer pays them fiat and proves
8
8
  > the payment, and the SDK exposes readable order state.
9
9
  > Base-USDC flows are serializable through prepare paths; source-routed cashout
@@ -43,9 +43,9 @@ Key facts:
43
43
  address/memo, and deadline; send once with the origin wallet; optionally
44
44
  submit that existing hash; poll status to SUCCESS; reconcile Base evidence;
45
45
  then cash out Base-only. Use EXACT_OUTPUT when the order amount must be fixed.
46
- - estimate() reports its binding point. Existing corridors bind the on-chain
47
- oracle when an intent is signaled; Alipay/CNY fixes a fresh Ethereum
48
- Chainlink snapshot during deposit preparation; UPI/INR uses a fresh Polygon Chainlink snapshot. ETA is `{ seconds, label }` from the same rolling
46
+ - estimate() reports binding: intent-signal for every corridor, including
47
+ Alipay/CNY and UPI/INR. All deposits float with the Base oracle plus zero
48
+ spread until a buyer signals. ETA is `{ seconds, label }` from the same rolling
49
49
  30-day, intent-attributed pair sampler as fillStats(), not a guarantee.
50
50
  - fillStats() returns raw `{ fills, medianFillSeconds? }` evidence keyed by
51
51
  `platform:currency` or a sorted set such as `revolut:EUR+GBP+USD`. Set
@@ -97,7 +97,7 @@ Key facts:
97
97
  platforms (each platform at most once); read each capability's pricing entry
98
98
  for its binding semantics.
99
99
  - Orders carry their payout legs (platform, currency, payee hash) plus indexed
100
- pricing evidence for either signal-time oracle or fixed-at-creation pricing.
100
+ pricing evidence for signal-time oracle pricing (or historical fixed-rate orders).
101
101
  - Order reads fail closed when any deposit method is absent from the active
102
102
  catalog; mixed historical deposits are never partially reclassified.
103
103
  - buyer(address) aggregates a buyer's track record (fulfilled/pruned/success
@@ -154,11 +154,16 @@ Key facts:
154
154
  - [Codecs](src/codecs/): zod schemas + lossless JSON round-trips
155
155
 
156
156
 
157
- UPI/INR reads the live Chainlink Polygon mainnet proxy
158
- `0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60` (chain 137), inverts
159
- USD per INR, and rounds the creation-time maker floor up. Configure its
160
- read-only RPC with `upiCreationRateRpcUrl` or `upiCreationRateTransport`.
161
- Alipay/CNY retains the Ethereum registry and `creationRateRpcUrl` /
162
- `creationRateTransport`. UPI rejects the wrong chain, invalid rounds, and
163
- observations older than 24 hours; market closures do not bypass freshness.
157
+ UPI/INR and Alipay/CNY use ZKP2P-operated AggregatorV3-compatible feeds on Base,
158
+ through the SDK's Chainlink oracle adapter with `invert: true` and zero spread.
159
+ The SDK feed catalog supplies the addresses and determines oracle availability;
160
+ Cash adds no currency exceptions. Estimates read through the normal Base
161
+ `transport` / `rpcUrl`; deposits float until each buyer signals an intent.
164
162
  UPI is available in production, preproduction, and staging without a feature flag.
163
+
164
+ With `@zkp2p/sdk` 0.14.5-rc.2, Express Cash intentionally also offers
165
+ Wise/INR, Wise/CNY, and Revolut/CNY because corridor support derives from
166
+ oracle availability and each platform's currency catalog. The exact INR/CNY
167
+ corridor set in production, preproduction, and staging is UPI/INR, Wise/INR,
168
+ Alipay/CNY, Wise/CNY, and Revolut/CNY. All five use zero-spread oracle pricing
169
+ bound at intent signal; currencies without an oracle config remain unsupported.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zkp2p/cash",
3
- "version": "0.6.3-rc.2",
4
- "description": "Peer Cash - offramp-only SDK for routing Relay or NEAR Intents assets to Base USDC, then cashing out to fiat at zero-spread Chainlink market rates.",
3
+ "version": "0.7.0",
4
+ "description": "Peer Cash - offramp-only SDK for routing Relay or NEAR Intents assets to Base USDC, then cashing out to fiat at zero-spread on-chain oracle market rates.",
5
5
  "license": "MIT",
6
6
  "author": "Peer (https://peer.xyz)",
7
7
  "type": "module",
@@ -108,7 +108,7 @@
108
108
  },
109
109
  "dependencies": {
110
110
  "@relayprotocol/relay-sdk": "^7.0.1",
111
- "@zkp2p/sdk": "0.14.5-rc.1",
111
+ "@zkp2p/sdk": "0.14.5",
112
112
  "zod": "^4.4.3"
113
113
  },
114
114
  "peerDependencies": {
@@ -28,11 +28,9 @@ protocol-held funds and no custodial off-ramp provider.
28
28
  send once with the origin wallet, optionally register that existing hash,
29
29
  poll status to `SUCCESS`, reconcile Base evidence, then cash out Base-only.
30
30
  Browser integrations use a same-origin proxy so the 1Click JWT stays server-side.
31
- - **Pricing has an explicit binding point.** Existing corridors carry
32
- `oracleRateConfig { spreadBps: 0 }`; the binding rate is the Chainlink rate
33
- when a buyer signals. Alipay/CNY is the exception: because Base has no CNY
34
- oracle adapter, the SDK reads Chainlink CNY/USD on Ethereum and fixes that
35
- fresh snapshot as the maker floor during deposit preparation. UPI/INR does the same using the direct Polygon INR/USD feed, rejecting wrong-chain, stale and invalid data. Read
31
+ - **Pricing has an explicit binding point.** Every corridor carries
32
+ `oracleRateConfig { spreadBps: 0 }`; the binding rate is the Base oracle rate
33
+ when a buyer signals, including Alipay/CNY and UPI/INR. Read
36
34
  `estimate().binding` and `capabilities().platforms[].pricing`.
37
35
  - **Custody story.** Funds are held by the protocol contract only. An unmatched
38
36
  deposit is withdrawable by the maker at any time. The SDK never holds keys.
@@ -271,23 +269,20 @@ a human with the `depositId` and tx hashes.
271
269
 
272
270
  ## UPI oracle checks
273
271
 
274
- UPI/INR reads the live Chainlink Polygon mainnet proxy
275
- `0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60` (chain 137), inverts
276
- USD per INR, and rounds the creation-time maker floor up. Configure its
277
- read-only RPC with `upiCreationRateRpcUrl` or `upiCreationRateTransport`.
278
- Alipay/CNY retains the Ethereum registry and `creationRateRpcUrl` /
279
- `creationRateTransport`. UPI rejects the wrong chain, invalid rounds, and
280
- observations older than 24 hours; market closures do not bypass freshness.
272
+ UPI/INR and Alipay/CNY use ZKP2P-operated AggregatorV3-compatible feeds on Base,
273
+ through the SDK's Chainlink oracle adapter with `invert: true` and zero spread.
274
+ The SDK feed catalog supplies the addresses and determines oracle availability;
275
+ Cash adds no currency exceptions. Estimates read through the normal Base
276
+ `transport` / `rpcUrl`; deposits float until each buyer signals an intent.
281
277
  UPI is available in production, preproduction, and staging without a feature flag.
282
278
 
283
279
  Before a funded UPI QA run, call
284
280
  `cash.estimate({ amount: 1000000n, platform: 'upi', currency: 'INR' }, { includeEta: false })`
285
- using the intended live Polygon RPC. Require a positive finite rate,
286
- `binding: 'deposit-creation'`, and `oracleUpdatedAt` no more than 86400 seconds
281
+ using the intended live Base RPC. Require a positive finite rate,
282
+ `binding: 'intent-signal'`, and `oracleUpdatedAt` no more than 86400 seconds
287
283
  old and not in the future. Record the observation time and selected chain,
288
284
  then stop before funding if the read fails. Unit-test fixtures prove routing,
289
- not live availability. The authoritative feed listing is
290
- https://data.chain.link/feeds/polygon/mainnet/inr-usd.
285
+ not live availability. Use the pinned SDK oracle feed catalog for the feed address.
291
286
 
292
287
  ## Optional Venmo receipt linking
293
288
 
@@ -29,17 +29,14 @@ for (const environment of ['staging', 'preproduction', 'production'] as const) {
29
29
  Use an explicitly authorized low-value wallet and verified recipient UPI ID.
30
30
  Keep the identity and operation ledger in ignored private storage, outside this skill.
31
31
  `cashout` accepts any valid VPA; no seller bank login or identity attestation is required.
32
- Verify INR pricing is a fresh Polygon Chainlink snapshot fixed at deposit creation.
33
- The official [INR/USD feed](https://data.chain.link/feeds/polygon/mainnet/inr-usd)
34
- is proxy `0xDA0F8Df6F5dB15b346f4B8D1156722027E194E60` on chain 137;
35
- it is not registered in Ethereum's Feed Registry. Before funding, call
32
+ Verify INR pricing uses the ZKP2P-operated Base INR/USD oracle from the SDK
33
+ catalog, inverted through the Chainlink adapter with zero spread. Before funding, call
36
34
  `cash.estimate({ amount: 5_000_000n, platform: 'upi', currency: 'INR' }, { includeEta: false })`
37
- against the live default or explicitly configured Polygon RPC. Confirm a positive
38
- rate, fresh `oracleUpdatedAt`, and `binding: 'deposit-creation'`. Do not accept
39
- mocked-rate unit tests as proof of a functioning live corridor. The reader must
40
- reject the wrong chain, invalid/incomplete rounds, future timestamps and data
41
- older than 86,400 seconds. Forex market-hour gaps must fail closed; do not
42
- substitute a static price or relax freshness to make QA pass.
35
+ against the live default or explicitly configured Base RPC. Confirm a positive
36
+ rate, fresh `oracleUpdatedAt`, and `binding: 'intent-signal'`. Do not accept
37
+ mocked-rate unit tests as proof of a functioning live corridor. Stop before funding
38
+ if the read fails or is stale. The on-chain adapter enforces the SDK config's
39
+ `maxStaleness` at signal; do not substitute a static price to make QA pass.
43
40
 
44
41
  With bounded task authorization, create one small Base-USDC deposit, recording
45
42
  transaction hash and deposit ID before any retry. Confirm the receipt, then
@@ -76,7 +73,7 @@ bodies out of committed evidence; publish only redacted checkpoint results.
76
73
 
77
74
  ## Live order reconstruction regression
78
75
 
79
- After the exact UPI fixture is indexed, require both `cash.order(depositId)` and `cash.orders(owner)` to return it. Fixed UPI/INR creation-rate deposits must have positive fixed-rate evidence and the indexed `peer-cash` attribution marker. Do not classify unrelated Advanced Sell deposits as Cash orders. A quoteable indexer row alone is insufficient: run order lookup, partial-fill observation and withdrawal checks too. Keep the method/currency pair explicit; UPI/CNY must be rejected.
76
+ After the exact UPI fixture is indexed, require both `cash.order(depositId)` and `cash.orders(owner)` to return it. New UPI/INR deposits must have zero-spread oracle pricing evidence. Historical fixed-rate UPI/INR deposits still require positive fixed-rate evidence and the indexed `peer-cash` attribution marker for recovery. Zero-spread oracle payouts are classified structurally without requiring the `peer-cash` marker, including INR/CNY as with other oracle currencies. A qualifying zero-spread INR/CNY oracle deposit created via Advanced Sell from the same wallet can therefore appear in `cash.orders(owner)` and `cash.order(depositId)`. The attribution requirement excludes unrelated historical fixed-rate Advanced Sell deposits, not qualifying oracle deposits. A quoteable indexer row alone is insufficient: run order lookup, partial-fill observation and withdrawal checks too. Keep the method/currency pair explicit; UPI/CNY must be rejected.
80
77
 
81
78
  ## Small-fixture visibility and dust reconciliation
82
79