@zkp2p/cash 0.4.7 → 0.4.9
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 +32 -17
- package/README.md +130 -94
- package/dist/{chunk-SYE25ICW.js → chunk-4LPWKZMW.js} +17 -8
- package/dist/{createCashClient-CL1SCbqF.d.cts → createCashClient-Br7uu4lQ.d.cts} +9 -8
- package/dist/{createCashClient-CL1SCbqF.d.ts → createCashClient-Br7uu4lQ.d.ts} +9 -8
- package/dist/index.cjs +99 -28
- package/dist/index.d.cts +307 -2910
- package/dist/index.d.ts +307 -2910
- package/dist/index.js +84 -22
- 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 +20 -14
- package/examples/agent-tool-use.ts +8 -2
- package/examples/node-cashout.ts +8 -6
- package/llms.txt +13 -3
- package/package.json +12 -12
- package/skills/peer-cash-integration/SKILL.md +26 -6
package/AGENTS.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @zkp2p/cash - agent integration manual
|
|
2
2
|
|
|
3
|
+
> This file is the shipped manual for agents **using** the package. To
|
|
4
|
+
> contribute to the SDK itself (layout, ground rules, CI, releasing), start at
|
|
5
|
+
> [CLAUDE.md](https://github.com/zkp2p/peer-cash/blob/main/CLAUDE.md).
|
|
6
|
+
|
|
3
7
|
You are integrating Peer Cash: an offramp that routes any Relay-supported EVM
|
|
4
8
|
source asset into Base USDC, then converts Base USDC to fiat (Venmo, Revolut,
|
|
5
9
|
Wise, Zelle, ...) at the live Chainlink market rate. The user whose USDC you
|
|
@@ -9,16 +13,18 @@ can withdraw an unmatched deposit.
|
|
|
9
13
|
|
|
10
14
|
## Decision tree: pick your entry point
|
|
11
15
|
|
|
12
|
-
1. **You control a signer in-process** (viem `WalletClient`,
|
|
13
|
-
|
|
14
|
-
directly.
|
|
16
|
+
1. **You control a signer in-process** (any viem `WalletClient`, including a
|
|
17
|
+
local or externally connected EOA) → use `cashout()` / `topUp()` /
|
|
18
|
+
`withdraw()` directly. No Privy wallet is required.
|
|
15
19
|
2. **Signing happens elsewhere** (AA bundler, policy engine, custody service,
|
|
16
20
|
human approval step) → use `prepare()` / `prepareTopUp()` /
|
|
17
21
|
`prepareWithdraw()`. Each returns unsigned `txs[]`
|
|
18
22
|
(`{ to, data, value, chainId }`) plus same-index `steps[]` labels; inspect
|
|
19
23
|
the plan, submit the transactions in order, and wait for each receipt. After
|
|
20
24
|
`createDeposit` confirms, pass its receipt to `finalizePreparedCashout()` and
|
|
21
|
-
persist the returned `depositId`.
|
|
25
|
+
persist the returned `depositId`. If the original plan set
|
|
26
|
+
`accessPolicyRequired: true`, submit and confirm
|
|
27
|
+
`prepareAccessPolicy(depositId)` with the depositor next.
|
|
22
28
|
3. **You are a tool-use host** (MCP server, CLI) → import the manifest from
|
|
23
29
|
`@zkp2p/cash/tools` and map the tool names to the verbs above. Base-USDC
|
|
24
30
|
mutating tools return unsigned transactions. `cash_source_quote` is a quote,
|
|
@@ -32,19 +38,23 @@ Every transaction (including approves) carries ERC-8021 attribution:
|
|
|
32
38
|
code. The namespaced referral marker gives its Peer Privy wallet the direct
|
|
33
39
|
deposit-level integration share instead of applying maker L1/L2.
|
|
34
40
|
|
|
35
|
-
**Platform caveats
|
|
41
|
+
**Platform caveats:**
|
|
36
42
|
|
|
37
|
-
- **Venmo, Cash App, and PayPal
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
- **Venmo, Cash App, and PayPal restrict who can signal intents by default.**
|
|
44
|
+
If any payout leg uses one of these platforms, signed
|
|
45
|
+
`cashout()` confirms `createDeposit`, then submits and confirms the Plus, Pro,
|
|
46
|
+
Peer Makers, and Peer Pay policy using the same viem wallet. This is a
|
|
47
|
+
deliberate non-atomic follow-up with a brief unprotected interval. For
|
|
48
|
+
`prepare()`, check `accessPolicyRequired`; after `createDeposit` confirms,
|
|
49
|
+
finalize its receipt and submit `prepareAccessPolicy(depositId)` with the
|
|
50
|
+
depositor. Any viem EOA works; Privy is not required.
|
|
42
51
|
|
|
43
52
|
- **Wise and PayPal** carry `requiresIdentityAttestation: true`. A new curator
|
|
44
53
|
registration needs a signed maker identity attestation this SDK cannot mint
|
|
45
|
-
(it
|
|
46
|
-
reused with bare payee data. A new handle
|
|
47
|
-
`PAYEE_VERIFICATION_REQUIRED` before funds
|
|
54
|
+
(first-party Peer web obtains it through the Peer TEE browser extension).
|
|
55
|
+
An already-registered handle can be reused with bare payee data. A new handle
|
|
56
|
+
without its attestation fails with `PAYEE_VERIFICATION_REQUIRED` before funds
|
|
57
|
+
move on-chain.
|
|
48
58
|
- **Venmo, Revolut, Cash App, Monzo** validate the handle against the live
|
|
49
59
|
platform at registration - the account must exist. The rest (Zelle, Chime,
|
|
50
60
|
etc.) are format-checked only. Match handles to the `payeeHint`.
|
|
@@ -178,6 +188,10 @@ console.log(routed.source?.transactions?.origin, routed.source?.transactions?.de
|
|
|
178
188
|
broadcast; follow its recovery action and inspect wallet/protocol state.
|
|
179
189
|
`TRANSACTION_STATUS_UNKNOWN` means the returned hash may already have
|
|
180
190
|
succeeded. Inspect `err.recovery.transactionHash` first.
|
|
191
|
+
- **Never repeat a cash-out after an access-policy failure.** The deposit
|
|
192
|
+
already exists. If `recovery.transactionHash` is present, inspect that policy
|
|
193
|
+
transaction first; prepare another policy transaction only when the previous
|
|
194
|
+
one is absent or confirmed reverted.
|
|
181
195
|
- **`ORDER_NOT_FOUND` seconds after `cashout()` is indexer lag**, not a lost
|
|
182
196
|
deposit. The tx receipt you hold is the truth. Retry; `watch()` absorbs
|
|
183
197
|
this automatically.
|
|
@@ -212,10 +226,10 @@ Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
|
|
|
212
226
|
| `INVALID_INTENT_AMOUNT_RANGE` | no | Use a positive min, max at least min, and max no greater than amount |
|
|
213
227
|
| `INVALID_PAYOUT_CURRENCIES` | no | Pass one or more unique currencies listed for the platform |
|
|
214
228
|
| `INVALID_PAYOUT_PLATFORMS` | no | Pass one leg or an array of legs, using each platform at most once |
|
|
215
|
-
| `PAYEE_VERIFICATION_REQUIRED` | no |
|
|
229
|
+
| `PAYEE_VERIFICATION_REQUIRED` | no | Use Peer web + TEE extension for new Wise/PayPal; reuse registered handles |
|
|
216
230
|
| `PAYEE_REGISTRATION_FAILED` | yes | Validate against `payeeHint`, then retry |
|
|
217
|
-
| `ATOMIC_ACCESS_POLICY_REQUIRED` | no |
|
|
218
|
-
| `ACCESS_POLICY_CONFIGURATION_FAILED` | no |
|
|
231
|
+
| `ATOMIC_ACCESS_POLICY_REQUIRED` | no | Deprecated compatibility code; current SDK flows never emit it |
|
|
232
|
+
| `ACCESS_POLICY_CONFIGURATION_FAILED` | no | Deposit exists; inspect policy tx first, attach only if needed; never repeat the cash-out. |
|
|
219
233
|
| `SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE` | no | Execute Relay with a signer first, then prepare a Base-USDC cashout |
|
|
220
234
|
| `SOURCE_RECIPIENT_MISMATCH` | no | Route Base USDC to the cashout depositor |
|
|
221
235
|
| `SOURCE_CAPABILITIES_FAILED` | yes | Retry discovery or fall back to Base USDC |
|
|
@@ -255,7 +269,8 @@ and tool results.
|
|
|
255
269
|
Prove your integration against `environment: 'staging'` with a funded test
|
|
256
270
|
wallet. Never wait on a buyer - buyer-side is out of your scope:
|
|
257
271
|
|
|
258
|
-
1. `cashout()` a small amount (1–2 USDC) → capture `depositId
|
|
272
|
+
1. `cashout()` a small amount (1–2 USDC) → capture `depositId` and, for a
|
|
273
|
+
restricted payout, `accessPolicyTxHash`.
|
|
259
274
|
2. `order(depositId)` shows `awaiting-buyer` (retry through indexer lag).
|
|
260
275
|
3. `orders(owner)` includes the deposit.
|
|
261
276
|
4. `withdraw(depositId)` → transaction succeeds.
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @zkp2p/cash
|
|
2
2
|
|
|
3
|
-
Route any Relay-supported EVM source asset into Base USDC, then cash out to
|
|
4
|
-
on Venmo, Revolut, Wise, Zelle, and more at the live Chainlink market
|
|
5
|
-
with zero spread and no centralized off-ramp provider.
|
|
3
|
+
Route any Relay-supported EVM source asset into Base USDC, then cash out to
|
|
4
|
+
fiat on Venmo, Revolut, Wise, Zelle, and more at the live Chainlink market
|
|
5
|
+
rate, with zero spread and no centralized off-ramp provider.
|
|
6
6
|
|
|
7
7
|
Peer Cash is an **offramp-only** SDK for the [ZKP2P](https://peer.xyz)
|
|
8
8
|
protocol. The cashing-out user is the maker: their USDC becomes a deposit in
|
|
@@ -12,18 +12,13 @@ No hosted widget, no provider custody, no quote engine to maintain.
|
|
|
12
12
|
|
|
13
13
|
**[npm](https://www.npmjs.com/package/@zkp2p/cash)** · **[Lifecycle and recovery](docs/lifecycle-and-recovery.md)** · **[Agent integration manual](AGENTS.md)**
|
|
14
14
|
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
Peer Cash and the general ZKP2P SDK serve different integration depths:
|
|
15
|
+
## Install
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
| `@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. |
|
|
17
|
+
```sh
|
|
18
|
+
npm install @zkp2p/cash viem
|
|
19
|
+
```
|
|
23
20
|
|
|
24
|
-
|
|
25
|
-
cannot express custom spreads, buyer-side proof flows, vaults, disputes, or
|
|
26
|
-
arbitrary protocol operations.
|
|
21
|
+
## Quickstart
|
|
27
22
|
|
|
28
23
|
```ts
|
|
29
24
|
import { createCashClient, usdc } from '@zkp2p/cash';
|
|
@@ -43,13 +38,15 @@ const fillStats = await cash.fillStats();
|
|
|
43
38
|
const pairStats = fillStats['venmo:USD'];
|
|
44
39
|
const multiCurrencyStats = fillStats['revolut:EUR+GBP+USD'];
|
|
45
40
|
|
|
46
|
-
const { depositId } = await cash.cashout(
|
|
41
|
+
const { depositId, accessPolicyTxHash } = await cash.cashout(
|
|
47
42
|
{
|
|
48
43
|
amount: usdc(1000),
|
|
49
|
-
receive: { platform: '
|
|
44
|
+
receive: { platform: 'venmo', currency: 'USD', payee: '@you' },
|
|
50
45
|
},
|
|
51
|
-
{ signer }, // any viem WalletClient on Base
|
|
46
|
+
{ signer }, // any viem WalletClient on Base, including an EOA
|
|
52
47
|
);
|
|
48
|
+
// Venmo, Cash App, and PayPal return only after their access policy confirms.
|
|
49
|
+
console.log(depositId, accessPolicyTxHash);
|
|
53
50
|
|
|
54
51
|
// One method can offer several currencies. The buyer chooses the fill
|
|
55
52
|
// currency, and each option resolves at its own live oracle rate.
|
|
@@ -71,7 +68,7 @@ const widestReach = await cash.cashout(
|
|
|
71
68
|
{
|
|
72
69
|
amount: usdc(1000),
|
|
73
70
|
receive: [
|
|
74
|
-
{ platform: '
|
|
71
|
+
{ platform: 'venmo', currency: 'USD', payee: '@you' },
|
|
75
72
|
{ platform: 'revolut', currencies: ['EUR', 'GBP'], payee: { offchainId: 'revtag' } },
|
|
76
73
|
],
|
|
77
74
|
},
|
|
@@ -84,52 +81,18 @@ for await (const order of cash.watch(depositId)) {
|
|
|
84
81
|
}
|
|
85
82
|
```
|
|
86
83
|
|
|
87
|
-
##
|
|
88
|
-
|
|
89
|
-
Use the same six-character referral code shown in your Peer mobile or web app.
|
|
90
|
-
No API key, registration transaction, or separate receiving address is needed:
|
|
91
|
-
the referral code already belongs to your Peer Privy wallet.
|
|
92
|
-
|
|
93
|
-
```ts
|
|
94
|
-
const cash = createCashClient({
|
|
95
|
-
environment: 'production',
|
|
96
|
-
referralCode: 'ABC123',
|
|
97
|
-
});
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
The SDK normalizes the value and stamps `peer-ref-ABC123` into ERC-8021
|
|
101
|
-
attribution on the deposit transaction. When that liquidity is filled, Curator
|
|
102
|
-
pays the code owner 50 bps, capped by the configured Peer service fee. This is
|
|
103
|
-
the deposit-level integration path: it replaces the maker L1/L2 referral split
|
|
104
|
-
for that deposit instead of enrolling the cashing-out user as your referee.
|
|
105
|
-
|
|
106
|
-
The mapping is permanent. If you later customize your displayed Peer referral
|
|
107
|
-
code, open deposits carrying the old code still pay the same wallet. Include at
|
|
108
|
-
most one `peer-ref-XXXXXX` marker; an unknown or conflicting marker receives no
|
|
109
|
-
integration share. The existing `referrer` option remains available for
|
|
110
|
-
analytics-only ERC-8021 codes such as `acme-app`.
|
|
84
|
+
## Pick the right SDK
|
|
111
85
|
|
|
112
|
-
|
|
86
|
+
Peer Cash and the general ZKP2P SDK serve different integration depths:
|
|
113
87
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
source: {
|
|
119
|
-
chainId: 1,
|
|
120
|
-
currency: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
121
|
-
tradeType: 'EXACT_INPUT',
|
|
122
|
-
},
|
|
123
|
-
receive: { platform: 'chime', currency: 'USD', payee: { offchainId: '$you' } },
|
|
124
|
-
},
|
|
125
|
-
{ signer, sourceSigner },
|
|
126
|
-
);
|
|
88
|
+
| Package | Use it when | Boundary |
|
|
89
|
+
| ------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
90
|
+
| `@zkp2p/cash` | Cash-out is the product | Offramp only. The user is always the maker, the destination is Base USDC, pricing is the live Chainlink rate at fill with zero spread, and the SDK owns the resumable order lifecycle. |
|
|
91
|
+
| `@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. |
|
|
127
92
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
console.log(source?.transactions?.origin, source?.transactions?.destination);
|
|
132
|
-
```
|
|
93
|
+
Peer Cash is a narrow facade over `@zkp2p/sdk`, not a replacement for it. It
|
|
94
|
+
cannot express custom spreads, buyer-side proof flows, vaults, disputes, or
|
|
95
|
+
arbitrary protocol operations.
|
|
133
96
|
|
|
134
97
|
## The core verbs
|
|
135
98
|
|
|
@@ -141,9 +104,9 @@ console.log(source?.transactions?.origin, source?.transactions?.destination);
|
|
|
141
104
|
| `quoteSource(input)` / `executeSourceQuote(quote, { signer })` | Relay SDK EVM source routing into Base USDC before cashout |
|
|
142
105
|
| `relayStatus(requestId)` | Relay request status from the Relay SDK request path |
|
|
143
106
|
| `estimate({ amount, currency }, { includeEta? })` | Base USDC oracle estimate; optionally skip the historical ETA for progressive rendering |
|
|
144
|
-
| `cashout(input, { signer })` |
|
|
145
|
-
| `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare
|
|
146
|
-
| `prepareAccessPolicy(depositId)` |
|
|
107
|
+
| `cashout(input, { signer })` | Creates the order with any viem wallet; Venmo, Cash App, and PayPal then attach the canonical access groups |
|
|
108
|
+
| `prepare(input)` / `finalizePreparedCashout(receipt)` | Prepare external signing, resolve the deposit, then check `accessPolicyRequired` for the follow-up |
|
|
109
|
+
| `prepareAccessPolicy(depositId)` | Prepare the post-deposit Plus, Pro, Peer Makers, and Peer Pay policy transaction |
|
|
147
110
|
| `order(depositId)` / `orders(owner)` | Resume any order from its id alone; list all orders for a wallet |
|
|
148
111
|
| `watch(depositId)` | Async iterator: yields on every state change until terminal, abort, or timeout |
|
|
149
112
|
| `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) |
|
|
@@ -165,31 +128,55 @@ Peer Cash transaction, including approves, carries ERC-8021 attribution:
|
|
|
165
128
|
`peer-cash` first, optional `peer-ref-XXXXXX` from `referralCode` next, and your
|
|
166
129
|
analytics-only `referrer` code(s) after it.
|
|
167
130
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
flow.
|
|
131
|
+
Order reads fail closed against the same active catalog. If any method on an
|
|
132
|
+
indexed deposit is unsupported, `orders()` excludes the whole deposit and
|
|
133
|
+
`order()` returns `ORDER_NOT_FOUND`; Peer Cash never partially reclassifies a
|
|
134
|
+
mixed historical deposit.
|
|
135
|
+
|
|
136
|
+
## Payout rails and access policies
|
|
137
|
+
|
|
138
|
+
| Payout rail | Access-policy behavior | New payee registration |
|
|
139
|
+
| --------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
140
|
+
| Venmo / Cash App | Four groups attach after deposit confirmation | Curator validates the live handle |
|
|
141
|
+
| PayPal | Same four-group follow-up | Requires a Peer TEE browser-extension identity attestation |
|
|
142
|
+
| Wise | No access-policy follow-up | Requires a Peer TEE browser-extension identity attestation |
|
|
143
|
+
| 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 |
|
|
144
|
+
|
|
145
|
+
No platform requires an atomic access-policy flow. `cashout()` and `prepare()`
|
|
146
|
+
work with any viem `WalletClient`, including a local or externally connected
|
|
147
|
+
EOA; no Privy wallet or signer API is required. The deprecated
|
|
148
|
+
`requiresAtomicAccessPolicy` capability remains for wire compatibility and is
|
|
149
|
+
always `false`.
|
|
150
|
+
|
|
151
|
+
Venmo, Cash App, and PayPal cash-outs restrict intent signaling to Plus, Pro,
|
|
152
|
+
Peer Makers, and Peer Pay groups by default. If any payout leg uses one of
|
|
153
|
+
those platforms, signed `cashout()` creates the deposit first, then uses the
|
|
154
|
+
same wallet to submit and confirm the policy transaction; this intentionally
|
|
155
|
+
leaves a brief non-atomic interval. Prepared integrations receive
|
|
156
|
+
`accessPolicyRequired: true` and, after confirming `createDeposit`, must call
|
|
157
|
+
`finalizePreparedCashout(receipt)` followed by
|
|
158
|
+
`prepareAccessPolicy(depositId)`. Other platforms do not need the follow-up.
|
|
159
|
+
|
|
160
|
+
If policy attachment fails, `ACCESS_POLICY_CONFIGURATION_FAILED.recovery`
|
|
161
|
+
identifies the existing deposit and any submitted policy transaction. Never
|
|
162
|
+
create another cash-out. When `recovery.transactionHash` is present, inspect
|
|
163
|
+
that transaction before resubmitting; otherwise prepare the policy again with
|
|
164
|
+
the same depositor wallet.
|
|
183
165
|
|
|
184
166
|
`capabilities()` presents Zelle as one platform. A cashout with
|
|
185
167
|
`receive.platform: 'zelle'` attaches only the generic Zelle payment method to
|
|
186
168
|
the deposit. Bank-specific capture routing is outside this maker-side SDK and
|
|
187
169
|
never changes the on-chain payment method.
|
|
188
170
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
`
|
|
192
|
-
|
|
171
|
+
`capabilities()` tells you which platforms need a verified identity for a new
|
|
172
|
+
payee registration (`requiresIdentityAttestation` - Wise and PayPal today).
|
|
173
|
+
The SDK accepts an `identityAttestation` in structured payee data but does not
|
|
174
|
+
mint one. First-party Peer web obtains it through the Peer TEE browser
|
|
175
|
+
extension. An already-registered Wise or PayPal handle can be reused with bare
|
|
176
|
+
payee data. A new handle without its signed attestation fails during curator
|
|
177
|
+
registration with `PAYEE_VERIFICATION_REQUIRED`, before funds move on-chain.
|
|
178
|
+
|
|
179
|
+
## Source routing (any EVM asset in)
|
|
193
180
|
|
|
194
181
|
The default/minimal flow is unchanged: pass Base USDC base units to
|
|
195
182
|
`estimate()` and `cashout()`. For any other source asset, pass `source` to
|
|
@@ -201,6 +188,27 @@ The destination is always canonical Base USDC
|
|
|
201
188
|
discovered and quoted by `@relayprotocol/relay-sdk`, not a static token
|
|
202
189
|
allowlist.
|
|
203
190
|
|
|
191
|
+
```ts
|
|
192
|
+
const { depositId, accessPolicyTxHash, source } = await cash.cashout(
|
|
193
|
+
{
|
|
194
|
+
amount: 10_000_000n, // exact input: 10 USDC in source-token base units
|
|
195
|
+
source: {
|
|
196
|
+
chainId: 1,
|
|
197
|
+
currency: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
|
|
198
|
+
tradeType: 'EXACT_INPUT',
|
|
199
|
+
},
|
|
200
|
+
receive: { platform: 'venmo', currency: 'USD', payee: { offchainId: '@you' } },
|
|
201
|
+
},
|
|
202
|
+
{ signer, sourceSigner },
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// source.amount is Relay's guaranteed minimum Base USDC output and the exact
|
|
206
|
+
// amount deposited into the cash-out order. It is not the route's actual output.
|
|
207
|
+
console.log(source?.amount, source?.requestId);
|
|
208
|
+
console.log(source?.transactions?.origin, source?.transactions?.destination);
|
|
209
|
+
console.log(accessPolicyTxHash); // present because this example uses Venmo
|
|
210
|
+
```
|
|
211
|
+
|
|
204
212
|
Routes that submit more than one source-chain transaction (approve, then
|
|
205
213
|
route) require a nonce-managed source signer -
|
|
206
214
|
`privateKeyToAccount(pk, { nonceManager })` from viem. Without one the SDK
|
|
@@ -208,12 +216,6 @@ refuses the route preflight with `SOURCE_NONCE_MANAGER_REQUIRED` instead of
|
|
|
208
216
|
letting the route transaction reuse the approval's nonce and revert
|
|
209
217
|
mid-route. Browser wallets are unaffected.
|
|
210
218
|
|
|
211
|
-
`capabilities()` tells you which platforms need a verified identity for a new
|
|
212
|
-
payee registration (`requiresIdentityAttestation` - Wise and PayPal today).
|
|
213
|
-
An already-registered Wise or PayPal handle can be reused with bare payee data.
|
|
214
|
-
A new handle without its signed attestation fails during curator registration
|
|
215
|
-
with `PAYEE_VERIFICATION_REQUIRED`, before funds move on-chain.
|
|
216
|
-
|
|
217
219
|
## Source-route recovery
|
|
218
220
|
|
|
219
221
|
Persist `depositId`, transaction hashes, and the Relay `requestId` as soon as
|
|
@@ -239,6 +241,11 @@ they are available. A source-routed result includes both a flat
|
|
|
239
241
|
- `TRANSACTION_SUBMISSION_UNKNOWN`: a Base-only cashout or another mutation
|
|
240
242
|
returned no hash. Treat it as potentially broadcast. Inspect recent Base
|
|
241
243
|
wallet activity and the supplied recovery action before any retry.
|
|
244
|
+
- `ACCESS_POLICY_CONFIGURATION_FAILED`: the deposit exists, but its required
|
|
245
|
+
Venmo, Cash App, or PayPal policy was not confirmed. Do not cash out again;
|
|
246
|
+
inspect `recovery.transactionHash` when present, then retry
|
|
247
|
+
`prepareAccessPolicy(error.recovery.depositId)` only if the prior policy
|
|
248
|
+
transaction did not succeed.
|
|
242
249
|
|
|
243
250
|
Wallet clients pinned to the wrong chain fail with `SIGNER_CHAIN_MISMATCH`
|
|
244
251
|
before a quote or transaction is submitted. Chainless wallets are checked
|
|
@@ -285,6 +292,31 @@ awaiting-buyer ──────────► matched ───────
|
|
|
285
292
|
|
|
286
293
|
Deep dive: [docs/lifecycle-and-recovery.md](docs/lifecycle-and-recovery.md).
|
|
287
294
|
|
|
295
|
+
## Earn the integration share
|
|
296
|
+
|
|
297
|
+
Use the same six-character referral code shown in your Peer mobile or web app.
|
|
298
|
+
No API key, registration transaction, or separate receiving address is needed:
|
|
299
|
+
the referral code already belongs to your Peer Privy wallet.
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
const cash = createCashClient({
|
|
303
|
+
environment: 'production',
|
|
304
|
+
referralCode: 'ABC123',
|
|
305
|
+
});
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
The SDK normalizes the value and stamps `peer-ref-ABC123` into ERC-8021
|
|
309
|
+
attribution on the deposit transaction. When that liquidity is filled, Curator
|
|
310
|
+
pays the code owner 50 bps, capped by the configured Peer service fee. This is
|
|
311
|
+
the deposit-level integration path: it replaces the maker L1/L2 referral split
|
|
312
|
+
for that deposit instead of enrolling the cashing-out user as your referee.
|
|
313
|
+
|
|
314
|
+
The mapping is permanent. If you later customize your displayed Peer referral
|
|
315
|
+
code, open deposits carrying the old code still pay the same wallet. Include at
|
|
316
|
+
most one `peer-ref-XXXXXX` marker; an unknown or conflicting marker receives no
|
|
317
|
+
integration share. The existing `referrer` option remains available for
|
|
318
|
+
analytics-only ERC-8021 codes such as `acme-app`.
|
|
319
|
+
|
|
288
320
|
## For agents
|
|
289
321
|
|
|
290
322
|
- `cashout`/`withdraw`/`topUp` have unsigned counterparts (`prepare`,
|
|
@@ -292,6 +324,9 @@ Deep dive: [docs/lifecycle-and-recovery.md](docs/lifecycle-and-recovery.md).
|
|
|
292
324
|
before signing, then submit the matching `txs[]` in order.
|
|
293
325
|
- Mutating tool calls return unsigned transactions by default; signing stays
|
|
294
326
|
with the host that owns custody, policy, and user approval.
|
|
327
|
+
- After a prepared restricted cash-out confirms, the host adapter must call
|
|
328
|
+
`finalizePreparedCashout(receipt)` and `prepareAccessPolicy(depositId)`; these
|
|
329
|
+
receipt/signing operations are `CashClient` methods, not built-in tool calls.
|
|
295
330
|
- Every error carries `code`, `retryable`, and a `remediation` sentence.
|
|
296
331
|
- Every order carries `nextActions: ('wait' | 'withdraw')[]` - no heuristics.
|
|
297
332
|
- Every wire type has a zod schema + JSON codec - state crosses process
|
|
@@ -322,12 +357,6 @@ defaults to `https://api-staging.zkp2p.xyz`. Indexer, curator, and Relay
|
|
|
322
357
|
options remain overridable via `createCashClient` options. Base USDC on Base
|
|
323
358
|
is the default source and the only destination asset for cashout orders.
|
|
324
359
|
|
|
325
|
-
## Install
|
|
326
|
-
|
|
327
|
-
```sh
|
|
328
|
-
npm install @zkp2p/cash viem
|
|
329
|
-
```
|
|
330
|
-
|
|
331
360
|
## Examples
|
|
332
361
|
|
|
333
362
|
Runnable first-party examples in [`examples/`](examples):
|
|
@@ -342,6 +371,13 @@ dependency currently ships from private source. Onchain custody is enforced by
|
|
|
342
371
|
the protocol: only the contract holds funds, and only the maker can withdraw
|
|
343
372
|
an unmatched deposit.
|
|
344
373
|
|
|
374
|
+
## Contributing
|
|
375
|
+
|
|
376
|
+
[CLAUDE.md](https://github.com/zkp2p/peer-cash/blob/main/CLAUDE.md) is the
|
|
377
|
+
contributor guide: ground rules, repo layout, the `bun run ci` gate, and the
|
|
378
|
+
release/publish process. `AGENTS.md` is the shipped manual for agents using
|
|
379
|
+
the package, not the contributor entry point.
|
|
380
|
+
|
|
345
381
|
## License
|
|
346
382
|
|
|
347
383
|
MIT
|
|
@@ -194,15 +194,16 @@ var errors = {
|
|
|
194
194
|
code: "PAYEE_VERIFICATION_REQUIRED",
|
|
195
195
|
message: `${platform} requires a verified maker identity attestation to register a payee; a bare handle is not accepted.`,
|
|
196
196
|
retryable: false,
|
|
197
|
-
remediation: `Register this ${platform} payee through
|
|
197
|
+
remediation: `Register this ${platform} payee through Peer web and its TEE browser extension, which produces the signed identity attestation, before cashing out. capabilities() flags such platforms with requiresIdentityAttestation: true.`
|
|
198
198
|
},
|
|
199
199
|
{ cause }
|
|
200
200
|
),
|
|
201
|
+
/** @deprecated Cash-outs no longer require an atomic access-policy flow. */
|
|
201
202
|
atomicAccessPolicyRequired: (platforms) => new CashError({
|
|
202
203
|
code: "ATOMIC_ACCESS_POLICY_REQUIRED",
|
|
203
|
-
message:
|
|
204
|
+
message: `Atomic access-policy enforcement for ${platforms.join(", ")} is deprecated.`,
|
|
204
205
|
retryable: false,
|
|
205
|
-
remediation: `
|
|
206
|
+
remediation: `Upgrade @zkp2p/cash; current cash-out flows do not require atomic access-policy configuration.`
|
|
206
207
|
}),
|
|
207
208
|
sourceRouteUnsupportedInPrepare: () => new CashError({
|
|
208
209
|
code: "SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE",
|
|
@@ -405,20 +406,28 @@ var errors = {
|
|
|
405
406
|
},
|
|
406
407
|
{ cause }
|
|
407
408
|
),
|
|
408
|
-
accessPolicyConfigurationFailed: (depositId, groupIds,
|
|
409
|
+
accessPolicyConfigurationFailed: (depositId, groupIds, context = {}) => new CashError(
|
|
409
410
|
{
|
|
410
411
|
code: "ACCESS_POLICY_CONFIGURATION_FAILED",
|
|
411
|
-
message: `Cash-out deposit ${depositId} was created, but its
|
|
412
|
+
message: `Cash-out deposit ${depositId} was created, but its access policy could not be confirmed.`,
|
|
412
413
|
retryable: false,
|
|
413
|
-
remediation: `Do not
|
|
414
|
+
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.`,
|
|
414
415
|
recovery: {
|
|
415
416
|
kind: "configure-cashout-access-policy",
|
|
416
417
|
depositId,
|
|
417
418
|
groupIds: [...groupIds],
|
|
418
|
-
...transactionHash ? { transactionHash } : {}
|
|
419
|
+
...context.transactionHash ? { transactionHash: context.transactionHash } : {},
|
|
420
|
+
...context.source ? {
|
|
421
|
+
source: {
|
|
422
|
+
amount: context.source.amount.toString(),
|
|
423
|
+
...context.source.requestId ? { requestId: context.source.requestId } : {},
|
|
424
|
+
txHashes: context.source.txHashes,
|
|
425
|
+
...context.source.transactions ? { transactions: context.source.transactions } : {}
|
|
426
|
+
}
|
|
427
|
+
} : {}
|
|
419
428
|
}
|
|
420
429
|
},
|
|
421
|
-
{ cause }
|
|
430
|
+
{ cause: context.cause }
|
|
422
431
|
),
|
|
423
432
|
escrowPaused: () => new CashError({
|
|
424
433
|
code: "ESCROW_PAUSED",
|
|
@@ -306,14 +306,15 @@ interface CashPlatformCapability {
|
|
|
306
306
|
payeeHint: string;
|
|
307
307
|
/**
|
|
308
308
|
* When true, registering a payee for this platform requires a signed maker
|
|
309
|
-
* identity attestation the SDK cannot produce -
|
|
310
|
-
*
|
|
309
|
+
* identity attestation the SDK cannot produce. First-party Peer web obtains
|
|
310
|
+
* it through the Peer TEE browser extension. Existing registrations can be
|
|
311
|
+
* reused with bare payee data; a new bare handle throws
|
|
311
312
|
* `PAYEE_VERIFICATION_REQUIRED`.
|
|
312
313
|
*/
|
|
313
314
|
requiresIdentityAttestation: boolean;
|
|
314
315
|
/**
|
|
315
|
-
*
|
|
316
|
-
*
|
|
316
|
+
* @deprecated Always false. This does not report the sequential restricted-
|
|
317
|
+
* platform policy; prepared hosts must inspect `PrepareResult.accessPolicyRequired`.
|
|
317
318
|
*/
|
|
318
319
|
requiresAtomicAccessPolicy: boolean;
|
|
319
320
|
}
|
|
@@ -549,7 +550,7 @@ interface CashoutInput {
|
|
|
549
550
|
};
|
|
550
551
|
}
|
|
551
552
|
interface SignerOptions {
|
|
552
|
-
/**
|
|
553
|
+
/** Any viem WalletClient with a Base account, including a local or external EOA. */
|
|
553
554
|
signer: WalletClient;
|
|
554
555
|
}
|
|
555
556
|
interface CashoutOptions extends SignerOptions {
|
|
@@ -587,7 +588,7 @@ interface CashoutResult {
|
|
|
587
588
|
onchainDepositId: bigint;
|
|
588
589
|
/** Optimistic snapshot (`awaiting-buyer`); poll `order(depositId)` for live state. */
|
|
589
590
|
order: CashOrder;
|
|
590
|
-
/**
|
|
591
|
+
/** Confirmed access-policy transaction when any payout leg is Venmo, Cash App, or PayPal. */
|
|
591
592
|
accessPolicyTxHash?: Hash;
|
|
592
593
|
/** Present when `cashout()` first routed a source asset through Relay. */
|
|
593
594
|
source?: {
|
|
@@ -615,7 +616,7 @@ interface PrepareResult {
|
|
|
615
616
|
register: {
|
|
616
617
|
hashedOnchainIds: string[];
|
|
617
618
|
};
|
|
618
|
-
/**
|
|
619
|
+
/** Whether the host must submit and confirm the policy after `createDeposit` confirms. */
|
|
619
620
|
accessPolicyRequired: boolean;
|
|
620
621
|
}
|
|
621
622
|
/** Confirmed createDeposit receipt from an externally executed prepare() plan. */
|
|
@@ -679,7 +680,7 @@ interface CashClient {
|
|
|
679
680
|
prepare(input: CashoutInput): Promise<PrepareResult>;
|
|
680
681
|
/** Resolve an externally executed createDeposit receipt into resumable cash-out state. */
|
|
681
682
|
finalizePreparedCashout(receipt: PreparedCashoutReceipt): CashoutResult;
|
|
682
|
-
/**
|
|
683
|
+
/** Prepare the required four-group follow-up for a restricted cash-out. */
|
|
683
684
|
prepareAccessPolicy(depositId: string): PreparedTransaction;
|
|
684
685
|
/** 3 - Observe: resumable from `depositId` alone; no session state anywhere. */
|
|
685
686
|
order(depositId: string): Promise<CashOrder>;
|
|
@@ -306,14 +306,15 @@ interface CashPlatformCapability {
|
|
|
306
306
|
payeeHint: string;
|
|
307
307
|
/**
|
|
308
308
|
* When true, registering a payee for this platform requires a signed maker
|
|
309
|
-
* identity attestation the SDK cannot produce -
|
|
310
|
-
*
|
|
309
|
+
* identity attestation the SDK cannot produce. First-party Peer web obtains
|
|
310
|
+
* it through the Peer TEE browser extension. Existing registrations can be
|
|
311
|
+
* reused with bare payee data; a new bare handle throws
|
|
311
312
|
* `PAYEE_VERIFICATION_REQUIRED`.
|
|
312
313
|
*/
|
|
313
314
|
requiresIdentityAttestation: boolean;
|
|
314
315
|
/**
|
|
315
|
-
*
|
|
316
|
-
*
|
|
316
|
+
* @deprecated Always false. This does not report the sequential restricted-
|
|
317
|
+
* platform policy; prepared hosts must inspect `PrepareResult.accessPolicyRequired`.
|
|
317
318
|
*/
|
|
318
319
|
requiresAtomicAccessPolicy: boolean;
|
|
319
320
|
}
|
|
@@ -549,7 +550,7 @@ interface CashoutInput {
|
|
|
549
550
|
};
|
|
550
551
|
}
|
|
551
552
|
interface SignerOptions {
|
|
552
|
-
/**
|
|
553
|
+
/** Any viem WalletClient with a Base account, including a local or external EOA. */
|
|
553
554
|
signer: WalletClient;
|
|
554
555
|
}
|
|
555
556
|
interface CashoutOptions extends SignerOptions {
|
|
@@ -587,7 +588,7 @@ interface CashoutResult {
|
|
|
587
588
|
onchainDepositId: bigint;
|
|
588
589
|
/** Optimistic snapshot (`awaiting-buyer`); poll `order(depositId)` for live state. */
|
|
589
590
|
order: CashOrder;
|
|
590
|
-
/**
|
|
591
|
+
/** Confirmed access-policy transaction when any payout leg is Venmo, Cash App, or PayPal. */
|
|
591
592
|
accessPolicyTxHash?: Hash;
|
|
592
593
|
/** Present when `cashout()` first routed a source asset through Relay. */
|
|
593
594
|
source?: {
|
|
@@ -615,7 +616,7 @@ interface PrepareResult {
|
|
|
615
616
|
register: {
|
|
616
617
|
hashedOnchainIds: string[];
|
|
617
618
|
};
|
|
618
|
-
/**
|
|
619
|
+
/** Whether the host must submit and confirm the policy after `createDeposit` confirms. */
|
|
619
620
|
accessPolicyRequired: boolean;
|
|
620
621
|
}
|
|
621
622
|
/** Confirmed createDeposit receipt from an externally executed prepare() plan. */
|
|
@@ -679,7 +680,7 @@ interface CashClient {
|
|
|
679
680
|
prepare(input: CashoutInput): Promise<PrepareResult>;
|
|
680
681
|
/** Resolve an externally executed createDeposit receipt into resumable cash-out state. */
|
|
681
682
|
finalizePreparedCashout(receipt: PreparedCashoutReceipt): CashoutResult;
|
|
682
|
-
/**
|
|
683
|
+
/** Prepare the required four-group follow-up for a restricted cash-out. */
|
|
683
684
|
prepareAccessPolicy(depositId: string): PreparedTransaction;
|
|
684
685
|
/** 3 - Observe: resumable from `depositId` alone; no session state anywhere. */
|
|
685
686
|
order(depositId: string): Promise<CashOrder>;
|