@zkp2p/cash 0.4.11-rc.0 → 0.4.11-rc.2
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 +64 -56
- package/README.md +50 -27
- package/dist/{chunk-3PAHKWJ2.js → chunk-AAWRU4JK.js} +37 -19
- package/dist/{createCashClient-BDp6CSBD.d.cts → createCashClient-G8ksaisL.d.cts} +18 -5
- package/dist/{createCashClient-BDp6CSBD.d.ts → createCashClient-G8ksaisL.d.ts} +18 -5
- package/dist/index.cjs +241 -60
- package/dist/index.d.cts +76 -3
- package/dist/index.d.ts +76 -3
- package/dist/index.js +206 -43
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/tools.cjs +2 -2
- package/dist/tools.d.cts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +2 -2
- package/docs/lifecycle-and-recovery.md +59 -54
- package/examples/agent-tool-use.ts +4 -3
- package/examples/carpe-diem-provider-cashout/README.md +75 -0
- package/examples/carpe-diem-provider-cashout/cashout.ts +65 -0
- package/examples/mpp-merchant-cashout/README.md +5 -4
- package/examples/mpp-merchant-cashout/app.ts +6 -3
- package/examples/node-cashout.ts +6 -1
- package/examples/onchain-demo/README.md +106 -0
- package/examples/onchain-demo/package.json +15 -0
- package/examples/onchain-demo/script/boot-test.mjs +86 -0
- package/examples/onchain-demo/script/build.mjs +55 -0
- package/examples/onchain-demo/script/check-deployer.mjs +15 -0
- package/examples/onchain-demo/script/compile.mjs +51 -0
- package/examples/onchain-demo/script/deploy.mjs +228 -0
- package/examples/onchain-demo/script/gen-key.mjs +16 -0
- package/examples/onchain-demo/script/lib.mjs +30 -0
- package/examples/onchain-demo/script/test-contract.mjs +127 -0
- package/examples/onchain-demo/src/PeerCashPage.sol +122 -0
- package/examples/onchain-demo/src/app.js +593 -0
- package/examples/onchain-demo/src/shell.html +159 -0
- package/examples/onchain-demo/src/stubs/viem-chains.mjs +7 -0
- package/examples/onchain-demo/src/stubs/zod-locales.mjs +3 -0
- package/llms.txt +7 -8
- package/package.json +2 -2
- package/skills/peer-cash-integration/SKILL.md +19 -10
package/AGENTS.md
CHANGED
|
@@ -24,8 +24,10 @@ can withdraw an unmatched deposit.
|
|
|
24
24
|
the plan, submit the transactions in order, and wait for each receipt. After
|
|
25
25
|
`createDeposit` confirms, pass its receipt to `finalizePreparedCashout()` and
|
|
26
26
|
persist the returned `depositId`. If the original plan set
|
|
27
|
-
`
|
|
28
|
-
`prepareAccessPolicy(depositId)` with the depositor
|
|
27
|
+
a non-empty `accessPolicyPaymentMethods`, submit and confirm
|
|
28
|
+
`prepareAccessPolicy(depositId, paymentMethod)` with the depositor for each
|
|
29
|
+
returned method. Do the same for `disputeProtectionPaymentMethods` with
|
|
30
|
+
`prepareDisputeProtection(depositId, paymentMethod)`.
|
|
29
31
|
3. **You are a tool-use host** (MCP server, CLI) → import the manifest from
|
|
30
32
|
`@zkp2p/cash/tools` and map the tool names to the verbs above. Base-USDC
|
|
31
33
|
mutating tools return unsigned transactions. `cash_source_quote` is a quote,
|
|
@@ -44,14 +46,13 @@ deposit-level integration share instead of applying maker L1/L2.
|
|
|
44
46
|
|
|
45
47
|
**Platform caveats:**
|
|
46
48
|
|
|
47
|
-
- **Venmo, Cash App, and PayPal
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
depositor. Any viem EOA works; Privy is not required.
|
|
49
|
+
- **Venmo, Cash App, and PayPal use both restricted-rail defaults.** Signed
|
|
50
|
+
`cashout()` proves dispute protection is ready, confirms `createDeposit`,
|
|
51
|
+
then submits and confirms a method-scoped Peer Pay merchant policy and
|
|
52
|
+
dispute-protection opt-in for every restricted payout leg using the same
|
|
53
|
+
viem wallet. For `prepare()`, finalize the confirmed receipt, then iterate
|
|
54
|
+
both `accessPolicyPaymentMethods` and `disputeProtectionPaymentMethods` with
|
|
55
|
+
their matching prepare methods. Any viem EOA works; Privy is not required.
|
|
55
56
|
|
|
56
57
|
- **Wise and PayPal** carry `requiresIdentityAttestation: true`. A new curator
|
|
57
58
|
registration needs a signed maker identity attestation this SDK cannot mint
|
|
@@ -233,6 +234,10 @@ const route = await cash.nearIntentsStatus({
|
|
|
233
234
|
already exists. If `recovery.transactionHash` is present, inspect that policy
|
|
234
235
|
transaction first; prepare another policy transaction only when the previous
|
|
235
236
|
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.
|
|
236
241
|
- **`ORDER_NOT_FOUND` seconds after `cashout()` is indexer lag**, not a lost
|
|
237
242
|
deposit. The tx receipt you hold is the truth. Retry; `watch()` absorbs
|
|
238
243
|
this automatically.
|
|
@@ -257,51 +262,53 @@ const route = await cash.nearIntentsStatus({
|
|
|
257
262
|
|
|
258
263
|
Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
|
|
259
264
|
|
|
260
|
-
| Code
|
|
261
|
-
|
|
|
262
|
-
| `ORACLE_UNSUPPORTED_CURRENCY`
|
|
263
|
-
| `ORACLE_READ_FAILED`
|
|
264
|
-
| `UNSUPPORTED_PLATFORM`
|
|
265
|
-
| `UNSUPPORTED_PLATFORM_CURRENCY`
|
|
266
|
-
| `AMOUNT_BELOW_MINIMUM`
|
|
267
|
-
| `INVALID_INTENT_AMOUNT_RANGE`
|
|
268
|
-
| `INVALID_PAYOUT_CURRENCIES`
|
|
269
|
-
| `INVALID_PAYOUT_PLATFORMS`
|
|
270
|
-
| `PAYEE_VERIFICATION_REQUIRED`
|
|
271
|
-
| `PAYEE_REGISTRATION_FAILED`
|
|
272
|
-
| `ATOMIC_ACCESS_POLICY_REQUIRED`
|
|
273
|
-
| `ACCESS_POLICY_CONFIGURATION_FAILED`
|
|
274
|
-
| `
|
|
275
|
-
| `
|
|
276
|
-
| `
|
|
277
|
-
| `
|
|
278
|
-
| `
|
|
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
|
-
| `
|
|
265
|
+
| Code | Retryable | Agent action |
|
|
266
|
+
| ----------------------------------------- | --------- | ------------------------------------------------------------------------------------------ |
|
|
267
|
+
| `ORACLE_UNSUPPORTED_CURRENCY` | no | Re-pick currency from `capabilities()` |
|
|
268
|
+
| `ORACLE_READ_FAILED` | yes | Retry the read through a healthy Base RPC; do not present a cached value as live |
|
|
269
|
+
| `UNSUPPORTED_PLATFORM` | no | Re-pick platform from `capabilities()` |
|
|
270
|
+
| `UNSUPPORTED_PLATFORM_CURRENCY` | no | Use a currency listed for that platform |
|
|
271
|
+
| `AMOUNT_BELOW_MINIMUM` | no | Raise amount (hard floor $0.01, recommended at least 1 USDC) |
|
|
272
|
+
| `INVALID_INTENT_AMOUNT_RANGE` | no | Use a positive min, max at least min, and max no greater than amount |
|
|
273
|
+
| `INVALID_PAYOUT_CURRENCIES` | no | Pass one or more unique currencies listed for the platform |
|
|
274
|
+
| `INVALID_PAYOUT_PLATFORMS` | no | Pass one leg or an array of legs, using each platform at most once |
|
|
275
|
+
| `PAYEE_VERIFICATION_REQUIRED` | no | Use Peer web + TEE extension for new Wise/PayPal; reuse registered handles |
|
|
276
|
+
| `PAYEE_REGISTRATION_FAILED` | yes | Validate against `payeeHint`, then retry |
|
|
277
|
+
| `ATOMIC_ACCESS_POLICY_REQUIRED` | no | Deprecated compatibility code; current SDK flows never emit it |
|
|
278
|
+
| `ACCESS_POLICY_CONFIGURATION_FAILED` | no | Deposit exists; inspect policy tx first, attach only if needed; never repeat the cash-out. |
|
|
279
|
+
| `DISPUTE_PROTECTION_UNAVAILABLE` | yes | No deposit exists; wait for the protected stack or choose an unrestricted rail. |
|
|
280
|
+
| `DISPUTE_PROTECTION_CONFIGURATION_FAILED` | no | Deposit exists; inspect the protection tx and recover it without repeating the cash-out. |
|
|
281
|
+
| `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE` | no | Execute Relay with a signer first, then prepare a Base-USDC cashout |
|
|
282
|
+
| `SOURCE_RECIPIENT_MISMATCH` | no | Route Base USDC to the cashout depositor |
|
|
283
|
+
| `SOURCE_CAPABILITIES_FAILED` | yes | Retry discovery or fall back to Base USDC |
|
|
284
|
+
| `SOURCE_QUOTE_FAILED` | yes | Refresh capabilities and request a new canonical Base-USDC quote |
|
|
285
|
+
| `SOURCE_NONCE_MANAGER_REQUIRED` | no | Preflight; recreate the source signer with viem's `nonceManager`, then quote again |
|
|
286
|
+
| `SOURCE_EXECUTION_FAILED` | no | Inspect source transactions and Relay status before any retry |
|
|
287
|
+
| `SOURCE_DEPOSIT_SUBMISSION_FAILED` | yes | Retry only the 1Click notification; never resend source funds |
|
|
288
|
+
| `SOURCE_STATUS_FAILED` | yes | Retry only the status read |
|
|
289
|
+
| `SOURCE_ROUTE_COMPLETED_CASHOUT_FAILED` | no | Do not route again; retry Base-only with `recovery.amount` |
|
|
290
|
+
| `SOURCE_CASHOUT_SUBMISSION_UNKNOWN` | no | Inspect Base activity and orders; prove no deposit exists before retrying |
|
|
291
|
+
| `SOURCE_CASHOUT_STATUS_UNKNOWN` | no | Inspect `recovery.depositTxHash`; do not resubmit while its receipt is unknown |
|
|
292
|
+
| `INSUFFICIENT_TOKEN_BALANCE` | no | Fund the required token amount, then retry |
|
|
293
|
+
| `ALLOWANCE_NOT_VISIBLE` | yes | Approval mined but a stale RPC hid it; retry after it becomes visible |
|
|
294
|
+
| `TRANSACTION_REJECTED` | yes | Retry when ready and approve the wallet request |
|
|
295
|
+
| `TRANSACTION_FAILED` | no | Inspect the failed/reverted call before another action |
|
|
296
|
+
| `TRANSACTION_SUBMISSION_UNKNOWN` | no | Inspect Base wallet/protocol state and the recovery action before any resubmission |
|
|
297
|
+
| `TRANSACTION_STATUS_UNKNOWN` | no | Inspect `recovery.transactionHash` before resubmitting |
|
|
298
|
+
| `DEPOSIT_RESOLUTION_FAILED` | no | Inspect the confirmed Base receipt and recover the id from `DepositReceived` |
|
|
299
|
+
| `INVALID_DEPOSIT_ID` | no | Use the exact id returned by `cashout()` |
|
|
300
|
+
| `ORDER_NOT_FOUND` | yes | Retry through immediate indexer lag; otherwise verify the id |
|
|
301
|
+
| `INDEXER_LAG` | yes | Retry after a few seconds |
|
|
302
|
+
| `INDEXER_UNAVAILABLE` | yes | Retry only the failed read; keep the id/owner and never repeat a transaction |
|
|
303
|
+
| `ACTIVE_INTENT_BLOCKS_WITHDRAWAL` | yes | Wait for fill/expiry, or withdraw only the unlocked amount |
|
|
304
|
+
| `INSUFFICIENT_AVAILABLE_FUNDS` | yes | Lower the partial withdrawal amount |
|
|
305
|
+
| `NOTHING_TO_WITHDRAW` | no | Order is terminal; reconcile records |
|
|
306
|
+
| `ORDER_NOT_ACTIVE` | no | Start a new cashout instead of topping up |
|
|
307
|
+
| `SIGNER_REQUIRED` | no | Provide a signer or use a Base-USDC prepare path |
|
|
308
|
+
| `SIGNER_CHAIN_MISMATCH` | no | Switch to the required chain and refresh any Relay quote before retrying |
|
|
309
|
+
| `SIGNER_CHAIN_UNAVAILABLE` | yes | Reconnect the wallet and prove its chain before retrying |
|
|
310
|
+
| `WATCH_TIMEOUT` | yes | Resume `watch(depositId)` later |
|
|
311
|
+
| `ESCROW_PAUSED` | yes | Back off; existing funds remain withdrawable |
|
|
305
312
|
|
|
306
313
|
`isCashError(err)` narrows unknown errors; `err.toJSON()` is safe for logs
|
|
307
314
|
and tool results.
|
|
@@ -312,7 +319,8 @@ Prove your integration against `environment: 'staging'` with a funded test
|
|
|
312
319
|
wallet. Never wait on a buyer - buyer-side is out of your scope:
|
|
313
320
|
|
|
314
321
|
1. `cashout()` a small amount (1–2 USDC) → capture `depositId` and, for a
|
|
315
|
-
restricted payout, `
|
|
322
|
+
restricted payout, every entry in `accessPolicyTxHashes` and
|
|
323
|
+
`disputeProtectionTxHashes`.
|
|
316
324
|
2. `order(depositId)` shows `awaiting-buyer` (retry through indexer lag).
|
|
317
325
|
3. `orders(owner)` includes the deposit.
|
|
318
326
|
4. `withdraw(depositId)` → transaction succeeds.
|
package/README.md
CHANGED
|
@@ -39,15 +39,15 @@ const fillStats = await cash.fillStats();
|
|
|
39
39
|
const pairStats = fillStats['venmo:USD'];
|
|
40
40
|
const multiCurrencyStats = fillStats['revolut:EUR+GBP+USD'];
|
|
41
41
|
|
|
42
|
-
const { depositId,
|
|
42
|
+
const { depositId, accessPolicyTxHashes, disputeProtectionTxHashes } = await cash.cashout(
|
|
43
43
|
{
|
|
44
44
|
amount: usdc(1000),
|
|
45
45
|
receive: { platform: 'venmo', currency: 'USD', payee: '@you' },
|
|
46
46
|
},
|
|
47
47
|
{ signer }, // any viem WalletClient on Base, including an EOA
|
|
48
48
|
);
|
|
49
|
-
//
|
|
50
|
-
console.log(depositId,
|
|
49
|
+
// Restricted rails return only after both defaults confirm.
|
|
50
|
+
console.log(depositId, accessPolicyTxHashes, disputeProtectionTxHashes);
|
|
51
51
|
|
|
52
52
|
// One method can offer several currencies. The buyer chooses the fill
|
|
53
53
|
// currency, and each option resolves at its own live oracle rate.
|
|
@@ -92,8 +92,9 @@ Peer Cash and the general ZKP2P SDK serve different integration depths:
|
|
|
92
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. |
|
|
93
93
|
|
|
94
94
|
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
|
-
arbitrary protocol operations.
|
|
95
|
+
cannot express custom spreads, buyer-side proof flows, vaults, dispute
|
|
96
|
+
management, or arbitrary protocol operations. The protected restricted-rail
|
|
97
|
+
default is fixed rather than a configurable dispute surface.
|
|
97
98
|
|
|
98
99
|
## The core verbs
|
|
99
100
|
|
|
@@ -108,9 +109,10 @@ arbitrary protocol operations.
|
|
|
108
109
|
| `quoteNearIntentsSource(input)` | Signed 1Click quote with an origin-chain deposit address and optional memo |
|
|
109
110
|
| `submitNearIntentsDeposit(input)` / `nearIntentsStatus(input)` | Optionally register an origin tx, then track 1Click delivery/refund evidence |
|
|
110
111
|
| `estimate({ amount, currency }, { includeEta? })` | Base USDC oracle estimate; optionally skip the historical ETA for progressive rendering |
|
|
111
|
-
| `cashout(input, { signer })` | Creates the order
|
|
112
|
-
| `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare external signing, resolve the deposit, then
|
|
113
|
-
| `prepareAccessPolicy(depositId)`
|
|
112
|
+
| `cashout(input, { signer })` | Creates the order; restricted methods then attach the Peer Pay merchant policy and enable dispute protection |
|
|
113
|
+
| `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare external signing, resolve the deposit, then iterate both restricted-method follow-up lists |
|
|
114
|
+
| `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 |
|
|
114
116
|
| `order(depositId)` / `orders(owner)` | Resume any order from its id alone; list all orders for a wallet |
|
|
115
117
|
| `watch(depositId)` | Async iterator: yields on every state change until terminal, abort, or timeout |
|
|
116
118
|
| `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) |
|
|
@@ -139,12 +141,12 @@ mixed historical deposit.
|
|
|
139
141
|
|
|
140
142
|
## Payout rails and access policies
|
|
141
143
|
|
|
142
|
-
| Payout rail |
|
|
143
|
-
| --------------------- |
|
|
144
|
-
| Venmo / Cash App |
|
|
145
|
-
| PayPal | Same
|
|
146
|
-
| Wise | No
|
|
147
|
-
| Other supported rails | No
|
|
144
|
+
| Payout rail | Restricted-rail defaults | New payee registration |
|
|
145
|
+
| --------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
146
|
+
| Venmo / Cash App | Method-scoped Peer Pay merchant policy plus dispute protection | Curator validates the live handle |
|
|
147
|
+
| PayPal | Same method-scoped merchant-group and dispute-protection follow-ups | Requires a Peer TEE browser-extension identity attestation |
|
|
148
|
+
| Wise | No restricted-rail follow-up | Requires a Peer TEE browser-extension identity attestation |
|
|
149
|
+
| Other supported rails | No restricted-rail follow-up; use `capabilities()` for currencies and format | Follow the `payeeHint`; live-validation behavior is described in the integration guide |
|
|
148
150
|
|
|
149
151
|
No platform requires an atomic access-policy flow. `cashout()` and `prepare()`
|
|
150
152
|
work with any viem `WalletClient`, including a local or externally connected
|
|
@@ -152,14 +154,16 @@ EOA; no Privy wallet or signer API is required. The deprecated
|
|
|
152
154
|
`requiresAtomicAccessPolicy` capability remains for wire compatibility and is
|
|
153
155
|
always `false`.
|
|
154
156
|
|
|
155
|
-
Venmo, Cash App, and PayPal cash-outs restrict intent signaling to
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
`
|
|
161
|
-
`finalizePreparedCashout(receipt)
|
|
162
|
-
`prepareAccessPolicy(depositId)
|
|
157
|
+
Venmo, Cash App, and PayPal cash-outs restrict intent signaling to the Peer Pay
|
|
158
|
+
merchant group and enable dispute protection by default. Both defaults are
|
|
159
|
+
method-scoped. Signed `cashout()` first proves the dispute-protection stack is
|
|
160
|
+
ready, then creates the deposit and uses the same wallet to submit and confirm
|
|
161
|
+
both follow-ups. Prepared integrations receive matching
|
|
162
|
+
`accessPolicyPaymentMethods` and `disputeProtectionPaymentMethods`; after
|
|
163
|
+
confirming `createDeposit`, call `finalizePreparedCashout(receipt)`, then submit
|
|
164
|
+
`prepareAccessPolicy(depositId, paymentMethod)` and
|
|
165
|
+
`prepareDisputeProtection(depositId, paymentMethod)` for every returned method.
|
|
166
|
+
Other platforms do not need either follow-up.
|
|
163
167
|
|
|
164
168
|
If policy attachment fails, `ACCESS_POLICY_CONFIGURATION_FAILED.recovery`
|
|
165
169
|
identifies the existing deposit and any submitted policy transaction. Never
|
|
@@ -167,6 +171,12 @@ create another cash-out. When `recovery.transactionHash` is present, inspect
|
|
|
167
171
|
that transaction before resubmitting; otherwise prepare the policy again with
|
|
168
172
|
the same depositor wallet.
|
|
169
173
|
|
|
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
|
+
|
|
170
180
|
`capabilities()` presents Zelle as one platform. A cashout with
|
|
171
181
|
`receive.platform: 'zelle'` attaches only the generic Zelle payment method to
|
|
172
182
|
the deposit. Bank-specific capture routing is outside this maker-side SDK and
|
|
@@ -195,7 +205,7 @@ discovered and quoted by `@relayprotocol/relay-sdk`, not a static token
|
|
|
195
205
|
allowlist.
|
|
196
206
|
|
|
197
207
|
```ts
|
|
198
|
-
const { depositId,
|
|
208
|
+
const { depositId, accessPolicyTxHashes, source } = await cash.cashout(
|
|
199
209
|
{
|
|
200
210
|
amount: 10_000_000n, // exact input: 10 USDC in source-token base units
|
|
201
211
|
source: {
|
|
@@ -212,7 +222,7 @@ const { depositId, accessPolicyTxHash, source } = await cash.cashout(
|
|
|
212
222
|
// amount deposited into the cash-out order. It is not the route's actual output.
|
|
213
223
|
console.log(source?.amount, source?.requestId);
|
|
214
224
|
console.log(source?.transactions?.origin, source?.transactions?.destination);
|
|
215
|
-
console.log(
|
|
225
|
+
console.log(accessPolicyTxHashes); // one entry because this example uses Venmo
|
|
216
226
|
```
|
|
217
227
|
|
|
218
228
|
Routes that submit more than one source-chain transaction (approve, then
|
|
@@ -305,8 +315,14 @@ they are available. A source-routed result includes both a flat
|
|
|
305
315
|
- `ACCESS_POLICY_CONFIGURATION_FAILED`: the deposit exists, but its required
|
|
306
316
|
Venmo, Cash App, or PayPal policy was not confirmed. Do not cash out again;
|
|
307
317
|
inspect `recovery.transactionHash` when present, then retry
|
|
308
|
-
`prepareAccessPolicy(error.recovery.depositId)`
|
|
318
|
+
`prepareAccessPolicy(error.recovery.depositId, error.recovery.paymentMethod)`
|
|
319
|
+
only if the prior policy
|
|
309
320
|
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.
|
|
310
326
|
|
|
311
327
|
Wallet clients pinned to the wrong chain fail with `SIGNER_CHAIN_MISMATCH`
|
|
312
328
|
before a quote or transaction is submitted. Chainless wallets are checked
|
|
@@ -386,8 +402,12 @@ analytics-only ERC-8021 codes such as `acme-app`.
|
|
|
386
402
|
- Mutating tool calls return unsigned transactions by default; signing stays
|
|
387
403
|
with the host that owns custody, policy, and user approval.
|
|
388
404
|
- After a prepared restricted cash-out confirms, the host adapter must call
|
|
389
|
-
`finalizePreparedCashout(receipt)` and
|
|
390
|
-
|
|
405
|
+
`finalizePreparedCashout(receipt)` and submit
|
|
406
|
+
`prepareAccessPolicy(depositId, paymentMethod)` for every value in
|
|
407
|
+
`accessPolicyPaymentMethods`, plus
|
|
408
|
+
`prepareDisputeProtection(depositId, paymentMethod)` for every value in
|
|
409
|
+
`disputeProtectionPaymentMethods`; these receipt/signing operations are
|
|
410
|
+
`CashClient` methods, not built-in tool calls.
|
|
391
411
|
- Every error carries `code`, `retryable`, and a `remediation` sentence.
|
|
392
412
|
- Every order carries `nextActions: ('wait' | 'withdraw')[]` - no heuristics.
|
|
393
413
|
- Every wire type has a zod schema + JSON codec - state crosses process
|
|
@@ -424,7 +444,10 @@ Runnable first-party examples in [`examples/`](examples):
|
|
|
424
444
|
|
|
425
445
|
- [`node-cashout.ts`](examples/node-cashout.ts) - server-side cash-out with a private-key signer, plus order tracking.
|
|
426
446
|
- [`agent-tool-use.ts`](examples/agent-tool-use.ts) - wiring the verbs into an agent tool-use loop with host-side signing.
|
|
447
|
+
- [`carpe-diem-provider-cashout`](examples/carpe-diem-provider-cashout) - cash out confirmed Carpe Diem provider DIEM revenue through the connected Base wallet.
|
|
427
448
|
- [`mpp-merchant-cashout`](examples/mpp-merchant-cashout) - turn confirmed MPP merchant revenue into an unsigned Peer Cash plan while the merchant keeps custody and signing.
|
|
449
|
+
- [`onchain-demo`](examples/onchain-demo) - the Peer Cash Demo: the express sell flow as one page that bundles the SDK and is stored on Base as contract bytecode, served by an immutable ERC-5219 wrapper,
|
|
450
|
+
[live on Base](https://basescan.org/address/0x6d6c7af86bfc6f49f32761e1718cf982224cf343).
|
|
428
451
|
|
|
429
452
|
## Trust model, honestly
|
|
430
453
|
|
|
@@ -3,24 +3,9 @@ var BASE_CHAIN_ID = 8453;
|
|
|
3
3
|
var BASE_USDC_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
|
|
4
4
|
var CASH_RESTRICTED_PLATFORMS = /* @__PURE__ */ new Set(["venmo", "cashapp", "paypal"]);
|
|
5
5
|
var CASH_ACCESS_GROUP_IDS = {
|
|
6
|
-
production: [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"0xdf1c64c54745aa1ce00642a5874f97e3183bf5e993c1f559d0a37a4df0b803c7",
|
|
10
|
-
"0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"
|
|
11
|
-
],
|
|
12
|
-
preproduction: [
|
|
13
|
-
"0xb8747401b308d4891385620071b5916e9c61284f25c4611541c529703de5babf",
|
|
14
|
-
"0xf030f72e772f954059ca28f94974088aaf6ba37bb1f264df48843a3d0c221dc3",
|
|
15
|
-
"0xdf1c64c54745aa1ce00642a5874f97e3183bf5e993c1f559d0a37a4df0b803c7",
|
|
16
|
-
"0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"
|
|
17
|
-
],
|
|
18
|
-
staging: [
|
|
19
|
-
"0xf6133c227eab8ae7da1ee143945bf7f31204394f3ba801dc9691f8af6ca8efa5",
|
|
20
|
-
"0xa6beb459bc621e7b050e431736c1c3298da26356d7095719185e859d68f70d9e",
|
|
21
|
-
"0x9cded1332f25c3ee0a9a822a4c827d3fbd081a8d7b2ba39c49917ec1983b8d6c",
|
|
22
|
-
"0xc82c20c00033046a2f017b65532d7148a337282f17c73296663a530e49ba00f7"
|
|
23
|
-
]
|
|
6
|
+
production: ["0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"],
|
|
7
|
+
preproduction: ["0x174b8a29536721a3eae290bfd55651b85a53fc334b971d993fa93ed8dde15e48"],
|
|
8
|
+
staging: ["0xc82c20c00033046a2f017b65532d7148a337282f17c73296663a530e49ba00f7"]
|
|
24
9
|
};
|
|
25
10
|
var USDC_DECIMALS = 6;
|
|
26
11
|
var MARKET_SPREAD_BPS = 0;
|
|
@@ -420,11 +405,44 @@ var errors = {
|
|
|
420
405
|
code: "ACCESS_POLICY_CONFIGURATION_FAILED",
|
|
421
406
|
message: `Cash-out deposit ${depositId} was created, but its access policy could not be confirmed.`,
|
|
422
407
|
retryable: false,
|
|
423
|
-
remediation: `Do not create another cash-out. If recovery.transactionHash is present, inspect that policy transaction first. Otherwise, or if it is confirmed reverted, submit and confirm prepareAccessPolicy(recovery.depositId) with the same depositor wallet.`,
|
|
408
|
+
remediation: `Do not create another cash-out. If recovery.transactionHash is present, inspect that policy transaction first. Otherwise, or if it is confirmed reverted, submit and confirm prepareAccessPolicy(recovery.depositId, recovery.paymentMethod) with the same depositor wallet.`,
|
|
424
409
|
recovery: {
|
|
425
410
|
kind: "configure-cashout-access-policy",
|
|
426
411
|
depositId,
|
|
427
412
|
groupIds: [...groupIds],
|
|
413
|
+
...context.paymentMethod ? { paymentMethod: context.paymentMethod } : {},
|
|
414
|
+
...context.transactionHash ? { transactionHash: context.transactionHash } : {},
|
|
415
|
+
...context.source ? {
|
|
416
|
+
source: {
|
|
417
|
+
amount: context.source.amount.toString(),
|
|
418
|
+
...context.source.requestId ? { requestId: context.source.requestId } : {},
|
|
419
|
+
txHashes: context.source.txHashes,
|
|
420
|
+
...context.source.transactions ? { transactions: context.source.transactions } : {}
|
|
421
|
+
}
|
|
422
|
+
} : {}
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
{ cause: context.cause }
|
|
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,
|
|
428
446
|
...context.transactionHash ? { transactionHash: context.transactionHash } : {},
|
|
429
447
|
...context.source ? {
|
|
430
448
|
source: {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Address, WalletClient, Hash, Log, Transport } from 'viem';
|
|
1
|
+
import { Address, WalletClient, Hash, Hex, Log, Transport } from 'viem';
|
|
2
2
|
import { IndexerIntentStatus, Zkp2pClient, IndexerIntent, CurrencyType, PreparedTransaction, RuntimeEnv } from '@zkp2p/sdk';
|
|
3
3
|
import { Execute, RelayClient, RelayChain, ProgressData } from '@relayprotocol/relay-sdk';
|
|
4
4
|
|
|
@@ -486,7 +486,8 @@ interface CashPlatformCapability {
|
|
|
486
486
|
requiresIdentityAttestation: boolean;
|
|
487
487
|
/**
|
|
488
488
|
* @deprecated Always false. This does not report the sequential restricted-
|
|
489
|
-
* platform
|
|
489
|
+
* platform defaults; prepared hosts must inspect both method lists on
|
|
490
|
+
* `PrepareResult`.
|
|
490
491
|
*/
|
|
491
492
|
requiresAtomicAccessPolicy: boolean;
|
|
492
493
|
}
|
|
@@ -764,8 +765,12 @@ interface CashoutResult {
|
|
|
764
765
|
onchainDepositId: bigint;
|
|
765
766
|
/** Optimistic snapshot (`awaiting-buyer`); poll `order(depositId)` for live state. */
|
|
766
767
|
order: CashOrder;
|
|
767
|
-
/**
|
|
768
|
+
/** Last confirmed access-policy transaction. Retained for single-policy compatibility. */
|
|
768
769
|
accessPolicyTxHash?: Hash;
|
|
770
|
+
/** Confirmed method-scoped policy transactions for restricted payout legs. */
|
|
771
|
+
accessPolicyTxHashes?: Hash[];
|
|
772
|
+
/** Confirmed method-scoped dispute-protection transactions for restricted payout legs. */
|
|
773
|
+
disputeProtectionTxHashes?: Hash[];
|
|
769
774
|
/** Present when `cashout()` first routed a source asset through Relay. */
|
|
770
775
|
source?: {
|
|
771
776
|
/** Conservative Base USDC amount deposited (Relay's guaranteed minimum output). */
|
|
@@ -794,6 +799,12 @@ interface PrepareResult {
|
|
|
794
799
|
};
|
|
795
800
|
/** Whether the host must submit and confirm the policy after `createDeposit` confirms. */
|
|
796
801
|
accessPolicyRequired: boolean;
|
|
802
|
+
/** Method hashes that each require a post-deposit Peer Pay policy transaction. */
|
|
803
|
+
accessPolicyPaymentMethods: Hex[];
|
|
804
|
+
/** Whether the host must enable dispute protection after `createDeposit` confirms. */
|
|
805
|
+
disputeProtectionRequired: boolean;
|
|
806
|
+
/** Method hashes that each require a post-deposit dispute-protection transaction. */
|
|
807
|
+
disputeProtectionPaymentMethods: Hex[];
|
|
797
808
|
}
|
|
798
809
|
/** Confirmed createDeposit receipt from an externally executed prepare() plan. */
|
|
799
810
|
interface PreparedCashoutReceipt {
|
|
@@ -870,8 +881,10 @@ interface CashClient {
|
|
|
870
881
|
prepare(input: CashoutInput): Promise<PrepareResult>;
|
|
871
882
|
/** Resolve an externally executed createDeposit receipt into resumable cash-out state. */
|
|
872
883
|
finalizePreparedCashout(receipt: PreparedCashoutReceipt): CashoutResult;
|
|
873
|
-
/** Prepare
|
|
874
|
-
prepareAccessPolicy(depositId: string): PreparedTransaction;
|
|
884
|
+
/** Prepare one method-scoped Peer Pay follow-up for a restricted cash-out. */
|
|
885
|
+
prepareAccessPolicy(depositId: string, paymentMethod: Hex): PreparedTransaction;
|
|
886
|
+
/** Prepare one method-scoped dispute-protection follow-up for a restricted cash-out. */
|
|
887
|
+
prepareDisputeProtection(depositId: string, paymentMethod: Hex): Promise<PreparedTransaction>;
|
|
875
888
|
/** 3 - Observe: resumable from `depositId` alone; no session state anywhere. */
|
|
876
889
|
order(depositId: string): Promise<CashOrder>;
|
|
877
890
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Address, WalletClient, Hash, Log, Transport } from 'viem';
|
|
1
|
+
import { Address, WalletClient, Hash, Hex, Log, Transport } from 'viem';
|
|
2
2
|
import { IndexerIntentStatus, Zkp2pClient, IndexerIntent, CurrencyType, PreparedTransaction, RuntimeEnv } from '@zkp2p/sdk';
|
|
3
3
|
import { Execute, RelayClient, RelayChain, ProgressData } from '@relayprotocol/relay-sdk';
|
|
4
4
|
|
|
@@ -486,7 +486,8 @@ interface CashPlatformCapability {
|
|
|
486
486
|
requiresIdentityAttestation: boolean;
|
|
487
487
|
/**
|
|
488
488
|
* @deprecated Always false. This does not report the sequential restricted-
|
|
489
|
-
* platform
|
|
489
|
+
* platform defaults; prepared hosts must inspect both method lists on
|
|
490
|
+
* `PrepareResult`.
|
|
490
491
|
*/
|
|
491
492
|
requiresAtomicAccessPolicy: boolean;
|
|
492
493
|
}
|
|
@@ -764,8 +765,12 @@ interface CashoutResult {
|
|
|
764
765
|
onchainDepositId: bigint;
|
|
765
766
|
/** Optimistic snapshot (`awaiting-buyer`); poll `order(depositId)` for live state. */
|
|
766
767
|
order: CashOrder;
|
|
767
|
-
/**
|
|
768
|
+
/** Last confirmed access-policy transaction. Retained for single-policy compatibility. */
|
|
768
769
|
accessPolicyTxHash?: Hash;
|
|
770
|
+
/** Confirmed method-scoped policy transactions for restricted payout legs. */
|
|
771
|
+
accessPolicyTxHashes?: Hash[];
|
|
772
|
+
/** Confirmed method-scoped dispute-protection transactions for restricted payout legs. */
|
|
773
|
+
disputeProtectionTxHashes?: Hash[];
|
|
769
774
|
/** Present when `cashout()` first routed a source asset through Relay. */
|
|
770
775
|
source?: {
|
|
771
776
|
/** Conservative Base USDC amount deposited (Relay's guaranteed minimum output). */
|
|
@@ -794,6 +799,12 @@ interface PrepareResult {
|
|
|
794
799
|
};
|
|
795
800
|
/** Whether the host must submit and confirm the policy after `createDeposit` confirms. */
|
|
796
801
|
accessPolicyRequired: boolean;
|
|
802
|
+
/** Method hashes that each require a post-deposit Peer Pay policy transaction. */
|
|
803
|
+
accessPolicyPaymentMethods: Hex[];
|
|
804
|
+
/** Whether the host must enable dispute protection after `createDeposit` confirms. */
|
|
805
|
+
disputeProtectionRequired: boolean;
|
|
806
|
+
/** Method hashes that each require a post-deposit dispute-protection transaction. */
|
|
807
|
+
disputeProtectionPaymentMethods: Hex[];
|
|
797
808
|
}
|
|
798
809
|
/** Confirmed createDeposit receipt from an externally executed prepare() plan. */
|
|
799
810
|
interface PreparedCashoutReceipt {
|
|
@@ -870,8 +881,10 @@ interface CashClient {
|
|
|
870
881
|
prepare(input: CashoutInput): Promise<PrepareResult>;
|
|
871
882
|
/** Resolve an externally executed createDeposit receipt into resumable cash-out state. */
|
|
872
883
|
finalizePreparedCashout(receipt: PreparedCashoutReceipt): CashoutResult;
|
|
873
|
-
/** Prepare
|
|
874
|
-
prepareAccessPolicy(depositId: string): PreparedTransaction;
|
|
884
|
+
/** Prepare one method-scoped Peer Pay follow-up for a restricted cash-out. */
|
|
885
|
+
prepareAccessPolicy(depositId: string, paymentMethod: Hex): PreparedTransaction;
|
|
886
|
+
/** Prepare one method-scoped dispute-protection follow-up for a restricted cash-out. */
|
|
887
|
+
prepareDisputeProtection(depositId: string, paymentMethod: Hex): Promise<PreparedTransaction>;
|
|
875
888
|
/** 3 - Observe: resumable from `depositId` alone; no session state anywhere. */
|
|
876
889
|
order(depositId: string): Promise<CashOrder>;
|
|
877
890
|
/**
|