@zkp2p/cash 0.4.11-rc.2 → 0.4.11-rc.4
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.
- package/AGENTS.md +72 -70
- package/README.md +58 -54
- package/dist/{chunk-AAWRU4JK.js → chunk-UIELF4TY.js} +4 -36
- package/dist/{createCashClient-G8ksaisL.d.cts → createCashClient-BIAuk1Qy.d.cts} +43 -29
- package/dist/{createCashClient-G8ksaisL.d.ts → createCashClient-BIAuk1Qy.d.ts} +43 -29
- package/dist/index.cjs +293 -198
- package/dist/index.d.cts +56 -81
- package/dist/index.d.ts +56 -81
- package/dist/index.js +290 -168
- package/dist/react.d.cts +5 -5
- package/dist/react.d.ts +5 -5
- package/dist/react.js +1 -1
- package/dist/tools.cjs +6 -6
- package/dist/tools.d.cts +5 -5
- package/dist/tools.d.ts +5 -5
- package/dist/tools.js +6 -6
- package/docs/lifecycle-and-recovery.md +79 -74
- package/examples/agent-tool-use.ts +4 -4
- package/examples/carpe-diem-provider-cashout/README.md +2 -2
- package/examples/mpp-merchant-cashout/README.md +5 -5
- package/examples/mpp-merchant-cashout/app.ts +3 -6
- package/examples/node-cashout.ts +3 -6
- package/examples/onchain-demo/src/shell.html +3 -3
- package/llms.txt +20 -16
- package/package.json +2 -2
- package/skills/peer-cash-integration/SKILL.md +18 -22
package/AGENTS.md
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
You are integrating Peer Cash: an offramp that routes Relay-supported EVM
|
|
8
8
|
assets or NEAR Intents 1Click external deposits into Base USDC, then converts
|
|
9
|
-
Base USDC to fiat (Venmo, Revolut, Wise, Zelle, ...) at
|
|
10
|
-
market rate.
|
|
9
|
+
Base USDC to fiat (Venmo, Revolut, Wise, Alipay, Zelle, ...) at a zero-spread
|
|
10
|
+
Chainlink market rate. Existing corridors bind at intent signal; Alipay/CNY
|
|
11
|
+
fixes a fresh Ethereum Chainlink snapshot during deposit preparation. The user whose USDC you
|
|
11
12
|
manage is the **maker**; a buyer pays them fiat and proves it with TEE-TLS; the
|
|
12
13
|
protocol releases the USDC. Funds are held by the protocol, and only the maker
|
|
13
14
|
can withdraw an unmatched deposit.
|
|
@@ -26,8 +27,7 @@ can withdraw an unmatched deposit.
|
|
|
26
27
|
persist the returned `depositId`. If the original plan set
|
|
27
28
|
a non-empty `accessPolicyPaymentMethods`, submit and confirm
|
|
28
29
|
`prepareAccessPolicy(depositId, paymentMethod)` with the depositor for each
|
|
29
|
-
returned method.
|
|
30
|
-
`prepareDisputeProtection(depositId, paymentMethod)`.
|
|
30
|
+
returned method.
|
|
31
31
|
3. **You are a tool-use host** (MCP server, CLI) → import the manifest from
|
|
32
32
|
`@zkp2p/cash/tools` and map the tool names to the verbs above. Base-USDC
|
|
33
33
|
mutating tools return unsigned transactions. `cash_source_quote` is a quote,
|
|
@@ -46,15 +46,22 @@ deposit-level integration share instead of applying maker L1/L2.
|
|
|
46
46
|
|
|
47
47
|
**Platform caveats:**
|
|
48
48
|
|
|
49
|
-
- **Venmo
|
|
50
|
-
`cashout()`
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
- **Venmo and PayPal restrict who can signal intents by default.**
|
|
50
|
+
Signed `cashout()` confirms `createDeposit`, then submits and confirms a
|
|
51
|
+
method-scoped Peer Pay merchant policy for every restricted payout leg using
|
|
52
|
+
the same viem wallet. This is a deliberate non-atomic follow-up with a brief
|
|
53
|
+
open-to-all-takers interval. Method-scoped dispute protection is already
|
|
54
|
+
default-on for these rails; do not readiness-gate deposit creation or submit
|
|
55
|
+
a redundant `setDisputeProtectionEnabled(true)` transaction. For `prepare()`,
|
|
56
|
+
read `accessPolicyPaymentMethods`; after `createDeposit` confirms, finalize
|
|
57
|
+
its receipt and submit
|
|
58
|
+
`prepareAccessPolicy(depositId, paymentMethod)` for every returned method.
|
|
59
|
+
Any viem EOA works; Privy is not required.
|
|
60
|
+
|
|
61
|
+
- **Cash App is non-chargebackable.** Cash App cash-outs stay public, do not
|
|
62
|
+
attach a Peer Pay merchant policy, and never require dispute-protection stake.
|
|
63
|
+
|
|
64
|
+
- **Wise, PayPal, and Alipay** carry `requiresIdentityAttestation: true`. A new curator
|
|
58
65
|
registration needs a signed maker identity attestation this SDK cannot mint
|
|
59
66
|
(first-party Peer web obtains it through the Peer TEE browser extension).
|
|
60
67
|
An already-registered handle can be reused with bare payee data. A new handle
|
|
@@ -111,7 +118,7 @@ const multiCurrency = await cash.cashout(
|
|
|
111
118
|
);
|
|
112
119
|
|
|
113
120
|
// Widest reach: several platforms on one order (each platform at most once);
|
|
114
|
-
// the buyer picks the leg they can pay
|
|
121
|
+
// the buyer picks the leg they can pay. Read capability pricing per corridor.
|
|
115
122
|
const multiPlatform = await cash.cashout(
|
|
116
123
|
{
|
|
117
124
|
amount: usdc(500),
|
|
@@ -183,9 +190,11 @@ const route = await cash.nearIntentsStatus({
|
|
|
183
190
|
|
|
184
191
|
## Rules that prevent wrong behavior
|
|
185
192
|
|
|
186
|
-
- **
|
|
187
|
-
|
|
188
|
-
|
|
193
|
+
- **Respect the declared binding point.** `estimate()` is
|
|
194
|
+
`kind: 'oracle-estimate'`. Its `binding` is `intent-signal` for existing
|
|
195
|
+
on-chain oracle corridors and `deposit-creation` for Alipay/CNY. Do not call
|
|
196
|
+
an estimate locked before that point. Once Alipay/CNY is prepared, its fresh
|
|
197
|
+
Chainlink snapshot is the on-chain maker floor.
|
|
189
198
|
- **Do not invent an ETA.** Use `estimate().eta`: `{ seconds, label }` backed
|
|
190
199
|
by the same rolling 30-day, intent-attributed pair sample as `fillStats()`,
|
|
191
200
|
measured from deposit creation to first fill. Use `order.explain()` for live
|
|
@@ -234,10 +243,6 @@ const route = await cash.nearIntentsStatus({
|
|
|
234
243
|
already exists. If `recovery.transactionHash` is present, inspect that policy
|
|
235
244
|
transaction first; prepare another policy transaction only when the previous
|
|
236
245
|
one is absent or confirmed reverted.
|
|
237
|
-
- **Fail closed on dispute protection.** `DISPUTE_PROTECTION_UNAVAILABLE`
|
|
238
|
-
occurs before deposit creation. `DISPUTE_PROTECTION_CONFIGURATION_FAILED`
|
|
239
|
-
means the deposit exists; inspect its recovery hash and use
|
|
240
|
-
`prepareDisputeProtection` only when resubmission is safe.
|
|
241
246
|
- **`ORDER_NOT_FOUND` seconds after `cashout()` is indexer lag**, not a lost
|
|
242
247
|
deposit. The tx receipt you hold is the truth. Retry; `watch()` absorbs
|
|
243
248
|
this automatically.
|
|
@@ -262,53 +267,51 @@ const route = await cash.nearIntentsStatus({
|
|
|
262
267
|
|
|
263
268
|
Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
|
|
264
269
|
|
|
265
|
-
| Code
|
|
266
|
-
|
|
|
267
|
-
| `ORACLE_UNSUPPORTED_CURRENCY`
|
|
268
|
-
| `ORACLE_READ_FAILED`
|
|
269
|
-
| `UNSUPPORTED_PLATFORM`
|
|
270
|
-
| `UNSUPPORTED_PLATFORM_CURRENCY`
|
|
271
|
-
| `AMOUNT_BELOW_MINIMUM`
|
|
272
|
-
| `INVALID_INTENT_AMOUNT_RANGE`
|
|
273
|
-
| `INVALID_PAYOUT_CURRENCIES`
|
|
274
|
-
| `INVALID_PAYOUT_PLATFORMS`
|
|
275
|
-
| `PAYEE_VERIFICATION_REQUIRED`
|
|
276
|
-
| `PAYEE_REGISTRATION_FAILED`
|
|
277
|
-
| `ATOMIC_ACCESS_POLICY_REQUIRED`
|
|
278
|
-
| `ACCESS_POLICY_CONFIGURATION_FAILED`
|
|
279
|
-
| `
|
|
280
|
-
| `
|
|
281
|
-
| `
|
|
282
|
-
| `
|
|
283
|
-
| `
|
|
284
|
-
| `
|
|
285
|
-
| `
|
|
286
|
-
| `
|
|
287
|
-
| `
|
|
288
|
-
| `
|
|
289
|
-
| `
|
|
290
|
-
| `
|
|
291
|
-
| `
|
|
292
|
-
| `
|
|
293
|
-
| `
|
|
294
|
-
| `
|
|
295
|
-
| `
|
|
296
|
-
| `
|
|
297
|
-
| `
|
|
298
|
-
| `
|
|
299
|
-
| `
|
|
300
|
-
| `
|
|
301
|
-
| `
|
|
302
|
-
| `
|
|
303
|
-
| `
|
|
304
|
-
| `
|
|
305
|
-
| `
|
|
306
|
-
| `
|
|
307
|
-
| `
|
|
308
|
-
| `
|
|
309
|
-
| `
|
|
310
|
-
| `WATCH_TIMEOUT` | yes | Resume `watch(depositId)` later |
|
|
311
|
-
| `ESCROW_PAUSED` | yes | Back off; existing funds remain withdrawable |
|
|
270
|
+
| Code | Retryable | Agent action |
|
|
271
|
+
| --------------------------------------- | --------- | ------------------------------------------------------------------------------------------ |
|
|
272
|
+
| `ORACLE_UNSUPPORTED_CURRENCY` | no | Re-pick currency from `capabilities()` |
|
|
273
|
+
| `ORACLE_READ_FAILED` | yes | Retry the read through a healthy Base RPC; do not present a cached value as live |
|
|
274
|
+
| `UNSUPPORTED_PLATFORM` | no | Re-pick platform from `capabilities()` |
|
|
275
|
+
| `UNSUPPORTED_PLATFORM_CURRENCY` | no | Use a currency listed for that platform |
|
|
276
|
+
| `AMOUNT_BELOW_MINIMUM` | no | Raise amount (hard floor $0.01, recommended at least 1 USDC) |
|
|
277
|
+
| `INVALID_INTENT_AMOUNT_RANGE` | no | Use a positive min, max at least min, and max no greater than amount |
|
|
278
|
+
| `INVALID_PAYOUT_CURRENCIES` | no | Pass one or more unique currencies listed for the platform |
|
|
279
|
+
| `INVALID_PAYOUT_PLATFORMS` | no | Pass one leg or an array of legs, using each platform at most once |
|
|
280
|
+
| `PAYEE_VERIFICATION_REQUIRED` | no | Use Peer web + TEE extension for new Wise/PayPal/Alipay; reuse registered handles |
|
|
281
|
+
| `PAYEE_REGISTRATION_FAILED` | yes | Validate against `payeeHint`, then retry |
|
|
282
|
+
| `ATOMIC_ACCESS_POLICY_REQUIRED` | no | Deprecated compatibility code; current SDK flows never emit it |
|
|
283
|
+
| `ACCESS_POLICY_CONFIGURATION_FAILED` | no | Deposit exists; inspect policy tx first, attach only if needed; never repeat the cash-out. |
|
|
284
|
+
| `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE` | no | Execute Relay with a signer first, then prepare a Base-USDC cashout |
|
|
285
|
+
| `SOURCE_RECIPIENT_MISMATCH` | no | Route Base USDC to the cashout depositor |
|
|
286
|
+
| `SOURCE_CAPABILITIES_FAILED` | yes | Retry discovery or fall back to Base USDC |
|
|
287
|
+
| `SOURCE_QUOTE_FAILED` | yes | Refresh capabilities and request a new canonical Base-USDC quote |
|
|
288
|
+
| `SOURCE_NONCE_MANAGER_REQUIRED` | no | Preflight; recreate the source signer with viem's `nonceManager`, then quote again |
|
|
289
|
+
| `SOURCE_EXECUTION_FAILED` | no | Inspect source transactions and Relay status before any retry |
|
|
290
|
+
| `SOURCE_DEPOSIT_SUBMISSION_FAILED` | yes | Retry only the 1Click notification; never resend source funds |
|
|
291
|
+
| `SOURCE_STATUS_FAILED` | yes | Retry only the status read |
|
|
292
|
+
| `SOURCE_ROUTE_COMPLETED_CASHOUT_FAILED` | no | Do not route again; retry Base-only with `recovery.amount` |
|
|
293
|
+
| `SOURCE_CASHOUT_SUBMISSION_UNKNOWN` | no | Inspect Base activity and orders; prove no deposit exists before retrying |
|
|
294
|
+
| `SOURCE_CASHOUT_STATUS_UNKNOWN` | no | Inspect `recovery.depositTxHash`; do not resubmit while its receipt is unknown |
|
|
295
|
+
| `INSUFFICIENT_TOKEN_BALANCE` | no | Fund the required token amount, then retry |
|
|
296
|
+
| `ALLOWANCE_NOT_VISIBLE` | yes | Approval mined but a stale RPC hid it; retry after it becomes visible |
|
|
297
|
+
| `TRANSACTION_REJECTED` | yes | Retry when ready and approve the wallet request |
|
|
298
|
+
| `TRANSACTION_FAILED` | no | Inspect the failed/reverted call before another action |
|
|
299
|
+
| `TRANSACTION_SUBMISSION_UNKNOWN` | no | Inspect Base wallet/protocol state and the recovery action before any resubmission |
|
|
300
|
+
| `TRANSACTION_STATUS_UNKNOWN` | no | Inspect `recovery.transactionHash` before resubmitting |
|
|
301
|
+
| `DEPOSIT_RESOLUTION_FAILED` | no | Inspect the confirmed Base receipt and recover the id from `DepositReceived` |
|
|
302
|
+
| `INVALID_DEPOSIT_ID` | no | Use the exact id returned by `cashout()` |
|
|
303
|
+
| `ORDER_NOT_FOUND` | yes | Retry through immediate indexer lag; otherwise verify the id |
|
|
304
|
+
| `INDEXER_LAG` | yes | Retry after a few seconds |
|
|
305
|
+
| `INDEXER_UNAVAILABLE` | yes | Retry only the failed read; keep the id/owner and never repeat a transaction |
|
|
306
|
+
| `ACTIVE_INTENT_BLOCKS_WITHDRAWAL` | yes | Wait for fill/expiry, or withdraw only the unlocked amount |
|
|
307
|
+
| `INSUFFICIENT_AVAILABLE_FUNDS` | yes | Lower the partial withdrawal amount |
|
|
308
|
+
| `NOTHING_TO_WITHDRAW` | no | Order is terminal; reconcile records |
|
|
309
|
+
| `ORDER_NOT_ACTIVE` | no | Start a new cashout instead of topping up |
|
|
310
|
+
| `SIGNER_REQUIRED` | no | Provide a signer or use a Base-USDC prepare path |
|
|
311
|
+
| `SIGNER_CHAIN_MISMATCH` | no | Switch to the required chain and refresh any Relay quote before retrying |
|
|
312
|
+
| `SIGNER_CHAIN_UNAVAILABLE` | yes | Reconnect the wallet and prove its chain before retrying |
|
|
313
|
+
| `WATCH_TIMEOUT` | yes | Resume `watch(depositId)` later |
|
|
314
|
+
| `ESCROW_PAUSED` | yes | Back off; existing funds remain withdrawable |
|
|
312
315
|
|
|
313
316
|
`isCashError(err)` narrows unknown errors; `err.toJSON()` is safe for logs
|
|
314
317
|
and tool results.
|
|
@@ -319,8 +322,7 @@ Prove your integration against `environment: 'staging'` with a funded test
|
|
|
319
322
|
wallet. Never wait on a buyer - buyer-side is out of your scope:
|
|
320
323
|
|
|
321
324
|
1. `cashout()` a small amount (1–2 USDC) → capture `depositId` and, for a
|
|
322
|
-
restricted payout, every entry in `accessPolicyTxHashes
|
|
323
|
-
`disputeProtectionTxHashes`.
|
|
325
|
+
restricted payout, every entry in `accessPolicyTxHashes`.
|
|
324
326
|
2. `order(depositId)` shows `awaiting-buyer` (retry through indexer lag).
|
|
325
327
|
3. `orders(owner)` includes the deposit.
|
|
326
328
|
4. `withdraw(depositId)` → transaction succeeds.
|
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# @zkp2p/cash
|
|
2
2
|
|
|
3
3
|
Route Relay-supported EVM assets or NEAR Intents 1Click external deposits into
|
|
4
|
-
Base USDC, then cash out to fiat on Venmo, Revolut, Wise, Zelle, and
|
|
5
|
-
|
|
6
|
-
provider.
|
|
4
|
+
Base USDC, then cash out to fiat on Venmo, Revolut, Wise, Alipay, Zelle, and
|
|
5
|
+
more at a zero-spread Chainlink market rate with no centralized off-ramp
|
|
6
|
+
provider. Existing corridors bind the live oracle when a buyer signals;
|
|
7
|
+
Alipay/CNY fixes a fresh Ethereum Chainlink snapshot when the SDK prepares the
|
|
8
|
+
deposit.
|
|
7
9
|
|
|
8
10
|
Peer Cash is an **offramp-only** SDK for the [ZKP2P](https://peer.xyz)
|
|
9
11
|
protocol. The cashing-out user is the maker: their USDC becomes a deposit in
|
|
@@ -39,15 +41,15 @@ const fillStats = await cash.fillStats();
|
|
|
39
41
|
const pairStats = fillStats['venmo:USD'];
|
|
40
42
|
const multiCurrencyStats = fillStats['revolut:EUR+GBP+USD'];
|
|
41
43
|
|
|
42
|
-
const { depositId, accessPolicyTxHashes
|
|
44
|
+
const { depositId, accessPolicyTxHashes } = await cash.cashout(
|
|
43
45
|
{
|
|
44
46
|
amount: usdc(1000),
|
|
45
47
|
receive: { platform: 'venmo', currency: 'USD', payee: '@you' },
|
|
46
48
|
},
|
|
47
49
|
{ signer }, // any viem WalletClient on Base, including an EOA
|
|
48
50
|
);
|
|
49
|
-
//
|
|
50
|
-
console.log(depositId, accessPolicyTxHashes
|
|
51
|
+
// Venmo and PayPal return only after their access policy confirms.
|
|
52
|
+
console.log(depositId, accessPolicyTxHashes);
|
|
51
53
|
|
|
52
54
|
// One method can offer several currencies. The buyer chooses the fill
|
|
53
55
|
// currency, and each option resolves at its own live oracle rate.
|
|
@@ -64,7 +66,8 @@ const fastFill = await cash.cashout(
|
|
|
64
66
|
);
|
|
65
67
|
|
|
66
68
|
// One order can also offer several platforms (each at most once). The buyer
|
|
67
|
-
// picks the leg they can pay
|
|
69
|
+
// picks the leg they can pay. Inspect capabilities().platforms[].pricing for
|
|
70
|
+
// the exact rate-binding semantics of each corridor.
|
|
68
71
|
const widestReach = await cash.cashout(
|
|
69
72
|
{
|
|
70
73
|
amount: usdc(1000),
|
|
@@ -76,6 +79,15 @@ const widestReach = await cash.cashout(
|
|
|
76
79
|
{ signer },
|
|
77
80
|
);
|
|
78
81
|
|
|
82
|
+
// Alipay/CNY is the explicit creation-time exception. New Alipay payees need
|
|
83
|
+
// the identity attestation prepared by first-party Peer web.
|
|
84
|
+
const alipayEstimate = await cash.estimate({
|
|
85
|
+
amount: usdc(1000),
|
|
86
|
+
platform: 'alipay',
|
|
87
|
+
currency: 'CNY',
|
|
88
|
+
});
|
|
89
|
+
// alipayEstimate.binding === 'deposit-creation'
|
|
90
|
+
|
|
79
91
|
for await (const order of cash.watch(depositId)) {
|
|
80
92
|
console.log(order.state, order.explain());
|
|
81
93
|
if (order.state === 'delivered') break;
|
|
@@ -86,15 +98,14 @@ for await (const order of cash.watch(depositId)) {
|
|
|
86
98
|
|
|
87
99
|
Peer Cash and the general ZKP2P SDK serve different integration depths:
|
|
88
100
|
|
|
89
|
-
| Package | Use it when | Boundary
|
|
90
|
-
| ------------- | ------------------------------------------------- |
|
|
91
|
-
| `@zkp2p/cash` | Cash-out is the product | Offramp only. The user is always the maker, the destination is Base USDC, pricing is
|
|
92
|
-
| `@zkp2p/sdk` | You are composing directly with the Peer protocol | General maker and taker operations, deposits, intents, proofs, quotes, vaults, rate managers, referrals, hooks, and API helpers. Your application owns the workflow and protocol choices.
|
|
101
|
+
| Package | Use it when | Boundary |
|
|
102
|
+
| ------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
103
|
+
| `@zkp2p/cash` | Cash-out is the product | Offramp only. The user is always the maker, the destination is Base USDC, pricing is zero-spread Chainlink (signal-time by default; creation-time for Alipay/CNY), and the SDK owns the resumable order lifecycle. |
|
|
104
|
+
| `@zkp2p/sdk` | You are composing directly with the Peer protocol | General maker and taker operations, deposits, intents, proofs, quotes, vaults, rate managers, referrals, hooks, and API helpers. Your application owns the workflow and protocol choices. |
|
|
93
105
|
|
|
94
106
|
Peer Cash is a narrow facade over `@zkp2p/sdk`, not a replacement for it. It
|
|
95
|
-
cannot express custom spreads, buyer-side proof flows, vaults,
|
|
96
|
-
|
|
97
|
-
default is fixed rather than a configurable dispute surface.
|
|
107
|
+
cannot express custom spreads, buyer-side proof flows, vaults, disputes, or
|
|
108
|
+
arbitrary protocol operations.
|
|
98
109
|
|
|
99
110
|
## The core verbs
|
|
100
111
|
|
|
@@ -108,11 +119,10 @@ default is fixed rather than a configurable dispute surface.
|
|
|
108
119
|
| `relayStatus(requestId)` | Relay request status from the Relay SDK request path |
|
|
109
120
|
| `quoteNearIntentsSource(input)` | Signed 1Click quote with an origin-chain deposit address and optional memo |
|
|
110
121
|
| `submitNearIntentsDeposit(input)` / `nearIntentsStatus(input)` | Optionally register an origin tx, then track 1Click delivery/refund evidence |
|
|
111
|
-
| `estimate({ amount, currency }, { includeEta? })`
|
|
112
|
-
| `cashout(input, { signer })` | Creates the order; restricted methods then attach the Peer Pay merchant policy
|
|
113
|
-
| `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare external signing, resolve the deposit, then iterate
|
|
122
|
+
| `estimate({ amount, currency, platform? }, { includeEta? })` | Base USDC market-rate estimate with an explicit `binding`; optionally skip historical ETA |
|
|
123
|
+
| `cashout(input, { signer })` | Creates the order with any viem wallet; restricted methods then attach the Peer Pay merchant policy |
|
|
124
|
+
| `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare external signing, resolve the deposit, then iterate `accessPolicyPaymentMethods` for follow-ups |
|
|
114
125
|
| `prepareAccessPolicy(depositId, paymentMethod)` | Prepare one post-deposit, method-scoped Peer Pay merchant policy transaction |
|
|
115
|
-
| `prepareDisputeProtection(depositId, paymentMethod)` | Prepare one post-deposit, method-scoped dispute-protection transaction |
|
|
116
126
|
| `order(depositId)` / `orders(owner)` | Resume any order from its id alone; list all orders for a wallet |
|
|
117
127
|
| `watch(depositId)` | Async iterator: yields on every state change until terminal, abort, or timeout |
|
|
118
128
|
| `withdraw(depositId, { signer, amount? })` | The ONE unwind verb - partial with an `amount` (live intents don't block it), full close without (prunes expired intents first) |
|
|
@@ -141,12 +151,13 @@ mixed historical deposit.
|
|
|
141
151
|
|
|
142
152
|
## Payout rails and access policies
|
|
143
153
|
|
|
144
|
-
| Payout rail |
|
|
145
|
-
| --------------------- |
|
|
146
|
-
| Venmo
|
|
147
|
-
| PayPal | Same method-scoped
|
|
148
|
-
|
|
|
149
|
-
|
|
|
154
|
+
| Payout rail | Access-policy behavior | New payee registration |
|
|
155
|
+
| --------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
156
|
+
| Venmo | Peer Pay merchant policy attaches for that payment method | Curator validates the live handle |
|
|
157
|
+
| PayPal | Same method-scoped Peer Pay follow-up | Requires a Peer TEE browser-extension identity attestation |
|
|
158
|
+
| Cash App | No access-policy follow-up; non-chargebackable and no stake required | Curator validates the live handle |
|
|
159
|
+
| Wise | No access-policy follow-up | Requires a Peer TEE browser-extension identity attestation |
|
|
160
|
+
| Other supported rails | No access-policy follow-up; use `capabilities()` for currencies and format | Follow the `payeeHint`; live-validation behavior is described in the integration guide |
|
|
150
161
|
|
|
151
162
|
No platform requires an atomic access-policy flow. `cashout()` and `prepare()`
|
|
152
163
|
work with any viem `WalletClient`, including a local or externally connected
|
|
@@ -154,16 +165,18 @@ EOA; no Privy wallet or signer API is required. The deprecated
|
|
|
154
165
|
`requiresAtomicAccessPolicy` capability remains for wire compatibility and is
|
|
155
166
|
always `false`.
|
|
156
167
|
|
|
157
|
-
Venmo
|
|
158
|
-
merchant group
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
`
|
|
165
|
-
`
|
|
166
|
-
|
|
168
|
+
Venmo and PayPal cash-outs restrict intent signaling to the Peer Pay
|
|
169
|
+
merchant group by default. Each restricted payout method gets its own policy.
|
|
170
|
+
Signed `cashout()` creates the deposit first, then uses the same wallet to
|
|
171
|
+
submit and confirm every required policy transaction; this intentionally
|
|
172
|
+
leaves a brief interval where any taker can signal. Method-scoped dispute
|
|
173
|
+
protection is already default-on for these rails, so Cash neither
|
|
174
|
+
readiness-gates deposit creation nor submits
|
|
175
|
+
`setDisputeProtectionEnabled(true)`. Prepared integrations receive
|
|
176
|
+
`accessPolicyPaymentMethods`; after confirming `createDeposit`, call
|
|
177
|
+
`finalizePreparedCashout(receipt)`, then submit
|
|
178
|
+
`prepareAccessPolicy(depositId, paymentMethod)` once for every returned method.
|
|
179
|
+
Other platforms do not need the follow-up.
|
|
167
180
|
|
|
168
181
|
If policy attachment fails, `ACCESS_POLICY_CONFIGURATION_FAILED.recovery`
|
|
169
182
|
identifies the existing deposit and any submitted policy transaction. Never
|
|
@@ -171,12 +184,6 @@ create another cash-out. When `recovery.transactionHash` is present, inspect
|
|
|
171
184
|
that transaction before resubmitting; otherwise prepare the policy again with
|
|
172
185
|
the same depositor wallet.
|
|
173
186
|
|
|
174
|
-
If protection is not ready, `DISPUTE_PROTECTION_UNAVAILABLE` fails before the
|
|
175
|
-
deposit is created. If its post-deposit transaction fails,
|
|
176
|
-
`DISPUTE_PROTECTION_CONFIGURATION_FAILED.recovery` identifies the existing
|
|
177
|
-
deposit and submitted hash, if any; inspect it before using
|
|
178
|
-
`prepareDisputeProtection` for recovery and never repeat the cash-out.
|
|
179
|
-
|
|
180
187
|
`capabilities()` presents Zelle as one platform. A cashout with
|
|
181
188
|
`receive.platform: 'zelle'` attaches only the generic Zelle payment method to
|
|
182
189
|
the deposit. Bank-specific capture routing is outside this maker-side SDK and
|
|
@@ -313,16 +320,11 @@ they are available. A source-routed result includes both a flat
|
|
|
313
320
|
returned no hash. Treat it as potentially broadcast. Inspect recent Base
|
|
314
321
|
wallet activity and the supplied recovery action before any retry.
|
|
315
322
|
- `ACCESS_POLICY_CONFIGURATION_FAILED`: the deposit exists, but its required
|
|
316
|
-
Venmo
|
|
323
|
+
Venmo or PayPal policy was not confirmed. Do not cash out again;
|
|
317
324
|
inspect `recovery.transactionHash` when present, then retry
|
|
318
325
|
`prepareAccessPolicy(error.recovery.depositId, error.recovery.paymentMethod)`
|
|
319
326
|
only if the prior policy
|
|
320
327
|
transaction did not succeed.
|
|
321
|
-
- `DISPUTE_PROTECTION_UNAVAILABLE`: the protected contract stack is not ready;
|
|
322
|
-
no deposit was created. Retry later or use an unrestricted payout platform.
|
|
323
|
-
- `DISPUTE_PROTECTION_CONFIGURATION_FAILED`: the deposit exists, but one
|
|
324
|
-
required protection transaction was not confirmed. Inspect its recovery hash
|
|
325
|
-
before calling `prepareDisputeProtection`; never create another cash-out.
|
|
326
328
|
|
|
327
329
|
Wallet clients pinned to the wrong chain fail with `SIGNER_CHAIN_MISMATCH`
|
|
328
330
|
before a quote or transaction is submitted. Chainless wallets are checked
|
|
@@ -344,11 +346,15 @@ awaiting-buyer ──────────► matched ───────
|
|
|
344
346
|
returned ◄─────────────────┘
|
|
345
347
|
```
|
|
346
348
|
|
|
347
|
-
- **You are the maker.**
|
|
348
|
-
|
|
349
|
-
- **
|
|
350
|
-
|
|
351
|
-
|
|
349
|
+
- **You are the maker.** Pricing is zero-spread. Existing corridors resolve
|
|
350
|
+
from the on-chain Chainlink oracle when a buyer signals an intent.
|
|
351
|
+
- **Alipay/CNY binds earlier.** Base has no CNY oracle adapter, so the SDK reads
|
|
352
|
+
Chainlink CNY/USD on Ethereum, rejects stale or invalid data, and fixes the
|
|
353
|
+
resulting CNY-per-USDC maker floor when it prepares the deposit. A buyer may
|
|
354
|
+
signal at that floor or a better rate for the maker.
|
|
355
|
+
- **Read `binding`.** `estimate().binding` is `intent-signal` by default and
|
|
356
|
+
`deposit-creation` for Alipay/CNY. An estimate remains approximate until its
|
|
357
|
+
stated binding point.
|
|
352
358
|
- **ETA is historical.** `estimate().eta` is just `{ seconds, label }`, backed
|
|
353
359
|
by the same rolling 30-day, intent-attributed pair sampler as `fillStats()`,
|
|
354
360
|
measured from deposit creation to the first fulfilled fill through the pair.
|
|
@@ -404,9 +410,7 @@ analytics-only ERC-8021 codes such as `acme-app`.
|
|
|
404
410
|
- After a prepared restricted cash-out confirms, the host adapter must call
|
|
405
411
|
`finalizePreparedCashout(receipt)` and submit
|
|
406
412
|
`prepareAccessPolicy(depositId, paymentMethod)` for every value in
|
|
407
|
-
`accessPolicyPaymentMethods
|
|
408
|
-
`prepareDisputeProtection(depositId, paymentMethod)` for every value in
|
|
409
|
-
`disputeProtectionPaymentMethods`; these receipt/signing operations are
|
|
413
|
+
`accessPolicyPaymentMethods`; these receipt/signing operations are
|
|
410
414
|
`CashClient` methods, not built-in tool calls.
|
|
411
415
|
- Every error carries `code`, `retryable`, and a `remediation` sentence.
|
|
412
416
|
- Every order carries `nextActions: ('wait' | 'withdraw')[]` - no heuristics.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/engine/constants.ts
|
|
2
2
|
var BASE_CHAIN_ID = 8453;
|
|
3
3
|
var BASE_USDC_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
|
|
4
|
-
var CASH_RESTRICTED_PLATFORMS = /* @__PURE__ */ new Set(["venmo", "
|
|
4
|
+
var CASH_RESTRICTED_PLATFORMS = /* @__PURE__ */ new Set(["venmo", "paypal"]);
|
|
5
5
|
var CASH_ACCESS_GROUP_IDS = {
|
|
6
6
|
production: ["0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"],
|
|
7
7
|
preproduction: ["0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"],
|
|
@@ -50,16 +50,16 @@ function isCashError(value) {
|
|
|
50
50
|
var errors = {
|
|
51
51
|
oracleUnsupportedCurrency: (currency) => new CashError({
|
|
52
52
|
code: "ORACLE_UNSUPPORTED_CURRENCY",
|
|
53
|
-
message: `${currency}
|
|
53
|
+
message: `${currency} is not available in this Peer Cash payout corridor.`,
|
|
54
54
|
retryable: false,
|
|
55
|
-
remediation: `Pick a currency listed in capabilities()
|
|
55
|
+
remediation: `Pick a platform and currency listed together in capabilities().`
|
|
56
56
|
}),
|
|
57
57
|
oracleReadFailed: (currency, cause) => new CashError(
|
|
58
58
|
{
|
|
59
59
|
code: "ORACLE_READ_FAILED",
|
|
60
60
|
message: `The ${currency} market-rate oracle could not be read.`,
|
|
61
61
|
retryable: true,
|
|
62
|
-
remediation: `Retry
|
|
62
|
+
remediation: `Retry shortly or configure a healthy RPC for this corridor. Do not present a cached value as a fresh market rate.`
|
|
63
63
|
},
|
|
64
64
|
{ cause }
|
|
65
65
|
),
|
|
@@ -424,38 +424,6 @@ var errors = {
|
|
|
424
424
|
},
|
|
425
425
|
{ cause: context.cause }
|
|
426
426
|
),
|
|
427
|
-
disputeProtectionUnavailable: (state, cause) => new CashError(
|
|
428
|
-
{
|
|
429
|
-
code: "DISPUTE_PROTECTION_UNAVAILABLE",
|
|
430
|
-
message: `Dispute protection is not ready for restricted Peer Cash payouts${state ? ` (${state})` : ""}.`,
|
|
431
|
-
retryable: true,
|
|
432
|
-
remediation: `Retry after the dispute-protection contracts are active and accepting protected deposits, or choose an unrestricted payout platform. No cash-out deposit was created.`
|
|
433
|
-
},
|
|
434
|
-
{ cause }
|
|
435
|
-
),
|
|
436
|
-
disputeProtectionConfigurationFailed: (depositId, paymentMethod, context = {}) => new CashError(
|
|
437
|
-
{
|
|
438
|
-
code: "DISPUTE_PROTECTION_CONFIGURATION_FAILED",
|
|
439
|
-
message: `Cash-out deposit ${depositId} was created, but dispute protection could not be confirmed for ${paymentMethod}.`,
|
|
440
|
-
retryable: false,
|
|
441
|
-
remediation: `Do not create another cash-out. If recovery.transactionHash is present, inspect that dispute-protection transaction first. Otherwise, or if it is confirmed reverted, submit and confirm prepareDisputeProtection(recovery.depositId, recovery.paymentMethod) with the same depositor wallet.`,
|
|
442
|
-
recovery: {
|
|
443
|
-
kind: "configure-cashout-dispute-protection",
|
|
444
|
-
depositId,
|
|
445
|
-
paymentMethod,
|
|
446
|
-
...context.transactionHash ? { transactionHash: context.transactionHash } : {},
|
|
447
|
-
...context.source ? {
|
|
448
|
-
source: {
|
|
449
|
-
amount: context.source.amount.toString(),
|
|
450
|
-
...context.source.requestId ? { requestId: context.source.requestId } : {},
|
|
451
|
-
txHashes: context.source.txHashes,
|
|
452
|
-
...context.source.transactions ? { transactions: context.source.transactions } : {}
|
|
453
|
-
}
|
|
454
|
-
} : {}
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
{ cause: context.cause }
|
|
458
|
-
),
|
|
459
427
|
escrowPaused: () => new CashError({
|
|
460
428
|
code: "ESCROW_PAUSED",
|
|
461
429
|
message: `The escrow contract is paused; deposits are temporarily disabled.`,
|