@zkp2p/cash 0.1.2 → 0.1.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 +92 -28
- package/README.md +69 -25
- package/dist/chunk-P3KYZ2FX.js +373 -0
- package/dist/{createCashClient-iHuGgjH_.d.cts → createCashClient-BbkfxILl.d.cts} +37 -8
- package/dist/{createCashClient-iHuGgjH_.d.ts → createCashClient-BbkfxILl.d.ts} +37 -8
- package/dist/index.cjs +1099 -254
- package/dist/index.d.cts +1554 -74
- package/dist/index.d.ts +1554 -74
- package/dist/index.js +886 -248
- package/dist/react.cjs +239 -59
- package/dist/react.d.cts +6 -4
- package/dist/react.d.ts +6 -4
- package/dist/react.js +236 -59
- package/dist/tools.cjs +36 -36
- package/dist/tools.d.cts +282 -3
- package/dist/tools.d.ts +282 -3
- package/dist/tools.js +36 -36
- package/docs/lifecycle-and-recovery.md +278 -0
- package/examples/agent-tool-use.ts +122 -0
- package/examples/node-cashout.ts +79 -0
- package/llms.txt +23 -5
- package/package.json +51 -21
- package/skills/peer-cash-integration/SKILL.md +82 -19
- package/dist/chunk-FKVPZVFH.js +0 -188
- package/dist/chunk-FKVPZVFH.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/react.cjs.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/tools.cjs.map +0 -1
- package/dist/tools.js.map +0 -1
package/AGENTS.md
CHANGED
|
@@ -19,8 +19,10 @@ can withdraw an unmatched deposit.
|
|
|
19
19
|
the plan, submit the transactions in order, and wait for each receipt.
|
|
20
20
|
3. **You are a tool-use host** (MCP server, CLI) → import the manifest from
|
|
21
21
|
`@zkp2p/cash/tools` and map the tool names to the verbs above. Base-USDC
|
|
22
|
-
mutating tools return unsigned transactions
|
|
23
|
-
|
|
22
|
+
mutating tools return unsigned transactions. `cash_source_quote` is a quote,
|
|
23
|
+
not an execution tool; the host must execute and confirm Relay through its
|
|
24
|
+
signer/runtime, use `cash_source_status` to monitor it, then call the
|
|
25
|
+
Base-USDC `cash_cashout` tool. Never pass `source` into `prepare()`.
|
|
24
26
|
|
|
25
27
|
Every transaction (including approves) carries ERC-8021 attribution:
|
|
26
28
|
`peer-cash`, then any `referrer` codes from `createCashClient` options, then
|
|
@@ -28,10 +30,11 @@ the Base builder code.
|
|
|
28
30
|
|
|
29
31
|
**Two platform caveats, both surfaced in `capabilities()`:**
|
|
30
32
|
|
|
31
|
-
- **Wise and PayPal** carry `requiresIdentityAttestation: true`.
|
|
33
|
+
- **Wise and PayPal** carry `requiresIdentityAttestation: true`. A new curator
|
|
32
34
|
registration needs a signed maker identity attestation this SDK cannot mint
|
|
33
|
-
(it comes from the Peer app/extension).
|
|
34
|
-
|
|
35
|
+
(it comes from the Peer app/extension). An already-registered handle can be
|
|
36
|
+
reused with bare payee data. A new handle without its attestation fails with
|
|
37
|
+
`PAYEE_VERIFICATION_REQUIRED` before funds move on-chain.
|
|
35
38
|
- **Venmo, Revolut, Cash App, Monzo** validate the handle against the live
|
|
36
39
|
platform at registration - the account must exist. The rest (Zelle, Chime,
|
|
37
40
|
etc.) are format-checked only. Match handles to the `payeeHint`.
|
|
@@ -71,17 +74,56 @@ if (order.nextActions.includes('withdraw') && shouldUnwind) {
|
|
|
71
74
|
}
|
|
72
75
|
```
|
|
73
76
|
|
|
77
|
+
Signed source route (exact-input cash-out):
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const routed = await cash.cashout(
|
|
81
|
+
{
|
|
82
|
+
amount: sourceAmount, // source-token base units
|
|
83
|
+
source: {
|
|
84
|
+
chainId: sourceChainId,
|
|
85
|
+
currency: sourceToken,
|
|
86
|
+
tradeType: 'EXACT_INPUT',
|
|
87
|
+
},
|
|
88
|
+
receive,
|
|
89
|
+
},
|
|
90
|
+
{ signer, sourceSigner },
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// Guaranteed minimum Base USDC and exact order deposit amount, not actual route output.
|
|
94
|
+
console.log(routed.source?.amount);
|
|
95
|
+
console.log(routed.source?.transactions?.origin, routed.source?.transactions?.destination);
|
|
96
|
+
```
|
|
97
|
+
|
|
74
98
|
## Rules that prevent wrong behavior
|
|
75
99
|
|
|
76
100
|
- **Never promise a rate.** `estimate()` is `kind: 'oracle-estimate'`; the
|
|
77
101
|
binding rate resolves at the oracle when a buyer fills. Do not display or
|
|
78
102
|
log it as a locked price.
|
|
79
103
|
- **Do not invent an ETA.** Use `estimate().eta`: `{ seconds, label }` backed
|
|
80
|
-
by rolling
|
|
81
|
-
|
|
104
|
+
by rolling 30-day indexer data from zero-spread (`spreadBps: 0`) market-rate
|
|
105
|
+
deposits in the same payout corridor, measured from deposit creation to first
|
|
106
|
+
fill. Use `order.explain()` for live order state.
|
|
82
107
|
- **Do not hardcode Relay source assets.** Use Relay SDK-backed EVM
|
|
83
108
|
`capabilities({ includeRelaySources: true })` and `cashout({ source, ... })`.
|
|
84
|
-
Destination is always Base USDC. Non-Base source chains require
|
|
109
|
+
Destination is always Base USDC. Non-Base source chains require
|
|
110
|
+
`sourceSigner`. Use `EXACT_INPUT` in high-level cash-out flows so `amount`
|
|
111
|
+
remains source-token base units. `source.amount` is Relay's guaranteed
|
|
112
|
+
minimum output and the exact Base USDC deposit amount.
|
|
113
|
+
- **Persist source evidence.** A routed result carries `requestId`, flat
|
|
114
|
+
`txHashes`, and chain-aware `transactions.origin` / `.destination` arrays.
|
|
115
|
+
- **Never repeat a completed or uncertain route.** On `SOURCE_EXECUTION_FAILED`,
|
|
116
|
+
inspect its Relay request and transaction recovery evidence. On
|
|
117
|
+
`SOURCE_ROUTE_COMPLETED_CASHOUT_FAILED`, retry without `source` using
|
|
118
|
+
`BigInt(err.recovery.amount)`. On `SOURCE_CASHOUT_SUBMISSION_UNKNOWN`, inspect
|
|
119
|
+
Base wallet activity and `orders(err.recovery.depositor)`. On
|
|
120
|
+
`SOURCE_CASHOUT_STATUS_UNKNOWN`, inspect
|
|
121
|
+
`err.recovery.depositTxHash`; do not submit again until its receipt is known.
|
|
122
|
+
- **Never resubmit an unknown Base transaction.**
|
|
123
|
+
`TRANSACTION_SUBMISSION_UNKNOWN` means a call returned no hash but may have
|
|
124
|
+
broadcast; follow its recovery action and inspect wallet/protocol state.
|
|
125
|
+
`TRANSACTION_STATUS_UNKNOWN` means the returned hash may already have
|
|
126
|
+
succeeded. Inspect `err.recovery.transactionHash` first.
|
|
85
127
|
- **`ORDER_NOT_FOUND` seconds after `cashout()` is indexer lag**, not a lost
|
|
86
128
|
deposit. The tx receipt you hold is the truth. Retry; `watch()` absorbs
|
|
87
129
|
this automatically.
|
|
@@ -96,31 +138,53 @@ if (order.nextActions.includes('withdraw') && shouldUnwind) {
|
|
|
96
138
|
- **Serialize with the codecs.** `orderToJson`/`orderFromJson` etc. round-trip
|
|
97
139
|
bigints losslessly and re-attach `explain()`. Plain `JSON.stringify` on a
|
|
98
140
|
live object throws on bigints.
|
|
141
|
+
- **Environment selects the curator.** Preproduction defaults to
|
|
142
|
+
`https://api-preprod.zkp2p.xyz`; staging defaults to
|
|
143
|
+
`https://api-staging.zkp2p.xyz`. Use `curatorUrl` only for an explicit
|
|
144
|
+
override.
|
|
99
145
|
|
|
100
146
|
## Error → remediation table
|
|
101
147
|
|
|
102
148
|
Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
|
|
103
149
|
|
|
104
|
-
| Code
|
|
105
|
-
|
|
|
106
|
-
| `ORACLE_UNSUPPORTED_CURRENCY`
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
| `
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
116
|
-
| `
|
|
117
|
-
| `
|
|
118
|
-
| `
|
|
119
|
-
| `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
150
|
+
| Code | Retryable | Agent action |
|
|
151
|
+
| --------------------------------------- | --------- | ------------------------------------------------------------------------------------------ |
|
|
152
|
+
| `ORACLE_UNSUPPORTED_CURRENCY` | no | Re-pick currency from `capabilities()` |
|
|
153
|
+
| `ORACLE_READ_FAILED` | yes | Retry the read through a healthy Base RPC; do not present a cached value as live |
|
|
154
|
+
| `UNSUPPORTED_PLATFORM` | no | Re-pick platform from `capabilities()` |
|
|
155
|
+
| `UNSUPPORTED_PLATFORM_CURRENCY` | no | Use a currency listed for that platform |
|
|
156
|
+
| `AMOUNT_BELOW_MINIMUM` | no | Raise amount (hard floor $0.01, recommended at least 1 USDC) |
|
|
157
|
+
| `INVALID_INTENT_AMOUNT_RANGE` | no | Use a positive min, max at least min, and max no greater than amount |
|
|
158
|
+
| `PAYEE_VERIFICATION_REQUIRED` | no | Register a new Wise/PayPal payee through Peer; an existing registered handle can be reused |
|
|
159
|
+
| `PAYEE_REGISTRATION_FAILED` | yes | Validate against `payeeHint`, then retry |
|
|
160
|
+
| `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE` | no | Execute Relay with a signer first, then prepare a Base-USDC cashout |
|
|
161
|
+
| `SOURCE_RECIPIENT_MISMATCH` | no | Route Base USDC to the cashout depositor |
|
|
162
|
+
| `SOURCE_CAPABILITIES_FAILED` | yes | Retry discovery or fall back to Base USDC |
|
|
163
|
+
| `SOURCE_QUOTE_FAILED` | yes | Refresh capabilities and request a new canonical Base-USDC quote |
|
|
164
|
+
| `SOURCE_EXECUTION_FAILED` | no | Inspect source transactions and Relay status before any retry |
|
|
165
|
+
| `SOURCE_STATUS_FAILED` | yes | Retry only the status read |
|
|
166
|
+
| `SOURCE_ROUTE_COMPLETED_CASHOUT_FAILED` | no | Do not route again; retry Base-only with `recovery.amount` |
|
|
167
|
+
| `SOURCE_CASHOUT_SUBMISSION_UNKNOWN` | no | Inspect Base activity and orders; prove no deposit exists before retrying |
|
|
168
|
+
| `SOURCE_CASHOUT_STATUS_UNKNOWN` | no | Inspect `recovery.depositTxHash`; do not resubmit while its receipt is unknown |
|
|
169
|
+
| `INSUFFICIENT_TOKEN_BALANCE` | no | Fund the required token amount, then retry |
|
|
170
|
+
| `ALLOWANCE_NOT_VISIBLE` | yes | Approval mined but a stale RPC hid it; retry after it becomes visible |
|
|
171
|
+
| `TRANSACTION_FAILED` | no | Inspect the failed/reverted call before another action |
|
|
172
|
+
| `TRANSACTION_SUBMISSION_UNKNOWN` | no | Inspect Base wallet/protocol state and the recovery action before any resubmission |
|
|
173
|
+
| `TRANSACTION_STATUS_UNKNOWN` | no | Inspect `recovery.transactionHash` before resubmitting |
|
|
174
|
+
| `DEPOSIT_RESOLUTION_FAILED` | no | Inspect the confirmed Base receipt and recover the id from `DepositReceived` |
|
|
175
|
+
| `INVALID_DEPOSIT_ID` | no | Use the exact id returned by `cashout()` |
|
|
176
|
+
| `ORDER_NOT_FOUND` | yes | Retry through immediate indexer lag; otherwise verify the id |
|
|
177
|
+
| `INDEXER_LAG` | yes | Retry after a few seconds |
|
|
178
|
+
| `INDEXER_UNAVAILABLE` | yes | Retry only the failed read; keep the id/owner and never repeat a transaction |
|
|
179
|
+
| `ACTIVE_INTENT_BLOCKS_WITHDRAWAL` | yes | Wait for fill/expiry, or withdraw only the unlocked amount |
|
|
180
|
+
| `INSUFFICIENT_AVAILABLE_FUNDS` | yes | Lower the partial withdrawal amount |
|
|
181
|
+
| `NOTHING_TO_WITHDRAW` | no | Order is terminal; reconcile records |
|
|
182
|
+
| `ORDER_NOT_ACTIVE` | no | Start a new cashout instead of topping up |
|
|
183
|
+
| `SIGNER_REQUIRED` | no | Provide a signer or use a Base-USDC prepare path |
|
|
184
|
+
| `SIGNER_CHAIN_MISMATCH` | no | Switch to the required chain and refresh any Relay quote before retrying |
|
|
185
|
+
| `SIGNER_CHAIN_UNAVAILABLE` | yes | Reconnect the wallet and prove its chain before retrying |
|
|
186
|
+
| `WATCH_TIMEOUT` | yes | Resume `watch(depositId)` later |
|
|
187
|
+
| `ESCROW_PAUSED` | yes | Back off; existing funds remain withdrawable |
|
|
124
188
|
|
|
125
189
|
`isCashError(err)` narrows unknown errors; `err.toJSON()` is safe for logs
|
|
126
190
|
and tool results.
|
package/README.md
CHANGED
|
@@ -40,13 +40,21 @@ Source asset path:
|
|
|
40
40
|
```ts
|
|
41
41
|
const { depositId, source } = await cash.cashout(
|
|
42
42
|
{
|
|
43
|
-
amount:
|
|
44
|
-
source: {
|
|
43
|
+
amount: 10_000_000n, // exact input: 10 USDC in source-token base units
|
|
44
|
+
source: {
|
|
45
|
+
chainId: 1,
|
|
46
|
+
currency: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
47
|
+
tradeType: 'EXACT_INPUT',
|
|
48
|
+
},
|
|
45
49
|
receive: { platform: 'venmo', currency: 'USD', payee: { offchainId: '@you' } },
|
|
46
50
|
},
|
|
47
51
|
{ signer, sourceSigner },
|
|
48
52
|
);
|
|
49
|
-
|
|
53
|
+
|
|
54
|
+
// source.amount is Relay's guaranteed minimum Base USDC output and the exact
|
|
55
|
+
// amount deposited into the cash-out order. It is not the route's actual output.
|
|
56
|
+
console.log(source?.amount, source?.requestId);
|
|
57
|
+
console.log(source?.transactions?.origin, source?.transactions?.destination);
|
|
50
58
|
```
|
|
51
59
|
|
|
52
60
|
## The core verbs
|
|
@@ -69,24 +77,59 @@ Base-USDC cashout, withdraw, and top-up have unsigned counterparts (`prepare`,
|
|
|
69
77
|
`prepareWithdraw`, `prepareTopUp`). The unsigned path returns raw `txs[]` plus
|
|
70
78
|
a same-index `steps[]` plan such as `approve`, `createDeposit`, or
|
|
71
79
|
`withdrawDeposit`, so wallets, AA systems, and agents can show what each
|
|
72
|
-
transaction does before signing.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
transaction does before signing. `prepare()` is Base-USDC-only and rejects a
|
|
81
|
+
`source` with `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE`. A signer-backed app can
|
|
82
|
+
use `cashout({ source }, { signer, sourceSigner })`; a custody-separated host
|
|
83
|
+
must execute and confirm its Relay route before preparing the Base-USDC
|
|
84
|
+
cashout. Every Peer Cash transaction, including approves, carries ERC-8021
|
|
85
|
+
attribution: `peer-cash` first, your own `referrer` code(s) after it.
|
|
77
86
|
|
|
78
87
|
The default/minimal flow is unchanged: pass Base USDC base units to
|
|
79
88
|
`estimate()` and `cashout()`. For any other source asset, pass `source` to
|
|
80
|
-
`cashout()` with a source-chain signer
|
|
81
|
-
|
|
89
|
+
`cashout()` with a source-chain signer. The SDK settles the Base allowance,
|
|
90
|
+
executes the Relay route into Base USDC, then creates the Peer Cash order. Use
|
|
91
|
+
`EXACT_INPUT` in cash-out UIs so `amount` always means source-token base units.
|
|
92
|
+
The destination is always canonical Base USDC
|
|
82
93
|
(`8453:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913`); source support is
|
|
83
94
|
discovered and quoted by `@relayprotocol/relay-sdk`, not a static token
|
|
84
95
|
allowlist.
|
|
85
96
|
|
|
86
|
-
`capabilities()` tells you which platforms need a verified identity
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
`capabilities()` tells you which platforms need a verified identity for a new
|
|
98
|
+
payee registration (`requiresIdentityAttestation` - Wise and PayPal today).
|
|
99
|
+
An already-registered Wise or PayPal handle can be reused with bare payee data.
|
|
100
|
+
A new handle without its signed attestation fails during curator registration
|
|
101
|
+
with `PAYEE_VERIFICATION_REQUIRED`, before funds move on-chain.
|
|
102
|
+
|
|
103
|
+
## Source-route recovery
|
|
104
|
+
|
|
105
|
+
Persist `depositId`, transaction hashes, and the Relay `requestId` as soon as
|
|
106
|
+
they are available. A source-routed result includes both a flat
|
|
107
|
+
`source.txHashes` list and chain-aware `source.transactions.origin` /
|
|
108
|
+
`.destination` entries.
|
|
109
|
+
|
|
110
|
+
- `SOURCE_ROUTE_COMPLETED_CASHOUT_FAILED`: Relay completed, but the Base
|
|
111
|
+
cashout was not created. Do not route again. Retry a Base-USDC-only
|
|
112
|
+
`cashout()` with `BigInt(error.recovery.amount)`.
|
|
113
|
+
- `SOURCE_CASHOUT_SUBMISSION_UNKNOWN`: Relay completed, but Base submission
|
|
114
|
+
returned no transaction hash. Inspect recent Base wallet activity and
|
|
115
|
+
`orders(error.recovery.depositor)` to prove no deposit exists before retrying.
|
|
116
|
+
- `SOURCE_CASHOUT_STATUS_UNKNOWN`: the Base cashout transaction was submitted,
|
|
117
|
+
but its receipt is unknown. Do not route or submit again. Inspect
|
|
118
|
+
`error.recovery.depositTxHash`; recover the `depositId` from its
|
|
119
|
+
`DepositReceived` log if it succeeded, or use the recovery amount for a
|
|
120
|
+
Base-USDC-only retry only after confirming it reverted.
|
|
121
|
+
- `TRANSACTION_SUBMISSION_UNKNOWN`: a Base-only cashout or another mutation
|
|
122
|
+
returned no hash. Treat it as potentially broadcast. Inspect recent Base
|
|
123
|
+
wallet activity and the supplied recovery action before any retry.
|
|
124
|
+
|
|
125
|
+
Wallet clients pinned to the wrong chain fail with `SIGNER_CHAIN_MISMATCH`
|
|
126
|
+
before a quote or transaction is submitted. Chainless wallets are checked
|
|
127
|
+
through `getChainId()`; a disconnected wallet returns retryable
|
|
128
|
+
`SIGNER_CHAIN_UNAVAILABLE`. Indexer and oracle transport outages are typed as
|
|
129
|
+
retryable `INDEXER_UNAVAILABLE` and `ORACLE_READ_FAILED` reads; retry the read
|
|
130
|
+
without repeating any transaction. `TRANSACTION_STATUS_UNKNOWN` carries the
|
|
131
|
+
submitted hash in `error.recovery.transactionHash` so recovery never depends
|
|
132
|
+
on parsing an error message.
|
|
90
133
|
|
|
91
134
|
## Lifecycle
|
|
92
135
|
|
|
@@ -105,7 +148,9 @@ awaiting-buyer ──────────► matched ───────
|
|
|
105
148
|
fills. `estimate()` says "approximately"; nothing in this API pretends to
|
|
106
149
|
lock a price.
|
|
107
150
|
- **ETA is historical.** `estimate().eta` is just `{ seconds, label }`, backed
|
|
108
|
-
by rolling
|
|
151
|
+
by rolling 30-day indexer data from zero-spread (`spreadBps: 0`) market-rate
|
|
152
|
+
deposits in the same payout corridor, measured from deposit creation to first
|
|
153
|
+
fulfilled fill.
|
|
109
154
|
- **Everything is resumable.** An order is reconstructed from the chain by
|
|
110
155
|
`depositId` alone. Close the tab, switch devices, crash the process - then
|
|
111
156
|
call `order(depositId)`.
|
|
@@ -145,10 +190,11 @@ React is an optional peer dependency - the root entry never imports it.
|
|
|
145
190
|
|
|
146
191
|
## Environments
|
|
147
192
|
|
|
148
|
-
`production` | `preproduction` | `staging`
|
|
149
|
-
indexer.
|
|
150
|
-
`
|
|
151
|
-
|
|
193
|
+
`production` | `preproduction` | `staging` selects contracts, curator, and
|
|
194
|
+
indexer. Preproduction defaults to `https://api-preprod.zkp2p.xyz`; staging
|
|
195
|
+
defaults to `https://api-staging.zkp2p.xyz`. Indexer, curator, and Relay
|
|
196
|
+
options remain overridable via `createCashClient` options. Base USDC on Base
|
|
197
|
+
is the default source and the only destination asset for cashout orders.
|
|
152
198
|
|
|
153
199
|
## Install
|
|
154
200
|
|
|
@@ -165,12 +211,10 @@ Runnable first-party examples in [`examples/`](examples):
|
|
|
165
211
|
|
|
166
212
|
## Trust model, honestly
|
|
167
213
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
Onchain custody is still enforced by the protocol: only the contract holds
|
|
173
|
-
funds, and only you can withdraw an unmatched deposit.
|
|
214
|
+
The published package depends on `@zkp2p/sdk` for protocol internals; that
|
|
215
|
+
dependency currently ships from private source. Onchain custody is enforced by
|
|
216
|
+
the protocol: only the contract holds funds, and only the maker can withdraw
|
|
217
|
+
an unmatched deposit.
|
|
174
218
|
|
|
175
219
|
## License
|
|
176
220
|
|