@zkp2p/cash 0.1.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,131 @@
1
+ # @zkp2p/cash - agent integration manual
2
+
3
+ You are integrating Peer Cash: an offramp that converts Base USDC to fiat
4
+ (Venmo, Revolut, Wise, Zelle, ...) at the live Chainlink market rate. The user
5
+ whose USDC you manage is the **maker**; a buyer pays them fiat and proves it
6
+ with TEE-TLS; the protocol releases the USDC. Funds are held by the protocol,
7
+ and only the maker can withdraw an unmatched deposit.
8
+
9
+ ## Decision tree: pick your entry point
10
+
11
+ 1. **You control a signer in-process** (viem `WalletClient`, e.g. a local
12
+ key or embedded wallet) → use `cashout()` / `topUp()` / `withdraw()`
13
+ directly.
14
+ 2. **Signing happens elsewhere** (AA bundler, policy engine, custody service,
15
+ human approval step) → use `prepare()` / `prepareTopUp()` /
16
+ `prepareWithdraw()`. Each returns unsigned `txs[]`
17
+ (`{ to, data, value, chainId }`) plus same-index `steps[]` labels; inspect
18
+ the plan, submit the transactions in order, and wait for each receipt.
19
+ 3. **You are a tool-use host** (MCP server, CLI) → import the manifest from
20
+ `@zkp2p/cash/tools` and map the tool names to the verbs above. Mutating
21
+ tools return unsigned transactions; keep signing host-side.
22
+
23
+ Every transaction (including approves) carries ERC-8021 attribution:
24
+ `peer-cash`, then any `referrer` codes from `createCashClient` options, then
25
+ the Base builder code.
26
+
27
+ **Two platform caveats, both surfaced in `capabilities()`:**
28
+
29
+ - **Wise and PayPal** carry `requiresIdentityAttestation: true`. Their curator
30
+ registration needs a signed maker identity attestation this SDK cannot mint
31
+ (it comes from the ZKP2P app/extension). A bare-handle `cashout()` to these
32
+ fails fast with `PAYEE_VERIFICATION_REQUIRED` before any transaction.
33
+ - **Venmo, Revolut, Cash App, Monzo** validate the handle against the live
34
+ platform at registration - the account must exist. The rest (Zelle, Chime,
35
+ etc.) are format-checked only. Match handles to the `payeeHint`.
36
+
37
+ ## The loop
38
+
39
+ ```ts
40
+ import { createCashClient, usdc, isCashError } from '@zkp2p/cash';
41
+
42
+ const cash = createCashClient({ environment: 'production' });
43
+
44
+ // 1. Discover - static and side-effect free, do this once.
45
+ const caps = cash.capabilities();
46
+
47
+ // 2. Estimate - idempotent, cacheable, no side effects.
48
+ const est = await cash.estimate({ amount: usdc(500), currency: 'EUR' });
49
+
50
+ // 3. Execute.
51
+ const { depositId } = await cash.cashout(
52
+ {
53
+ amount: usdc(500),
54
+ receive: { platform: 'revolut', currency: 'EUR', payee: { offchainId: 'revtag' } },
55
+ },
56
+ { signer },
57
+ );
58
+
59
+ // 4. Persist depositId ↔ your user. That row is the entire integration state.
60
+
61
+ // 5. Drive the lifecycle from nextActions - no heuristics.
62
+ const order = await cash.order(depositId);
63
+ if (order.nextActions.includes('withdraw') && shouldUnwind) {
64
+ await cash.withdraw(depositId, { signer });
65
+ } else if (order.nextActions.includes('wait')) {
66
+ // poll again later, or `for await (const o of cash.watch(depositId))`
67
+ }
68
+ ```
69
+
70
+ ## Rules that prevent wrong behavior
71
+
72
+ - **Never promise a rate.** `estimate()` is `kind: 'oracle-estimate'`; the
73
+ binding rate resolves at the oracle when a buyer fills. Do not display or
74
+ log it as a locked price.
75
+ - **Never invent an ETA.** Use `order.explain()` - one honest sentence from
76
+ live data. Buyer arrival time is not knowable.
77
+ - **`ORDER_NOT_FOUND` seconds after `cashout()` is indexer lag**, not a lost
78
+ deposit. The tx receipt you hold is the truth. Retry; `watch()` absorbs
79
+ this automatically.
80
+ - **Unwind with `withdraw()` only.** It is state-aware (prunes expired
81
+ intents first). Do not call escrow functions directly.
82
+ - **Read fills as receipts.** `fiatOwed` is the buyer's obligation at the
83
+ locked rate; after the proof, `fiatPaid`/`paymentId`/`releasedAmount` are
84
+ the verified outcome. Reconcile against those, not your own math.
85
+ - **Check the buyer during `matched`.** `buyer(address)` (tool: `cash_buyer`)
86
+ returns their fulfilled/pruned history and success rate - surface it
87
+ instead of a raw address.
88
+ - **Serialize with the codecs.** `orderToJson`/`orderFromJson` etc. round-trip
89
+ bigints losslessly and re-attach `explain()`. Plain `JSON.stringify` on a
90
+ live object throws on bigints.
91
+
92
+ ## Error → remediation table
93
+
94
+ Every `CashError` carries `code`, `retryable`, `remediation`. Behavior:
95
+
96
+ | Code | Retryable | Agent action |
97
+ | --------------------------------- | --------- | ----------------------------------------------------------------------------------------------- |
98
+ | `ORACLE_UNSUPPORTED_CURRENCY` | no | Re-pick currency from `capabilities()` |
99
+ | `UNSUPPORTED_PLATFORM` | no | Re-pick platform from `capabilities()` |
100
+ | `AMOUNT_BELOW_MINIMUM` | no | Raise amount (hard floor $0.01, recommended ≥ 1 USDC) |
101
+ | `PAYEE_VERIFICATION_REQUIRED` | no | Wise/PayPal need a signed identity attestation - register the payee via the ZKP2P app first |
102
+ | `PAYEE_REGISTRATION_FAILED` | yes | Validate handle against `payeeHint`, retry with backoff (curator caps at 20 registrations/min) |
103
+ | `ALLOWANCE_NOT_VISIBLE` | yes | Approve mined but a stale RPC replica hid it; retry the same call in a few seconds |
104
+ | `TRANSACTION_FAILED` | no | The on-chain call reverted or was mapped from a raw error; surface to operator; funds unchanged |
105
+ | `DEPOSIT_RESOLUTION_FAILED` | no | Extract depositId from the `DepositReceived` log in the receipt |
106
+ | `ORDER_NOT_FOUND` | yes | Retry (indexer lag) unless the id is provably wrong |
107
+ | `INDEXER_LAG` | yes | Retry after a few seconds |
108
+ | `ACTIVE_INTENT_BLOCKS_WITHDRAWAL` | yes | Wait; retry full `withdraw()` after intent expiry (or withdraw the unlocked part with `amount`) |
109
+ | `INSUFFICIENT_AVAILABLE_FUNDS` | yes | Partial amount exceeds the unlocked balance; lower it or close fully later |
110
+ | `NOTHING_TO_WITHDRAW` | no | Order is terminal; reconcile your records |
111
+ | `ORDER_NOT_ACTIVE` | no | Top-up target is closed; start a new `cashout()` instead |
112
+ | `SIGNER_REQUIRED` | no | Provide `{ signer }` or switch to the prepare path |
113
+ | `WATCH_TIMEOUT` | yes | Resume `watch(depositId)` whenever convenient |
114
+ | `ESCROW_PAUSED` | yes | Back off; existing funds remain withdrawable |
115
+
116
+ `isCashError(err)` narrows unknown errors; `err.toJSON()` is safe for logs
117
+ and tool results.
118
+
119
+ ## Verification checklist (staging, maker-side only)
120
+
121
+ Prove your integration against `environment: 'staging'` with a funded test
122
+ wallet. Never wait on a buyer - buyer-side is out of your scope:
123
+
124
+ 1. `cashout()` a small amount (1–2 USDC) → capture `depositId`.
125
+ 2. `order(depositId)` shows `awaiting-buyer` (retry through indexer lag).
126
+ 3. `orders(owner)` includes the deposit.
127
+ 4. `withdraw(depositId)` → transaction succeeds.
128
+ 5. `order(depositId)` shows `returned`; wallet balance is restored minus gas.
129
+
130
+ If step 4 ever fails with funds stuck, stop and escalate - do not retry
131
+ blindly.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ZKP2P
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # @zkp2p/cash
2
+
3
+ Cash out Base USDC to fiat on Venmo, Revolut, Wise, Zelle, and more at the
4
+ live Chainlink market rate, with zero spread and no centralized off-ramp
5
+ provider.
6
+
7
+ Peer Cash is an **offramp-only** SDK for the [ZKP2P](https://peer.xyz)
8
+ protocol. The cashing-out user is the maker: their USDC becomes a
9
+ protocol-held deposit, ZKP2P handles the buyer side, and the SDK gives the
10
+ integrator a small set of typed verbs plus readable order state. No hosted
11
+ widget, no provider custody, no quote engine to maintain.
12
+
13
+ ```ts
14
+ import { createCashClient, usdc } from '@zkp2p/cash';
15
+
16
+ const cash = createCashClient({ environment: 'production' });
17
+
18
+ const est = await cash.estimate({ amount: usdc(1000), currency: 'USD' });
19
+ // { rate: 1, receiveAmount: 1000, kind: 'oracle-estimate' } - "≈", never a locked quote
20
+
21
+ const { depositId } = await cash.cashout(
22
+ {
23
+ amount: usdc(1000),
24
+ receive: { platform: 'venmo', currency: 'USD', payee: { offchainId: '@you' } },
25
+ },
26
+ { signer }, // any viem WalletClient on Base
27
+ );
28
+
29
+ for await (const order of cash.watch(depositId)) {
30
+ console.log(order.state, order.explain());
31
+ if (order.state === 'delivered') break;
32
+ }
33
+ ```
34
+
35
+ ## The eight verbs
36
+
37
+ | Verb | What it does |
38
+ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
39
+ | `capabilities()` | Sync discovery: platforms × currencies × payee format hints × amount bounds |
40
+ | `estimate({ amount, currency })` | Live oracle rate - no payee, no side effects, idempotent |
41
+ | `cashout(input, { signer })` | Registers your payee, creates the protocol-held order, returns the `depositId` |
42
+ | `prepare(input)` | Same as cashout but returns unsigned `txs[]` + readable `steps[]` - agent wallets, AA, server keys |
43
+ | `order(depositId)` / `orders(owner)` | Resume any order from its id alone; list all orders for a wallet |
44
+ | `watch(depositId)` | Async iterator: yields on every state change until terminal, abort, or timeout |
45
+ | `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) |
46
+ | `topUp(depositId, amount, { signer })` | Add USDC to a live order - same payee, same market rate |
47
+ | `buyer(address)` | A buyer's track record from their intent history - who just matched your order? |
48
+
49
+ Every mutating verb has an unsigned counterpart (`prepare`, `prepareWithdraw`,
50
+ `prepareTopUp`). The unsigned path returns raw `txs[]` plus a same-index
51
+ `steps[]` plan such as `approve`, `createDeposit`, or `withdrawDeposit`, so
52
+ wallets, AA systems, and agents can show what each transaction does before
53
+ signing. Every transaction - including approves - carries ERC-8021
54
+ attribution: `peer-cash` first, your own `referrer` code(s) after it, so
55
+ onchain analytics can segment cash flow end to end.
56
+
57
+ `capabilities()` tells you which platforms need a verified identity to
58
+ register a payee (`requiresIdentityAttestation` - Wise and PayPal today); a
59
+ bare-handle `cashout()` to those fails fast with `PAYEE_VERIFICATION_REQUIRED`
60
+ rather than reverting on-chain.
61
+
62
+ ## Lifecycle
63
+
64
+ ```
65
+ buyer signals fiat proven
66
+ awaiting-buyer ──────────► matched ──────────► delivered
67
+ │ │ (partial fills pass through "delivering")
68
+ │ withdraw() │ buyer never pays → intent expires
69
+ ▼ ▼ withdraw() prunes + returns funds
70
+ returned ◄─────────────────┘
71
+ ```
72
+
73
+ - **You are the maker.** Your deposit is priced by the live Chainlink oracle
74
+ with `spreadBps: 0`, making it the best offer on the book by construction.
75
+ - **There is no quote.** The binding rate resolves at the oracle when a buyer
76
+ fills. `estimate()` says "approximately"; nothing in this API pretends to
77
+ lock a price.
78
+ - **Everything is resumable.** An order is reconstructed from the chain by
79
+ `depositId` alone. Close the tab, switch devices, crash the process - then
80
+ call `order(depositId)`.
81
+ - **Unwind is one verb.** Buyer never paid? Their intent expires; `withdraw()`
82
+ prunes it and returns your USDC. You never choose between cancel and recover.
83
+
84
+ Deep dive: [docs/lifecycle-and-recovery.md](docs/lifecycle-and-recovery.md).
85
+
86
+ ## For agents
87
+
88
+ - `cashout`/`withdraw`/`topUp` have unsigned counterparts (`prepare`,
89
+ `prepareWithdraw`, `prepareTopUp`) - inspect readable `steps[]` and calldata
90
+ before signing, then submit the matching `txs[]` in order.
91
+ - Mutating tool calls return unsigned transactions by default; signing stays
92
+ with the host that owns custody, policy, and user approval.
93
+ - Every error carries `code`, `retryable`, and a `remediation` sentence.
94
+ - Every order carries `nextActions: ('wait' | 'withdraw')[]` - no heuristics.
95
+ - Every wire type has a zod schema + JSON codec - state crosses process
96
+ boundaries losslessly.
97
+ - Everything arrives decoded: platform ids and currency codes instead of
98
+ bytes32 hashes, plain-number rates instead of 1e18 bigints.
99
+ - Fills are receipts: the locked rate and fiat owed at signal, then the
100
+ verified fiat paid, currency, platform payment id, released USDC, and
101
+ fill latency once the proof lands.
102
+ - `@zkp2p/cash/tools` exports a JSON-schema tool manifest of the verbs.
103
+
104
+ Start at [AGENTS.md](AGENTS.md), or load the
105
+ [`peer-cash-integration` skill](skills/peer-cash-integration/SKILL.md).
106
+
107
+ ## React
108
+
109
+ ```ts
110
+ import { useEstimate, useCashout, useOrder, useOrders } from '@zkp2p/cash/react';
111
+ ```
112
+
113
+ React is an optional peer dependency - the root entry never imports it.
114
+
115
+ ## Environments
116
+
117
+ `production` | `preproduction` | `staging` - selects contracts, curator, and
118
+ indexer. Indexer and curator URLs are overridable via `createCashClient`
119
+ options. v1 is same-chain only: Base USDC in.
120
+
121
+ ## Install
122
+
123
+ ```sh
124
+ npm install @zkp2p/cash@dev viem
125
+ ```
126
+
127
+ ## Trust model, honestly
128
+
129
+ This SDK is open source, so the code that constructs the parameters moving
130
+ your USDC into protocol-held funds is auditable. It depends on the published
131
+ `@zkp2p/sdk` for protocol internals, which currently ships from private source.
132
+ The Peer Cash facade is verifiable here; the dependency is not yet fully open.
133
+ Onchain custody is still enforced by the protocol: only the contract holds
134
+ funds, and only you can withdraw an unmatched deposit.
135
+
136
+ ## License
137
+
138
+ MIT
@@ -0,0 +1,176 @@
1
+ // src/engine/constants.ts
2
+ var BASE_CHAIN_ID = 8453;
3
+ var BASE_USDC_ADDRESS = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913";
4
+ var USDC_DECIMALS = 6;
5
+ var MARKET_SPREAD_BPS = 0;
6
+ var ORACLE_MIN_CONVERSION_RATE_SENTINEL = 1n;
7
+ var CASH_ORDER_STATUSES = [
8
+ "SIGNALED",
9
+ "FULFILLED",
10
+ "PRUNED",
11
+ "MANUALLY_RELEASED"
12
+ ];
13
+ var CASH_ORDER_POLL_INTERVAL_MS = 5e3;
14
+ var CASH_RETAIN_ON_EMPTY = false;
15
+
16
+ // src/client/errors.ts
17
+ var CashError = class extends Error {
18
+ code;
19
+ retryable;
20
+ remediation;
21
+ constructor(shape, options) {
22
+ super(shape.message, options);
23
+ this.name = "CashError";
24
+ this.code = shape.code;
25
+ this.retryable = shape.retryable;
26
+ this.remediation = shape.remediation;
27
+ }
28
+ /** Serializable view (for tool results and logs). */
29
+ toJSON() {
30
+ return {
31
+ code: this.code,
32
+ message: this.message,
33
+ retryable: this.retryable,
34
+ remediation: this.remediation
35
+ };
36
+ }
37
+ };
38
+ function isCashError(value) {
39
+ return value instanceof CashError;
40
+ }
41
+ var errors = {
42
+ oracleUnsupportedCurrency: (currency) => new CashError({
43
+ code: "ORACLE_UNSUPPORTED_CURRENCY",
44
+ message: `${currency} has no live Chainlink oracle feed; Peer Cash is market-rate only.`,
45
+ retryable: false,
46
+ remediation: `Pick a currency listed in capabilities() - each one is priced by a live oracle feed.`
47
+ }),
48
+ unsupportedPlatform: (platform) => new CashError({
49
+ code: "UNSUPPORTED_PLATFORM",
50
+ message: `'${platform}' is not a supported payout platform in this environment.`,
51
+ retryable: false,
52
+ remediation: `Pick a platform listed in capabilities().`
53
+ }),
54
+ amountBelowMinimum: (amount, min) => new CashError({
55
+ code: "AMOUNT_BELOW_MINIMUM",
56
+ message: `Amount ${amount} is below the minimum cash-out of ${min} USDC base units.`,
57
+ retryable: false,
58
+ remediation: `Increase the amount to at least ${min} base units (${Number(min) / 1e6} USDC).`
59
+ }),
60
+ activeIntentBlocksWithdrawal: (depositId) => new CashError({
61
+ code: "ACTIVE_INTENT_BLOCKS_WITHDRAWAL",
62
+ message: `Order ${depositId} has a live buyer intent; escrow blocks withdrawal while a buyer may still deliver.`,
63
+ retryable: true,
64
+ remediation: `Wait for the buyer to complete or for their intent to expire, then call withdraw() again - it prunes expired intents automatically.`
65
+ }),
66
+ insufficientAvailableFunds: (depositId, requested, available) => new CashError({
67
+ code: "INSUFFICIENT_AVAILABLE_FUNDS",
68
+ message: `Order ${depositId} has ${available} base units available; ${requested} requested.`,
69
+ retryable: true,
70
+ remediation: `Withdraw at most the available (unlocked) amount, or omit the amount to close the order fully once no buyer intent is live.`
71
+ }),
72
+ orderNotActive: (depositId) => new CashError({
73
+ code: "ORDER_NOT_ACTIVE",
74
+ message: `Order ${depositId} is closed (delivered or returned); it cannot be topped up.`,
75
+ retryable: false,
76
+ remediation: `Start a new cash-out with cashout() instead.`
77
+ }),
78
+ nothingToWithdraw: (depositId) => new CashError({
79
+ code: "NOTHING_TO_WITHDRAW",
80
+ message: `Order ${depositId} holds no withdrawable funds (already delivered or returned).`,
81
+ retryable: false,
82
+ remediation: `Check order(depositId).state - this order is terminal.`
83
+ }),
84
+ indexerLag: (depositId) => new CashError({
85
+ code: "INDEXER_LAG",
86
+ message: `Order ${depositId} is not indexed yet (the deposit may be seconds old).`,
87
+ retryable: true,
88
+ remediation: `Retry in a few seconds; on-chain state is ahead of the indexer right after a transaction.`
89
+ }),
90
+ orderNotFound: (depositId) => new CashError({
91
+ code: "ORDER_NOT_FOUND",
92
+ message: `No deposit found for id ${depositId}.`,
93
+ retryable: true,
94
+ remediation: `Verify the composite depositId (escrow_onchainId). If the deposit was created seconds ago this is indexer lag - retry shortly.`
95
+ }),
96
+ payeeRegistrationFailed: (cause) => new CashError(
97
+ {
98
+ code: "PAYEE_REGISTRATION_FAILED",
99
+ message: `Registering payee details with the curator failed.`,
100
+ retryable: true,
101
+ remediation: `Check the payee handle format for the platform (see capabilities() hints) and retry.`
102
+ },
103
+ { cause }
104
+ ),
105
+ payeeVerificationRequired: (platform, cause) => new CashError(
106
+ {
107
+ code: "PAYEE_VERIFICATION_REQUIRED",
108
+ message: `${platform} requires a verified maker identity attestation to register a payee; a bare handle is not accepted.`,
109
+ retryable: false,
110
+ remediation: `Register this ${platform} payee through the ZKP2P app / extension (which produces the signed identity attestation) before cashing out. capabilities() flags such platforms with requiresIdentityAttestation: true.`
111
+ },
112
+ { cause }
113
+ ),
114
+ allowanceNotVisible: (amount) => new CashError({
115
+ code: "ALLOWANCE_NOT_VISIBLE",
116
+ message: `USDC approval for ${amount} base units did not become visible on the read path in time.`,
117
+ retryable: true,
118
+ remediation: `The approve transaction mined but a load-balanced RPC is serving stale state. Retry the same call in a few seconds.`
119
+ }),
120
+ depositResolutionFailed: (txHash) => new CashError({
121
+ code: "DEPOSIT_RESOLUTION_FAILED",
122
+ message: `Deposit transaction ${txHash} succeeded but no DepositReceived event was found in the receipt.`,
123
+ retryable: false,
124
+ remediation: `Inspect the transaction on Basescan; recover the depositId from the DepositReceived log manually, then resume with order(depositId).`
125
+ }),
126
+ signerRequired: (verb) => new CashError({
127
+ code: "SIGNER_REQUIRED",
128
+ message: `${verb}() mutates on-chain state and needs a signer.`,
129
+ retryable: false,
130
+ remediation: `Pass { signer } (a viem WalletClient with an account), or use prepare() and submit the returned txs with your own signing infrastructure.`
131
+ }),
132
+ watchTimeout: (depositId, timeoutMs) => new CashError({
133
+ code: "WATCH_TIMEOUT",
134
+ message: `watch(${depositId}) exceeded ${timeoutMs}ms without reaching a terminal state.`,
135
+ retryable: true,
136
+ remediation: `The order is still live - resume any time with watch(depositId) or order(depositId).`
137
+ }),
138
+ transactionFailed: (txHash, cause) => new CashError(
139
+ {
140
+ code: "TRANSACTION_FAILED",
141
+ message: `Transaction ${txHash} reverted.`,
142
+ retryable: false,
143
+ remediation: `Inspect the transaction on Basescan; the deposit state is unchanged if the revert happened before escrow accepted funds.`
144
+ },
145
+ { cause }
146
+ ),
147
+ escrowPaused: () => new CashError({
148
+ code: "ESCROW_PAUSED",
149
+ message: `The escrow contract is paused; deposits are temporarily disabled.`,
150
+ retryable: true,
151
+ remediation: `Wait for the protocol to unpause and retry. Existing funds remain withdrawable.`
152
+ }),
153
+ /** Generic fallback for an on-chain call that failed for an unrecognized reason. */
154
+ chainCallFailed: (verb, cause) => new CashError(
155
+ {
156
+ code: "TRANSACTION_FAILED",
157
+ message: `The on-chain ${verb} call failed.`,
158
+ retryable: false,
159
+ remediation: `Inspect the error cause and the wallet on Basescan. Deposit state is unchanged if the call reverted before escrow accepted funds.`
160
+ },
161
+ { cause }
162
+ )
163
+ };
164
+ function mapChainError(verb, err) {
165
+ if (isCashError(err)) return err;
166
+ const message = err instanceof Error ? err.message : String(err);
167
+ if (/\bpaused\b/i.test(message)) return errors.escrowPaused();
168
+ if (/exceeds allowance|insufficient allowance|transfer amount exceeds/i.test(message)) {
169
+ return errors.allowanceNotVisible(0n);
170
+ }
171
+ return errors.chainCallFailed(verb, err);
172
+ }
173
+
174
+ export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RETAIN_ON_EMPTY, CashError, MARKET_SPREAD_BPS, ORACLE_MIN_CONVERSION_RATE_SENTINEL, USDC_DECIMALS, errors, isCashError, mapChainError };
175
+ //# sourceMappingURL=chunk-4DRZRWWS.js.map
176
+ //# sourceMappingURL=chunk-4DRZRWWS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/engine/constants.ts","../src/client/errors.ts"],"names":[],"mappings":"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","file":"chunk-4DRZRWWS.js","sourcesContent":["/**\n * Peer Cash - engine constants.\n *\n * Peer Cash is an async crypto→fiat offramp built on the maker/deposit side of\n * the protocol: the cashing-out user IS the maker. They create a deposit at the\n * live oracle/market rate (0% spread); a buyer (a standard taker) signals an\n * intent, pays fiat, and proves it via the standard TEE-TLS flow, releasing the\n * user's crypto. The protocol is reused in its existing direction - no proof\n * inversion, no sell-side quote.\n */\nimport type { IntentStatus } from '../sdk-types';\n\n/** Base chain id - Peer Cash settles in Base USDC. */\nexport const BASE_CHAIN_ID = 8453;\n\n/** Canonical USDC on Base (6 decimals). The deposit asset for every cash-out. */\nexport const BASE_USDC_ADDRESS = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913' as const;\n\n/** USDC has 6 decimals. */\nexport const USDC_DECIMALS = 6;\n\n/**\n * Market rate = the live Chainlink oracle with **zero spread**. The user sets no\n * rate; selling at market is the fast-fill incentive (the deposit is the best\n * deal on the book, so buyers have reason to take it quickly).\n */\nexport const MARKET_SPREAD_BPS = 0;\n\n/**\n * EscrowV2 rejects a zero `minConversionRate` even when an oracle-backed rate\n * config is attached. Use the smallest non-zero sentinel so the oracle rate\n * still fully determines pricing while satisfying the on-chain invariant.\n */\nexport const ORACLE_MIN_CONVERSION_RATE_SENTINEL = 1n;\n\n/**\n * The full intent-status set a cash-out order can pass through. The indexer's\n * `getIntentsForDeposits` defaults to `['SIGNALED']` only - passing this\n * explicit set is REQUIRED, otherwise `delivered`/`returned` states are\n * silently filtered out.\n */\nexport const CASH_ORDER_STATUSES: IntentStatus[] = [\n 'SIGNALED',\n 'FULFILLED',\n 'PRUNED',\n 'MANUALLY_RELEASED',\n];\n\n/** Default polling cadence for an in-flight order (ms). Matches the protocol's active-intent polling. */\nexport const CASH_ORDER_POLL_INTERVAL_MS = 5_000;\n\n/**\n * Default deposit config for every Peer Cash deposit: a one-shot cash-out\n * cleans up when fully filled rather than lingering empty.\n */\nexport const CASH_RETAIN_ON_EMPTY = false;\n","/**\n * Typed errors - every failure carries a `code`, whether it is `retryable`,\n * and a `remediation` sentence so agents can self-drive recovery.\n */\nexport type CashErrorCode =\n | 'ORACLE_UNSUPPORTED_CURRENCY'\n | 'UNSUPPORTED_PLATFORM'\n | 'AMOUNT_BELOW_MINIMUM'\n | 'ACTIVE_INTENT_BLOCKS_WITHDRAWAL'\n | 'NOTHING_TO_WITHDRAW'\n | 'INSUFFICIENT_AVAILABLE_FUNDS'\n | 'ORDER_NOT_ACTIVE'\n | 'ESCROW_PAUSED'\n | 'INDEXER_LAG'\n | 'ORDER_NOT_FOUND'\n | 'PAYEE_REGISTRATION_FAILED'\n | 'PAYEE_VERIFICATION_REQUIRED'\n | 'DEPOSIT_RESOLUTION_FAILED'\n | 'ALLOWANCE_NOT_VISIBLE'\n | 'SIGNER_REQUIRED'\n | 'WATCH_TIMEOUT'\n | 'TRANSACTION_FAILED';\n\nexport interface CashErrorShape {\n code: CashErrorCode;\n message: string;\n retryable: boolean;\n remediation: string;\n}\n\nexport class CashError extends Error implements CashErrorShape {\n readonly code: CashErrorCode;\n readonly retryable: boolean;\n readonly remediation: string;\n\n constructor(shape: CashErrorShape, options?: { cause?: unknown }) {\n super(shape.message, options);\n this.name = 'CashError';\n this.code = shape.code;\n this.retryable = shape.retryable;\n this.remediation = shape.remediation;\n }\n\n /** Serializable view (for tool results and logs). */\n toJSON(): CashErrorShape {\n return {\n code: this.code,\n message: this.message,\n retryable: this.retryable,\n remediation: this.remediation,\n };\n }\n}\n\nexport function isCashError(value: unknown): value is CashError {\n return value instanceof CashError;\n}\n\n/** Factory helpers keep call sites one-liners and remediation copy consistent. */\nexport const errors = {\n oracleUnsupportedCurrency: (currency: string) =>\n new CashError({\n code: 'ORACLE_UNSUPPORTED_CURRENCY',\n message: `${currency} has no live Chainlink oracle feed; Peer Cash is market-rate only.`,\n retryable: false,\n remediation: `Pick a currency listed in capabilities() - each one is priced by a live oracle feed.`,\n }),\n unsupportedPlatform: (platform: string) =>\n new CashError({\n code: 'UNSUPPORTED_PLATFORM',\n message: `'${platform}' is not a supported payout platform in this environment.`,\n retryable: false,\n remediation: `Pick a platform listed in capabilities().`,\n }),\n amountBelowMinimum: (amount: bigint, min: bigint) =>\n new CashError({\n code: 'AMOUNT_BELOW_MINIMUM',\n message: `Amount ${amount} is below the minimum cash-out of ${min} USDC base units.`,\n retryable: false,\n remediation: `Increase the amount to at least ${min} base units (${Number(min) / 1e6} USDC).`,\n }),\n activeIntentBlocksWithdrawal: (depositId: string) =>\n new CashError({\n code: 'ACTIVE_INTENT_BLOCKS_WITHDRAWAL',\n message: `Order ${depositId} has a live buyer intent; escrow blocks withdrawal while a buyer may still deliver.`,\n retryable: true,\n remediation: `Wait for the buyer to complete or for their intent to expire, then call withdraw() again - it prunes expired intents automatically.`,\n }),\n insufficientAvailableFunds: (depositId: string, requested: bigint, available: bigint) =>\n new CashError({\n code: 'INSUFFICIENT_AVAILABLE_FUNDS',\n message: `Order ${depositId} has ${available} base units available; ${requested} requested.`,\n retryable: true,\n remediation: `Withdraw at most the available (unlocked) amount, or omit the amount to close the order fully once no buyer intent is live.`,\n }),\n orderNotActive: (depositId: string) =>\n new CashError({\n code: 'ORDER_NOT_ACTIVE',\n message: `Order ${depositId} is closed (delivered or returned); it cannot be topped up.`,\n retryable: false,\n remediation: `Start a new cash-out with cashout() instead.`,\n }),\n nothingToWithdraw: (depositId: string) =>\n new CashError({\n code: 'NOTHING_TO_WITHDRAW',\n message: `Order ${depositId} holds no withdrawable funds (already delivered or returned).`,\n retryable: false,\n remediation: `Check order(depositId).state - this order is terminal.`,\n }),\n indexerLag: (depositId: string) =>\n new CashError({\n code: 'INDEXER_LAG',\n message: `Order ${depositId} is not indexed yet (the deposit may be seconds old).`,\n retryable: true,\n remediation: `Retry in a few seconds; on-chain state is ahead of the indexer right after a transaction.`,\n }),\n orderNotFound: (depositId: string) =>\n new CashError({\n code: 'ORDER_NOT_FOUND',\n message: `No deposit found for id ${depositId}.`,\n retryable: true,\n remediation: `Verify the composite depositId (escrow_onchainId). If the deposit was created seconds ago this is indexer lag - retry shortly.`,\n }),\n payeeRegistrationFailed: (cause: unknown) =>\n new CashError(\n {\n code: 'PAYEE_REGISTRATION_FAILED',\n message: `Registering payee details with the curator failed.`,\n retryable: true,\n remediation: `Check the payee handle format for the platform (see capabilities() hints) and retry.`,\n },\n { cause },\n ),\n payeeVerificationRequired: (platform: string, cause?: unknown) =>\n new CashError(\n {\n code: 'PAYEE_VERIFICATION_REQUIRED',\n message: `${platform} requires a verified maker identity attestation to register a payee; a bare handle is not accepted.`,\n retryable: false,\n remediation: `Register this ${platform} payee through the ZKP2P app / extension (which produces the signed identity attestation) before cashing out. capabilities() flags such platforms with requiresIdentityAttestation: true.`,\n },\n { cause },\n ),\n allowanceNotVisible: (amount: bigint) =>\n new CashError({\n code: 'ALLOWANCE_NOT_VISIBLE',\n message: `USDC approval for ${amount} base units did not become visible on the read path in time.`,\n retryable: true,\n remediation: `The approve transaction mined but a load-balanced RPC is serving stale state. Retry the same call in a few seconds.`,\n }),\n depositResolutionFailed: (txHash: string) =>\n new CashError({\n code: 'DEPOSIT_RESOLUTION_FAILED',\n message: `Deposit transaction ${txHash} succeeded but no DepositReceived event was found in the receipt.`,\n retryable: false,\n remediation: `Inspect the transaction on Basescan; recover the depositId from the DepositReceived log manually, then resume with order(depositId).`,\n }),\n signerRequired: (verb: string) =>\n new CashError({\n code: 'SIGNER_REQUIRED',\n message: `${verb}() mutates on-chain state and needs a signer.`,\n retryable: false,\n remediation: `Pass { signer } (a viem WalletClient with an account), or use prepare() and submit the returned txs with your own signing infrastructure.`,\n }),\n watchTimeout: (depositId: string, timeoutMs: number) =>\n new CashError({\n code: 'WATCH_TIMEOUT',\n message: `watch(${depositId}) exceeded ${timeoutMs}ms without reaching a terminal state.`,\n retryable: true,\n remediation: `The order is still live - resume any time with watch(depositId) or order(depositId).`,\n }),\n transactionFailed: (txHash: string, cause?: unknown) =>\n new CashError(\n {\n code: 'TRANSACTION_FAILED',\n message: `Transaction ${txHash} reverted.`,\n retryable: false,\n remediation: `Inspect the transaction on Basescan; the deposit state is unchanged if the revert happened before escrow accepted funds.`,\n },\n { cause },\n ),\n escrowPaused: () =>\n new CashError({\n code: 'ESCROW_PAUSED',\n message: `The escrow contract is paused; deposits are temporarily disabled.`,\n retryable: true,\n remediation: `Wait for the protocol to unpause and retry. Existing funds remain withdrawable.`,\n }),\n /** Generic fallback for an on-chain call that failed for an unrecognized reason. */\n chainCallFailed: (verb: string, cause?: unknown) =>\n new CashError(\n {\n code: 'TRANSACTION_FAILED',\n message: `The on-chain ${verb} call failed.`,\n retryable: false,\n remediation: `Inspect the error cause and the wallet on Basescan. Deposit state is unchanged if the call reverted before escrow accepted funds.`,\n },\n { cause },\n ),\n};\n\n/**\n * Map a raw SDK/RPC/viem error from a mutating on-chain call to a typed\n * `CashError`, so the package's error contract holds even when the underlying\n * call reverts. Recognized reverts get specific codes; everything else falls\n * back to a wrapped `TRANSACTION_FAILED` (never a raw error to the consumer).\n */\nexport function mapChainError(verb: string, err: unknown): CashError {\n if (isCashError(err)) return err;\n const message = err instanceof Error ? err.message : String(err);\n if (/\\bpaused\\b/i.test(message)) return errors.escrowPaused();\n if (/exceeds allowance|insufficient allowance|transfer amount exceeds/i.test(message)) {\n return errors.allowanceNotVisible(0n);\n }\n return errors.chainCallFailed(verb, err);\n}\n"]}