@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/dist/index.js ADDED
@@ -0,0 +1,1322 @@
1
+ import { MARKET_SPREAD_BPS, ORACLE_MIN_CONVERSION_RATE_SENTINEL, CASH_RETAIN_ON_EMPTY, BASE_USDC_ADDRESS, USDC_DECIMALS, BASE_CHAIN_ID, errors, isCashError, CASH_ORDER_STATUSES, mapChainError } from './chunk-4DRZRWWS.js';
2
+ 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 } from './chunk-4DRZRWWS.js';
3
+ import { parseAbi, parseEventLogs, http, createWalletClient, encodeFunctionData } from 'viem';
4
+ import { base } from 'viem/chains';
5
+ import { getSpreadOracleConfig, currencyInfo, getPaymentMethodsCatalog, getGatingServiceAddress, resolvePaymentMethodHashFromCatalog, resolvePaymentMethodNameFromHash, getCurrencyCodeFromHash, createCompositeDepositId, appendAttributionToCalldata, Zkp2pClient, CHAINLINK_ORACLE_FEEDS } from '@zkp2p/sdk';
6
+ import { z } from 'zod';
7
+
8
+ function isMarketRateSupported(currency, adapters) {
9
+ return getSpreadOracleConfig(currency, adapters) != null;
10
+ }
11
+ function buildMarketRateCurrencyOverride(currency, adapters) {
12
+ const code = currencyInfo[currency]?.currencyCodeHash;
13
+ const oracle = getSpreadOracleConfig(currency, adapters);
14
+ if (!code || !oracle) return null;
15
+ return {
16
+ code,
17
+ minConversionRate: ORACLE_MIN_CONVERSION_RATE_SENTINEL,
18
+ oracleRateConfig: {
19
+ adapter: oracle.adapter,
20
+ adapterConfig: oracle.adapterConfig,
21
+ spreadBps: MARKET_SPREAD_BPS,
22
+ maxStaleness: oracle.maxStaleness
23
+ }
24
+ };
25
+ }
26
+ var DEFAULT_MIN_ORDER_FLOOR = 1000000n;
27
+ function buildIntentAmountRange(amount) {
28
+ if (amount <= 0n) throw new Error("Cash-out amount must be positive");
29
+ const min = amount < DEFAULT_MIN_ORDER_FLOOR ? amount : DEFAULT_MIN_ORDER_FLOOR;
30
+ return { min, max: amount };
31
+ }
32
+ async function prepareCashDepositParams(client, input, adapters) {
33
+ const { payouts } = input;
34
+ if (!payouts.length) throw new Error("At least one payout is required");
35
+ const chainId = client.chainId;
36
+ const runtimeEnv = client.runtimeEnv;
37
+ const catalog = getPaymentMethodsCatalog(chainId, runtimeEnv);
38
+ const intentGatingService = getGatingServiceAddress(chainId, runtimeEnv);
39
+ for (const payout of payouts) {
40
+ if (!isMarketRateSupported(payout.currency, adapters)) {
41
+ throw new Error(
42
+ `${payout.currency} has no live market-rate oracle feed; Peer Cash supports market-rate currencies only.`
43
+ );
44
+ }
45
+ }
46
+ const processorNames = payouts.map((p) => p.processorName);
47
+ const { hashedOnchainIds } = await client.registerPayeeDetails({
48
+ processorNames,
49
+ payeeData: payouts.map((p) => p.payeeData)
50
+ });
51
+ if (hashedOnchainIds.length !== payouts.length) {
52
+ throw new Error("Payee registration returned an unexpected number of hashes");
53
+ }
54
+ const paymentMethodsOverride = processorNames.map(
55
+ (name) => resolvePaymentMethodHashFromCatalog(name, catalog)
56
+ );
57
+ const paymentMethodDataOverride = hashedOnchainIds.map((hid) => ({
58
+ intentGatingService,
59
+ payeeDetails: hid,
60
+ data: "0x"
61
+ }));
62
+ const currenciesOverride = payouts.map((p) => {
63
+ const tuple = buildMarketRateCurrencyOverride(p.currency, adapters);
64
+ if (!tuple) throw new Error(`Failed to build market-rate config for ${p.currency}`);
65
+ return [tuple];
66
+ });
67
+ const conversionRates = payouts.map((p) => [
68
+ { currency: p.currency, conversionRate: ORACLE_MIN_CONVERSION_RATE_SENTINEL.toString() }
69
+ ]);
70
+ const intentAmountRange = input.intentAmountRange ?? buildIntentAmountRange(input.amount);
71
+ return {
72
+ token: input.token ?? BASE_USDC_ADDRESS,
73
+ amount: input.amount,
74
+ intentAmountRange,
75
+ processorNames,
76
+ conversionRates,
77
+ paymentMethodsOverride,
78
+ paymentMethodDataOverride,
79
+ currenciesOverride,
80
+ retainOnEmpty: CASH_RETAIN_ON_EMPTY
81
+ };
82
+ }
83
+
84
+ // src/internal/convert.ts
85
+ function toBigInt(value) {
86
+ return toBigIntOrUndefined(value) ?? 0n;
87
+ }
88
+ function toBigIntOrUndefined(value) {
89
+ if (value === null || value === void 0 || value === "") return void 0;
90
+ try {
91
+ return BigInt(typeof value === "number" ? Math.trunc(value) : String(value));
92
+ } catch {
93
+ return void 0;
94
+ }
95
+ }
96
+
97
+ // src/engine/amounts.ts
98
+ function usdc(amount) {
99
+ const text = typeof amount === "number" ? amount.toString() : amount.trim();
100
+ if (!/^\d+(\.\d+)?$/.test(text)) {
101
+ throw new Error(`Invalid USDC amount: '${amount}'`);
102
+ }
103
+ const [whole = "0", frac = ""] = text.split(".");
104
+ if (frac.length > USDC_DECIMALS) {
105
+ throw new Error(`USDC has ${USDC_DECIMALS} decimals; '${amount}' has too many`);
106
+ }
107
+ return BigInt(whole) * 10n ** BigInt(USDC_DECIMALS) + BigInt(frac.padEnd(USDC_DECIMALS, "0") || "0");
108
+ }
109
+ function formatUsdc(amount) {
110
+ const negative = amount < 0n;
111
+ const abs = negative ? -amount : amount;
112
+ const whole = abs / 10n ** BigInt(USDC_DECIMALS);
113
+ const frac = (abs % 10n ** BigInt(USDC_DECIMALS)).toString().padStart(USDC_DECIMALS, "0");
114
+ const trimmed = frac.replace(/0+$/, "");
115
+ return `${negative ? "-" : ""}${whole}${trimmed ? `.${trimmed}` : ""}`;
116
+ }
117
+ var RATE_PRECISION = 10n ** 18n;
118
+ function fiatFromUsdc(amount, conversionRate) {
119
+ const raw = amount * conversionRate / RATE_PRECISION;
120
+ const penny = 10n ** BigInt(USDC_DECIMALS - 2);
121
+ const remainder = raw % penny;
122
+ return remainder > 0n ? raw - remainder + penny : raw;
123
+ }
124
+ function rateToNumber(conversionRate) {
125
+ return Number(conversionRate) / 1e18;
126
+ }
127
+ function fiatToNumber(fiat) {
128
+ return Number(fiat) / 10 ** USDC_DECIMALS;
129
+ }
130
+ function centsToNumber(cents) {
131
+ return Number(cents) / 100;
132
+ }
133
+
134
+ // src/engine/orderState.ts
135
+ var DUST_THRESHOLD = 10000n;
136
+ function toUnixSeconds(value) {
137
+ if (value === null || value === void 0 || value === "") return void 0;
138
+ const n = Number(value);
139
+ return Number.isFinite(n) && n > 0 ? n : void 0;
140
+ }
141
+ var FULFILLED_STATUSES = /* @__PURE__ */ new Set([
142
+ "FULFILLED",
143
+ "MANUALLY_RELEASED"
144
+ ]);
145
+ function toFill(intent) {
146
+ const signaledAt = toUnixSeconds(intent.signalTimestamp);
147
+ const expiresAt = toUnixSeconds(intent.expiryTime);
148
+ const fulfilledAt = toUnixSeconds(intent.fulfillTimestamp);
149
+ const prunedAt = toUnixSeconds(intent.prunedTimestamp ?? intent.pruneTimestamp);
150
+ const paidAt = toUnixSeconds(intent.paymentTimestamp);
151
+ const amount = toBigInt(intent.amount);
152
+ const conversionRate = toBigIntOrUndefined(intent.conversionRate);
153
+ const currency = intent.fiatCurrency != null ? getCurrencyCodeFromHash(intent.fiatCurrency) : void 0;
154
+ const paidCurrency = intent.paymentCurrency != null ? getCurrencyCodeFromHash(intent.paymentCurrency) : void 0;
155
+ const paymentCents = toBigIntOrUndefined(intent.paymentAmount);
156
+ const releasedAmount = toBigIntOrUndefined(intent.releasedAmount);
157
+ const fillLatencySeconds = signaledAt !== void 0 && fulfilledAt !== void 0 && fulfilledAt >= signaledAt ? fulfilledAt - signaledAt : void 0;
158
+ return {
159
+ intentHash: intent.intentHash,
160
+ status: intent.status,
161
+ amount,
162
+ buyer: (intent.owner ?? "").toLowerCase(),
163
+ ...currency !== void 0 ? { currency } : {},
164
+ ...intent.fiatCurrency != null ? { currencyHash: intent.fiatCurrency } : {},
165
+ ...conversionRate !== void 0 && conversionRate > 0n ? {
166
+ conversionRate,
167
+ rate: rateToNumber(conversionRate),
168
+ fiatOwed: fiatToNumber(fiatFromUsdc(amount, conversionRate))
169
+ } : {},
170
+ ...paymentCents !== void 0 && paymentCents > 0n ? { fiatPaid: centsToNumber(paymentCents) } : {},
171
+ ...paidCurrency !== void 0 ? { paidCurrency } : {},
172
+ ...intent.paymentId != null && intent.paymentId !== "" ? { paymentId: intent.paymentId } : {},
173
+ ...paidAt !== void 0 ? { paidAt } : {},
174
+ ...releasedAmount !== void 0 && releasedAmount > 0n ? { releasedAmount } : {},
175
+ ...fillLatencySeconds !== void 0 ? { fillLatencySeconds } : {},
176
+ ...intent.isExpired != null ? { isExpired: intent.isExpired } : {},
177
+ ...signaledAt !== void 0 ? { signaledAt } : {},
178
+ ...expiresAt !== void 0 ? { expiresAt } : {},
179
+ ...fulfilledAt !== void 0 ? { fulfilledAt } : {},
180
+ ...prunedAt !== void 0 ? { prunedAt } : {}
181
+ };
182
+ }
183
+ function isFillLive(fill, nowSeconds) {
184
+ if (fill.status !== "SIGNALED") return false;
185
+ if (fill.isExpired === true) return false;
186
+ return fill.expiresAt === void 0 || fill.expiresAt > nowSeconds;
187
+ }
188
+ function fmtUsdc(amount) {
189
+ return `${formatUsdc(amount)} USDC`;
190
+ }
191
+ function explainOrder(order) {
192
+ switch (order.state) {
193
+ case "awaiting-buyer":
194
+ return `Your ${fmtUsdc(order.totalAmount)} cash-out is live and waiting for a buyer; you can withdraw it any time before a buyer commits.`;
195
+ case "matched":
196
+ return `A buyer committed to ${fmtUsdc(order.pendingAmount)} and is sending fiat now; funds release automatically once their payment is proven.`;
197
+ case "delivering":
198
+ return `${fmtUsdc(order.filledAmount)} of ${fmtUsdc(order.totalAmount)} has been delivered; the rest is ${order.pendingAmount > 0n ? "locked by an active buyer" : "still waiting for a buyer"}.`;
199
+ case "delivered":
200
+ return `Cash-out complete: ${fmtUsdc(order.filledAmount)} was delivered to ${order.fills.filter((f) => f.fulfilledAt !== void 0).length || "your"} buyer fill(s).`;
201
+ case "returned":
202
+ return order.filledAmount > 0n ? `${fmtUsdc(order.filledAmount)} was delivered and the remaining ${fmtUsdc(order.returnedAmount)} was returned to your wallet.` : `No buyer delivered; ${fmtUsdc(order.returnedAmount)} was returned to your wallet.`;
203
+ }
204
+ }
205
+ function withExplain(data) {
206
+ return { ...data, explain: () => explainOrder(data) };
207
+ }
208
+ function deriveNextActions(state, hasLiveIntent) {
209
+ if (state === "delivered" || state === "returned") return [];
210
+ if (state === "awaiting-buyer") return ["wait", "withdraw"];
211
+ return hasLiveIntent ? ["wait"] : ["wait", "withdraw"];
212
+ }
213
+ function deriveCashOrder(depositId, intents, options = {}) {
214
+ const fills = intents.map(toFill);
215
+ const taken = options.takenAmount ?? fills.filter((f) => FULFILLED_STATUSES.has(f.status)).reduce((a, f) => a + f.amount, 0n);
216
+ const outstanding = options.outstandingAmount ?? fills.filter((f) => f.status === "SIGNALED").reduce((a, f) => a + f.amount, 0n);
217
+ const withdrawn = options.withdrawnAmount ?? 0n;
218
+ const remaining = options.remainingAmount ?? 0n;
219
+ const total = options.totalAmount ?? remaining + outstanding + taken + withdrawn;
220
+ const status = options.status;
221
+ const isTerminal = status === "CLOSED" || status === "WITHDRAWN";
222
+ const hasLiveFunds = remaining > DUST_THRESHOLD || outstanding > 0n;
223
+ let state;
224
+ if (outstanding > 0n) {
225
+ state = taken > 0n ? "delivering" : "matched";
226
+ } else if (taken > 0n && !hasLiveFunds) {
227
+ state = "delivered";
228
+ } else if (taken > 0n && hasLiveFunds) {
229
+ state = "delivering";
230
+ } else if (!hasLiveFunds && (withdrawn > 0n || isTerminal)) {
231
+ state = "returned";
232
+ } else if (hasLiveFunds) {
233
+ state = "awaiting-buyer";
234
+ } else {
235
+ state = taken > 0n ? "delivered" : "returned";
236
+ }
237
+ let matchedAt;
238
+ let deliveredAt;
239
+ for (const fill of fills) {
240
+ if (fill.signaledAt && (!matchedAt || fill.signaledAt < matchedAt)) matchedAt = fill.signaledAt;
241
+ if (FULFILLED_STATUSES.has(fill.status) && fill.fulfilledAt) {
242
+ if (!deliveredAt || fill.fulfilledAt > deliveredAt) deliveredAt = fill.fulfilledAt;
243
+ }
244
+ }
245
+ const primary = fills.find((f) => f.status === "SIGNALED") ?? fills.find((f) => FULFILLED_STATUSES.has(f.status)) ?? fills[0];
246
+ const isInFlight = state === "awaiting-buyer" || state === "matched" || state === "delivering";
247
+ const nowSeconds = options.nowSeconds ?? Math.floor(Date.now() / 1e3);
248
+ const fillsIncluded = options.fillsIncluded ?? fills.length > 0;
249
+ const hasLiveIntent = fillsIncluded ? fills.some((f) => isFillLive(f, nowSeconds)) : outstanding > 0n;
250
+ return withExplain({
251
+ depositId,
252
+ state,
253
+ fills,
254
+ totalAmount: total,
255
+ filledAmount: taken,
256
+ pendingAmount: outstanding,
257
+ returnedAmount: withdrawn,
258
+ nextActions: deriveNextActions(state, hasLiveIntent),
259
+ ...primary?.intentHash !== void 0 ? { primaryIntentHash: primary.intentHash } : {},
260
+ ...matchedAt !== void 0 ? { matchedAt } : {},
261
+ ...deliveredAt !== void 0 ? { deliveredAt } : {},
262
+ ...options.updatedAt !== void 0 ? { updatedAt: options.updatedAt } : {},
263
+ intentCount: options.intentCount ?? fills.length,
264
+ ...options.payouts !== void 0 ? { payouts: options.payouts } : {},
265
+ ...options.successRateBps !== void 0 ? { successRateBps: options.successRateBps } : {},
266
+ isInFlight,
267
+ withdrawn: isTerminal
268
+ });
269
+ }
270
+ var ORACLE_KINDS = /* @__PURE__ */ new Set(["oracle_chainlink", "oracle_pyth"]);
271
+ function toPricing(tuple) {
272
+ if (!tuple) return { marketRate: false };
273
+ const spreadBps = tuple.spreadBps != null ? Number(tuple.spreadBps) : void 0;
274
+ const oracleRate = toBigIntOrUndefined(tuple.oracleRate);
275
+ const lastOracleUpdatedAt = tuple.lastOracleUpdatedAt != null ? Number(tuple.lastOracleUpdatedAt) : void 0;
276
+ return {
277
+ ...spreadBps !== void 0 && Number.isFinite(spreadBps) ? { spreadBps } : {},
278
+ ...tuple.kind != null ? { kind: tuple.kind } : {},
279
+ ...tuple.rateSource != null ? { rateSource: tuple.rateSource } : {},
280
+ ...oracleRate !== void 0 && oracleRate > 0n ? { oracleRate: rateToNumber(oracleRate) } : {},
281
+ ...lastOracleUpdatedAt !== void 0 && Number.isFinite(lastOracleUpdatedAt) ? { lastOracleUpdatedAt } : {},
282
+ marketRate: spreadBps === 0 && tuple.kind != null && ORACLE_KINDS.has(tuple.kind)
283
+ };
284
+ }
285
+ function derivePayouts(paymentMethods, currencies, catalog) {
286
+ return paymentMethods.flatMap((method) => {
287
+ const platformHash = method.paymentMethodHash ?? "";
288
+ if (!platformHash) return [];
289
+ let platform;
290
+ try {
291
+ platform = resolvePaymentMethodNameFromHash(platformHash, catalog);
292
+ } catch {
293
+ platform = void 0;
294
+ }
295
+ const tuples = currencies.filter(
296
+ (c) => (c.paymentMethodHash ?? "").toLowerCase() === platformHash.toLowerCase()
297
+ );
298
+ const base2 = {
299
+ ...platform !== void 0 ? { platform } : {},
300
+ platformHash,
301
+ payeeHash: method.payeeDetailsHash ?? "",
302
+ active: method.active ?? true
303
+ };
304
+ if (tuples.length === 0) return [{ ...base2, pricing: toPricing(void 0) }];
305
+ return tuples.map((tuple) => {
306
+ const currency = tuple.currencyCode != null ? getCurrencyCodeFromHash(tuple.currencyCode) : void 0;
307
+ return {
308
+ ...base2,
309
+ ...currency !== void 0 ? { currency } : {},
310
+ ...tuple.currencyCode != null ? { currencyHash: tuple.currencyCode } : {},
311
+ pricing: toPricing(tuple)
312
+ };
313
+ });
314
+ });
315
+ }
316
+
317
+ // src/engine/buyerProfile.ts
318
+ function toSeconds(value) {
319
+ if (value === null || value === void 0 || value === "") return void 0;
320
+ const n = Number(value);
321
+ return Number.isFinite(n) && n > 0 ? n : void 0;
322
+ }
323
+ function deriveBuyerProfile(address, intents) {
324
+ let fulfilled = 0;
325
+ let pruned = 0;
326
+ let signaled = 0;
327
+ let firstSeenAt;
328
+ let lastSeenAt;
329
+ for (const intent of intents) {
330
+ const status = intent.status ?? "";
331
+ if (status === "FULFILLED" || status === "MANUALLY_RELEASED") fulfilled += 1;
332
+ else if (status === "PRUNED") pruned += 1;
333
+ else if (status === "SIGNALED") signaled += 1;
334
+ const at = toSeconds(intent.signalTimestamp);
335
+ if (at !== void 0) {
336
+ if (firstSeenAt === void 0 || at < firstSeenAt) firstSeenAt = at;
337
+ if (lastSeenAt === void 0 || at > lastSeenAt) lastSeenAt = at;
338
+ }
339
+ }
340
+ const settled = fulfilled + pruned;
341
+ return {
342
+ address: address.toLowerCase(),
343
+ totalIntents: intents.length,
344
+ fulfilled,
345
+ pruned,
346
+ signaled,
347
+ ...settled > 0 ? { successRateBps: Math.round(fulfilled / settled * 1e4) } : {},
348
+ ...firstSeenAt !== void 0 ? { firstSeenAt } : {},
349
+ ...lastSeenAt !== void 0 ? { lastSeenAt } : {}
350
+ };
351
+ }
352
+ function resolveCashDepositId(params) {
353
+ let events;
354
+ try {
355
+ events = parseEventLogs({
356
+ abi: params.abi,
357
+ eventName: "DepositReceived",
358
+ logs: params.logs
359
+ });
360
+ } catch {
361
+ return null;
362
+ }
363
+ const event = events[0];
364
+ if (!event) return null;
365
+ const rawId = event.args.depositId;
366
+ if (rawId === void 0 || rawId === null) return null;
367
+ const onchainDepositId = BigInt(rawId);
368
+ const escrowAddress = event.address.toLowerCase();
369
+ return {
370
+ onchainDepositId,
371
+ escrowAddress,
372
+ compositeId: createCompositeDepositId(escrowAddress, onchainDepositId)
373
+ };
374
+ }
375
+ function parseCompositeDepositId(compositeId) {
376
+ const idx = compositeId.lastIndexOf("_");
377
+ if (idx === -1) {
378
+ return { escrowAddress: "", onchainDepositId: BigInt(compositeId) };
379
+ }
380
+ const escrowAddress = compositeId.slice(0, idx);
381
+ const onchainDepositId = BigInt(compositeId.slice(idx + 1) || "0");
382
+ return { escrowAddress, onchainDepositId };
383
+ }
384
+ var MIN_CASHOUT_AMOUNT = 10000n;
385
+ var RECOMMENDED_MIN_CASHOUT_AMOUNT = 1000000n;
386
+ var PAYEE_HINTS = {
387
+ venmo: "Venmo username, with or without the leading @ (e.g. @andrew-w)",
388
+ cashapp: "Cashtag, with or without the leading $ (e.g. $andrew)",
389
+ revolut: "Revtag (e.g. andrew1abc)",
390
+ wise: "Wisetag or the email on the Wise account",
391
+ zelle: "Email address or US phone number enrolled with Zelle",
392
+ paypal: "PayPal.Me handle or account email",
393
+ mercadopago: "Mercado Pago alias or CVU",
394
+ monzo: "Monzo.me username",
395
+ chime: "ChimeSign (e.g. $andrew)",
396
+ luxon: "Luxon Pay ID or account email",
397
+ n26: "MoneyBeam email or phone number"
398
+ };
399
+ var IDENTITY_ATTESTATION_PLATFORMS = /* @__PURE__ */ new Set(["wise", "paypal"]);
400
+ function platformRequiresIdentityAttestation(platform) {
401
+ return IDENTITY_ATTESTATION_PLATFORMS.has(platform);
402
+ }
403
+ function buildCapabilities(environment) {
404
+ const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
405
+ const platforms = Object.entries(catalog).map(([platform, entry]) => {
406
+ const currencies2 = (entry.currencies ?? []).map((hash) => getCurrencyCodeFromHash(hash)).filter(
407
+ (code) => code != null && isMarketRateSupported(code)
408
+ );
409
+ return {
410
+ platform,
411
+ currencies: [...new Set(currencies2)],
412
+ payeeHint: PAYEE_HINTS[platform] ?? "Your payment handle for this platform",
413
+ requiresIdentityAttestation: IDENTITY_ATTESTATION_PLATFORMS.has(platform)
414
+ };
415
+ }).filter((p) => p.currencies.length > 0).sort((a, b) => a.platform.localeCompare(b.platform));
416
+ const currencies = [...new Set(platforms.flatMap((p) => p.currencies))].sort();
417
+ return {
418
+ chainId: BASE_CHAIN_ID,
419
+ token: { address: BASE_USDC_ADDRESS, symbol: "USDC", decimals: USDC_DECIMALS },
420
+ environment,
421
+ platforms,
422
+ currencies,
423
+ amount: { min: MIN_CASHOUT_AMOUNT, recommendedMin: RECOMMENDED_MIN_CASHOUT_AMOUNT, max: null },
424
+ pricing: { kind: "oracle-market-rate", spreadBps: 0 }
425
+ };
426
+ }
427
+ var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
428
+ var CHAINLINK_LATEST_ROUND_ABI = [
429
+ {
430
+ name: "latestRoundData",
431
+ type: "function",
432
+ stateMutability: "view",
433
+ inputs: [],
434
+ outputs: [
435
+ { name: "roundId", type: "uint80" },
436
+ { name: "answer", type: "int256" },
437
+ { name: "startedAt", type: "uint256" },
438
+ { name: "updatedAt", type: "uint256" },
439
+ { name: "answeredInRound", type: "uint80" }
440
+ ]
441
+ }
442
+ ];
443
+ var DEFAULT_MAX_STALENESS_SECONDS = 86400;
444
+ async function readEstimate(publicClient, input) {
445
+ const { amount, currency } = input;
446
+ if (amount < MIN_CASHOUT_AMOUNT) {
447
+ throw errors.amountBelowMinimum(amount, MIN_CASHOUT_AMOUNT);
448
+ }
449
+ if (!isMarketRateSupported(currency)) {
450
+ throw errors.oracleUnsupportedCurrency(currency);
451
+ }
452
+ const feedConfig = CHAINLINK_ORACLE_FEEDS[currency];
453
+ const asOf = Math.floor(Date.now() / 1e3);
454
+ let rate;
455
+ let oracleUpdatedAt;
456
+ if (!feedConfig || feedConfig.feed.toLowerCase() === ZERO_ADDRESS) {
457
+ rate = 1;
458
+ } else {
459
+ const result = await publicClient.readContract({
460
+ address: feedConfig.feed,
461
+ abi: CHAINLINK_LATEST_ROUND_ABI,
462
+ functionName: "latestRoundData"
463
+ });
464
+ const answer = Number(result[1]);
465
+ const price = answer / 10 ** feedConfig.decimals;
466
+ if (!Number.isFinite(price) || price <= 0) {
467
+ throw errors.oracleUnsupportedCurrency(currency);
468
+ }
469
+ const updatedAt = Number(result[3]);
470
+ if (Number.isFinite(updatedAt) && updatedAt > 0) oracleUpdatedAt = updatedAt;
471
+ rate = feedConfig.invert ? 1 / price : price;
472
+ }
473
+ const stale = oracleUpdatedAt !== void 0 && asOf - oracleUpdatedAt > DEFAULT_MAX_STALENESS_SECONDS;
474
+ return {
475
+ kind: "oracle-estimate",
476
+ currency,
477
+ amount,
478
+ rate,
479
+ receiveAmount: Number(amount) / 10 ** USDC_DECIMALS * rate,
480
+ asOf,
481
+ ...oracleUpdatedAt !== void 0 ? { oracleUpdatedAt } : {},
482
+ ...stale ? { stale: true } : {}
483
+ };
484
+ }
485
+
486
+ // src/client/createCashClient.ts
487
+ var DEFAULT_RPC_URL = "https://mainnet.base.org";
488
+ var CASH_ATTRIBUTION_CODE = "peer-cash";
489
+ var DEFAULT_CURATOR_URLS = {
490
+ staging: "https://api-staging.zkp2p.xyz"
491
+ };
492
+ var ERC20_APPROVE_ABI = parseAbi([
493
+ "function approve(address spender, uint256 amount) returns (bool)"
494
+ ]);
495
+ var ERC20_ALLOWANCE_ABI = parseAbi([
496
+ "function allowance(address owner, address spender) view returns (uint256)"
497
+ ]);
498
+ function sleep(ms, signal) {
499
+ return new Promise((resolve) => {
500
+ const timer = setTimeout(done, ms);
501
+ function done() {
502
+ signal?.removeEventListener("abort", done);
503
+ clearTimeout(timer);
504
+ resolve();
505
+ }
506
+ signal?.addEventListener("abort", done, { once: true });
507
+ });
508
+ }
509
+ function orderFingerprint(order) {
510
+ return [
511
+ order.state,
512
+ order.filledAmount,
513
+ order.pendingAmount,
514
+ order.returnedAmount,
515
+ order.intentCount ?? 0,
516
+ order.nextActions.join("+")
517
+ ].join("|");
518
+ }
519
+ async function submitAndConfirm(client, verb, send) {
520
+ let hash;
521
+ try {
522
+ hash = await send();
523
+ } catch (err) {
524
+ throw mapChainError(verb, err);
525
+ }
526
+ const receipt = await client.publicClient.waitForTransactionReceipt({ hash });
527
+ if (receipt.status === "reverted") throw errors.transactionFailed(hash);
528
+ return hash;
529
+ }
530
+ function depositOrderOptions(deposit) {
531
+ const remaining = toBigIntOrUndefined(deposit.remainingDeposits);
532
+ const outstanding = toBigIntOrUndefined(deposit.outstandingIntentAmount);
533
+ const taken = toBigIntOrUndefined(deposit.totalAmountTaken);
534
+ const withdrawn = toBigIntOrUndefined(deposit.totalWithdrawn);
535
+ const updatedAt = deposit.updatedAt != null ? Number(deposit.updatedAt) : void 0;
536
+ return {
537
+ ...remaining !== void 0 ? { remainingAmount: remaining } : {},
538
+ ...outstanding !== void 0 ? { outstandingAmount: outstanding } : {},
539
+ ...taken !== void 0 ? { takenAmount: taken } : {},
540
+ ...withdrawn !== void 0 ? { withdrawnAmount: withdrawn } : {},
541
+ ...deposit.status != null ? { status: deposit.status } : {},
542
+ ...deposit.totalIntents != null ? { intentCount: deposit.totalIntents } : {},
543
+ ...deposit.successRateBps != null ? { successRateBps: deposit.successRateBps } : {},
544
+ ...updatedAt !== void 0 && Number.isFinite(updatedAt) ? { updatedAt } : {}
545
+ };
546
+ }
547
+ function createCashClient(options) {
548
+ const { environment } = options;
549
+ const transport = options.transport ?? http(options.rpcUrl ?? DEFAULT_RPC_URL);
550
+ const referrerCodes = [
551
+ CASH_ATTRIBUTION_CODE,
552
+ ...options.referrer === void 0 ? [] : Array.isArray(options.referrer) ? options.referrer : [options.referrer]
553
+ ];
554
+ const attribution = { referrer: referrerCodes };
555
+ function buildSdkClient(walletClient) {
556
+ return new Zkp2pClient({
557
+ walletClient,
558
+ chainId: BASE_CHAIN_ID,
559
+ runtimeEnv: environment,
560
+ rpcTransport: transport,
561
+ ...options.rpcUrl ? { rpcUrl: options.rpcUrl } : {},
562
+ ...options.indexerUrl ? { indexerUrl: options.indexerUrl } : {},
563
+ ...options.curatorUrl ?? DEFAULT_CURATOR_URLS[environment] ? { baseApiUrl: options.curatorUrl ?? DEFAULT_CURATOR_URLS[environment] } : {},
564
+ ...options.apiKey ? { apiKey: options.apiKey } : {}
565
+ });
566
+ }
567
+ const readClient = buildSdkClient(createWalletClient({ chain: base, transport }));
568
+ const signingClients = /* @__PURE__ */ new WeakMap();
569
+ function signingClient(verb, opts) {
570
+ const signer = opts?.signer;
571
+ if (!signer?.account) throw errors.signerRequired(verb);
572
+ let client = signingClients.get(signer);
573
+ if (!client) {
574
+ client = buildSdkClient(signer);
575
+ signingClients.set(signer, client);
576
+ }
577
+ return client;
578
+ }
579
+ function validateInput(input) {
580
+ const { amount, receive } = input;
581
+ if (amount < MIN_CASHOUT_AMOUNT) throw errors.amountBelowMinimum(amount, MIN_CASHOUT_AMOUNT);
582
+ const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
583
+ if (!catalog[receive.platform]) throw errors.unsupportedPlatform(receive.platform);
584
+ if (!isMarketRateSupported(receive.currency)) {
585
+ throw errors.oracleUnsupportedCurrency(receive.currency);
586
+ }
587
+ if (platformRequiresIdentityAttestation(receive.platform) && !receive.payee.identityAttestation) {
588
+ throw errors.payeeVerificationRequired(receive.platform);
589
+ }
590
+ return {
591
+ amount,
592
+ payouts: [
593
+ {
594
+ processorName: receive.platform,
595
+ currency: receive.currency,
596
+ payeeData: receive.payee
597
+ }
598
+ ],
599
+ ...input.intentAmountRange ? { intentAmountRange: input.intentAmountRange } : {}
600
+ };
601
+ }
602
+ async function buildDepositParams(client, depositInput) {
603
+ try {
604
+ return await prepareCashDepositParams(client, depositInput);
605
+ } catch (err) {
606
+ if (isCashError(err)) throw err;
607
+ const message = err instanceof Error ? err.message : String(err);
608
+ if (/identityAttestation is required|identity attestation/i.test(message)) {
609
+ const platform = depositInput.payouts[0]?.processorName ?? "this platform";
610
+ throw errors.payeeVerificationRequired(platform, err);
611
+ }
612
+ throw errors.payeeRegistrationFailed(err);
613
+ }
614
+ }
615
+ async function fetchOrder(depositId) {
616
+ const deposits = await readClient.indexer.getDepositsByIdsWithRelations([depositId], {
617
+ includeIntents: true,
618
+ intentStatuses: CASH_ORDER_STATUSES
619
+ });
620
+ const deposit = deposits[0];
621
+ if (!deposit) {
622
+ const intents = await readClient.indexer.getIntentsForDeposits(
623
+ [depositId],
624
+ CASH_ORDER_STATUSES
625
+ );
626
+ if (intents.length === 0) throw errors.orderNotFound(depositId);
627
+ return deriveCashOrder(depositId, intents);
628
+ }
629
+ const payouts = derivePayouts(
630
+ deposit.paymentMethods ?? [],
631
+ deposit.currencies ?? [],
632
+ getPaymentMethodsCatalog(BASE_CHAIN_ID, environment)
633
+ );
634
+ return deriveCashOrder(depositId, deposit.intents ?? [], {
635
+ ...depositOrderOptions(deposit),
636
+ ...payouts.length > 0 ? { payouts } : {}
637
+ });
638
+ }
639
+ function escrowContext(depositId) {
640
+ const { escrowAddress, onchainDepositId } = parseCompositeDepositId(depositId);
641
+ return {
642
+ onchainDepositId,
643
+ escrowArg: escrowAddress ? { escrowAddress } : {}
644
+ };
645
+ }
646
+ function availableAmount(order) {
647
+ return order.totalAmount - order.filledAmount - order.pendingAmount - order.returnedAmount;
648
+ }
649
+ async function withdrawContext(depositId) {
650
+ const order = await fetchOrder(depositId);
651
+ const nowSeconds = Math.floor(Date.now() / 1e3);
652
+ const signaled = order.fills.filter((f) => f.status === "SIGNALED");
653
+ const liveIntent = signaled.some((f) => isFillLive(f, nowSeconds));
654
+ const expiredIntent = signaled.length > 0 && !liveIntent;
655
+ if (order.pendingAmount > 0n && liveIntent) {
656
+ throw errors.activeIntentBlocksWithdrawal(depositId);
657
+ }
658
+ if (availableAmount(order) <= 0n && order.pendingAmount === 0n) {
659
+ throw errors.nothingToWithdraw(depositId);
660
+ }
661
+ return { expiredIntent, ...escrowContext(depositId) };
662
+ }
663
+ async function partialWithdrawContext(depositId, amount) {
664
+ if (amount <= 0n) throw errors.amountBelowMinimum(amount, 1n);
665
+ const order = await fetchOrder(depositId);
666
+ const available = availableAmount(order);
667
+ if (amount > available) {
668
+ throw errors.insufficientAvailableFunds(depositId, amount, available);
669
+ }
670
+ return escrowContext(depositId);
671
+ }
672
+ async function topUpContext(depositId, amount) {
673
+ if (amount < MIN_CASHOUT_AMOUNT) throw errors.amountBelowMinimum(amount, MIN_CASHOUT_AMOUNT);
674
+ const order = await fetchOrder(depositId);
675
+ if (!order.isInFlight) throw errors.orderNotActive(depositId);
676
+ return escrowContext(depositId);
677
+ }
678
+ async function settleAllowance(client, token, owner, escrow, amount) {
679
+ let allowance;
680
+ try {
681
+ allowance = await client.ensureAllowance({
682
+ token,
683
+ amount,
684
+ spender: escrow,
685
+ txOverrides: attribution
686
+ });
687
+ } catch (err) {
688
+ throw mapChainError("approve", err);
689
+ }
690
+ if (allowance.hadAllowance || !allowance.hash) return;
691
+ const receipt = await client.publicClient.waitForTransactionReceipt({ hash: allowance.hash });
692
+ if (receipt.status === "reverted") throw errors.transactionFailed(allowance.hash);
693
+ for (let attempt = 0; attempt < 15; attempt++) {
694
+ const visible = await client.publicClient.readContract({
695
+ address: token,
696
+ abi: ERC20_ALLOWANCE_ABI,
697
+ functionName: "allowance",
698
+ args: [owner, escrow]
699
+ });
700
+ if (visible >= amount) return;
701
+ await sleep(1e3);
702
+ }
703
+ throw errors.allowanceNotVisible(amount);
704
+ }
705
+ return {
706
+ capabilities() {
707
+ return buildCapabilities(environment);
708
+ },
709
+ async estimate(input) {
710
+ return readEstimate(readClient.publicClient, input);
711
+ },
712
+ async cashout(input, opts) {
713
+ const depositInput = validateInput(input);
714
+ const client = signingClient("cashout", opts);
715
+ const params = await buildDepositParams(client, depositInput);
716
+ const escrow = client.escrowV2Address ?? client.escrowAddress;
717
+ const owner = opts.signer.account.address;
718
+ await settleAllowance(client, params.token, owner, escrow, depositInput.amount);
719
+ const attributedParams = { ...params, txOverrides: attribution };
720
+ const send = async () => {
721
+ try {
722
+ return (await client.createDeposit(attributedParams)).hash;
723
+ } catch (err) {
724
+ if (err instanceof Error && /exceeds allowance/i.test(err.message)) {
725
+ await sleep(2e3);
726
+ return (await client.createDeposit(attributedParams)).hash;
727
+ }
728
+ throw err;
729
+ }
730
+ };
731
+ let hash;
732
+ try {
733
+ hash = await send();
734
+ } catch (err) {
735
+ throw mapChainError("createDeposit", err);
736
+ }
737
+ const receipt = await client.publicClient.waitForTransactionReceipt({ hash });
738
+ if (receipt.status === "reverted") throw errors.transactionFailed(hash);
739
+ const abi = client.escrowV2Abi ?? client.escrowAbi;
740
+ const resolved = resolveCashDepositId({ logs: receipt.logs, abi });
741
+ if (!resolved) throw errors.depositResolutionFailed(hash);
742
+ const order = deriveCashOrder(resolved.compositeId, [], {
743
+ remainingAmount: depositInput.amount,
744
+ status: "ACTIVE"
745
+ });
746
+ return {
747
+ depositId: resolved.compositeId,
748
+ txHash: hash,
749
+ escrowAddress: resolved.escrowAddress,
750
+ onchainDepositId: resolved.onchainDepositId,
751
+ order
752
+ };
753
+ },
754
+ async prepare(input) {
755
+ const depositInput = validateInput(input);
756
+ const params = await buildDepositParams(readClient, depositInput);
757
+ const { prepared } = await readClient.prepareCreateDeposit({
758
+ ...params,
759
+ txOverrides: attribution
760
+ });
761
+ const approve = {
762
+ to: params.token,
763
+ data: appendAttributionToCalldata(
764
+ encodeFunctionData({
765
+ abi: ERC20_APPROVE_ABI,
766
+ functionName: "approve",
767
+ args: [prepared.to, depositInput.amount]
768
+ }),
769
+ referrerCodes
770
+ ),
771
+ value: 0n,
772
+ chainId: BASE_CHAIN_ID
773
+ };
774
+ const hashedOnchainIds = (params.paymentMethodDataOverride ?? []).map((d) => d.payeeDetails);
775
+ return {
776
+ txs: [approve, prepared],
777
+ steps: [
778
+ {
779
+ kind: "approve",
780
+ description: "Approve Base USDC for the Peer Cash escrow."
781
+ },
782
+ {
783
+ kind: "createDeposit",
784
+ description: "Create the protocol-held cash-out order."
785
+ }
786
+ ],
787
+ register: { hashedOnchainIds }
788
+ };
789
+ },
790
+ async order(depositId) {
791
+ return fetchOrder(depositId);
792
+ },
793
+ async buyer(address) {
794
+ const intents = await readClient.indexer.getOwnerIntents(address, CASH_ORDER_STATUSES);
795
+ return deriveBuyerProfile(address, intents);
796
+ },
797
+ async orders(owner, opts = {}) {
798
+ const { inFlight = false, limit = 100 } = opts;
799
+ const deposits = await readClient.indexer.getDeposits({ depositor: owner }, { limit });
800
+ const derived = deposits.map((d) => deriveCashOrder(d.id, [], { ...depositOrderOptions(d), fillsIncluded: false })).filter((o) => o.totalAmount > 10000n).sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
801
+ return inFlight ? derived.filter((o) => o.isInFlight) : derived;
802
+ },
803
+ async *watch(depositId, opts = {}) {
804
+ const { signal, pollIntervalMs = 5e3, timeoutMs } = opts;
805
+ const startedAt = Date.now();
806
+ let lastFingerprint;
807
+ while (true) {
808
+ if (signal?.aborted) return;
809
+ if (timeoutMs !== void 0 && Date.now() - startedAt >= timeoutMs) {
810
+ throw errors.watchTimeout(depositId, timeoutMs);
811
+ }
812
+ let order = null;
813
+ try {
814
+ order = await fetchOrder(depositId);
815
+ } catch (err) {
816
+ if (!(isCashError(err) && err.code === "ORDER_NOT_FOUND")) throw err;
817
+ }
818
+ if (order) {
819
+ const fingerprint = orderFingerprint(order);
820
+ if (fingerprint !== lastFingerprint) {
821
+ lastFingerprint = fingerprint;
822
+ yield order;
823
+ }
824
+ if (!order.isInFlight) return;
825
+ }
826
+ await sleep(pollIntervalMs, signal);
827
+ }
828
+ },
829
+ async withdraw(depositId, opts) {
830
+ const client = signingClient("withdraw", opts);
831
+ if (opts.amount !== void 0) {
832
+ const { onchainDepositId: onchainDepositId2, escrowArg: escrowArg2 } = await partialWithdrawContext(
833
+ depositId,
834
+ opts.amount
835
+ );
836
+ const withdrawTxHash2 = await submitAndConfirm(
837
+ client,
838
+ "removeFunds",
839
+ () => client.removeFunds({
840
+ depositId: onchainDepositId2,
841
+ amount: opts.amount,
842
+ ...escrowArg2,
843
+ txOverrides: attribution
844
+ })
845
+ );
846
+ return { depositId, withdrawTxHash: withdrawTxHash2 };
847
+ }
848
+ const { expiredIntent, onchainDepositId, escrowArg } = await withdrawContext(depositId);
849
+ let pruneTxHash;
850
+ if (expiredIntent) {
851
+ pruneTxHash = await submitAndConfirm(
852
+ client,
853
+ "pruneExpiredIntents",
854
+ () => client.pruneExpiredIntents({
855
+ depositId: onchainDepositId,
856
+ ...escrowArg,
857
+ txOverrides: attribution
858
+ })
859
+ );
860
+ }
861
+ const withdrawTxHash = await submitAndConfirm(
862
+ client,
863
+ "withdrawDeposit",
864
+ () => client.withdrawDeposit({
865
+ depositId: onchainDepositId,
866
+ ...escrowArg,
867
+ txOverrides: attribution
868
+ })
869
+ );
870
+ return {
871
+ depositId,
872
+ ...pruneTxHash !== void 0 ? { pruneTxHash } : {},
873
+ withdrawTxHash
874
+ };
875
+ },
876
+ async prepareWithdraw(depositId, opts = {}) {
877
+ if (opts.amount !== void 0) {
878
+ const { onchainDepositId: onchainDepositId2, escrowArg: escrowArg2 } = await partialWithdrawContext(
879
+ depositId,
880
+ opts.amount
881
+ );
882
+ const tx = await readClient.removeFunds.prepare({
883
+ depositId: onchainDepositId2,
884
+ amount: opts.amount,
885
+ ...escrowArg2,
886
+ txOverrides: attribution
887
+ });
888
+ return {
889
+ txs: [tx],
890
+ steps: [
891
+ {
892
+ kind: "removeFunds",
893
+ description: "Withdraw the requested unlocked USDC amount."
894
+ }
895
+ ]
896
+ };
897
+ }
898
+ const { expiredIntent, onchainDepositId, escrowArg } = await withdrawContext(depositId);
899
+ const txs = [];
900
+ const steps = [];
901
+ if (expiredIntent) {
902
+ txs.push(
903
+ await readClient.pruneExpiredIntents.prepare({
904
+ depositId: onchainDepositId,
905
+ ...escrowArg,
906
+ txOverrides: attribution
907
+ })
908
+ );
909
+ steps.push({
910
+ kind: "pruneExpiredIntents",
911
+ description: "Prune expired buyer intents so the locked amount becomes withdrawable."
912
+ });
913
+ }
914
+ txs.push(
915
+ await readClient.withdrawDeposit.prepare({
916
+ depositId: onchainDepositId,
917
+ ...escrowArg,
918
+ txOverrides: attribution
919
+ })
920
+ );
921
+ steps.push({
922
+ kind: "withdrawDeposit",
923
+ description: "Close the order and withdraw all remaining USDC."
924
+ });
925
+ return { txs, steps };
926
+ },
927
+ async topUp(depositId, amount, opts) {
928
+ const client = signingClient("topUp", opts);
929
+ const { onchainDepositId, escrowArg } = await topUpContext(depositId, amount);
930
+ const owner = opts.signer.account.address;
931
+ const escrow = escrowArg.escrowAddress ?? client.escrowV2Address ?? client.escrowAddress;
932
+ await settleAllowance(client, BASE_USDC_ADDRESS, owner, escrow, amount);
933
+ const txHash = await submitAndConfirm(
934
+ client,
935
+ "addFunds",
936
+ () => client.addFunds({
937
+ depositId: onchainDepositId,
938
+ amount,
939
+ ...escrowArg,
940
+ txOverrides: attribution
941
+ })
942
+ );
943
+ return { depositId, txHash };
944
+ },
945
+ async prepareTopUp(depositId, amount) {
946
+ const { onchainDepositId, escrowArg } = await topUpContext(depositId, amount);
947
+ const prepared = await readClient.addFunds.prepare({
948
+ depositId: onchainDepositId,
949
+ amount,
950
+ ...escrowArg,
951
+ txOverrides: attribution
952
+ });
953
+ const approve = {
954
+ to: BASE_USDC_ADDRESS,
955
+ data: appendAttributionToCalldata(
956
+ encodeFunctionData({
957
+ abi: ERC20_APPROVE_ABI,
958
+ functionName: "approve",
959
+ args: [prepared.to, amount]
960
+ }),
961
+ referrerCodes
962
+ ),
963
+ value: 0n,
964
+ chainId: BASE_CHAIN_ID
965
+ };
966
+ return {
967
+ txs: [approve, prepared],
968
+ steps: [
969
+ {
970
+ kind: "approve",
971
+ description: "Approve additional Base USDC for the Peer Cash escrow."
972
+ },
973
+ {
974
+ kind: "addFunds",
975
+ description: "Add USDC to the live cash-out order."
976
+ }
977
+ ]
978
+ };
979
+ }
980
+ };
981
+ }
982
+ var bigintString = z.string().regex(/^-?\d+$/, "expected a decimal bigint string");
983
+ var cashOrderStateSchema = z.enum([
984
+ "awaiting-buyer",
985
+ "matched",
986
+ "delivering",
987
+ "delivered",
988
+ "returned"
989
+ ]);
990
+ var cashNextActionSchema = z.enum(["wait", "withdraw"]);
991
+ var intentStatusSchema = z.enum(["SIGNALED", "FULFILLED", "PRUNED", "MANUALLY_RELEASED"]);
992
+ var cashFillJsonSchema = z.object({
993
+ intentHash: z.string(),
994
+ status: intentStatusSchema,
995
+ amount: bigintString,
996
+ buyer: z.string(),
997
+ currency: z.string().optional(),
998
+ currencyHash: z.string().optional(),
999
+ rate: z.number().optional(),
1000
+ conversionRate: bigintString.optional(),
1001
+ fiatOwed: z.number().optional(),
1002
+ fiatPaid: z.number().optional(),
1003
+ paidCurrency: z.string().optional(),
1004
+ paymentId: z.string().optional(),
1005
+ paidAt: z.number().optional(),
1006
+ releasedAmount: bigintString.optional(),
1007
+ fillLatencySeconds: z.number().optional(),
1008
+ isExpired: z.boolean().optional(),
1009
+ signaledAt: z.number().optional(),
1010
+ expiresAt: z.number().optional(),
1011
+ fulfilledAt: z.number().optional(),
1012
+ prunedAt: z.number().optional()
1013
+ });
1014
+ var cashPayoutPricingJsonSchema = z.object({
1015
+ spreadBps: z.number().optional(),
1016
+ kind: z.string().optional(),
1017
+ rateSource: z.string().optional(),
1018
+ oracleRate: z.number().optional(),
1019
+ lastOracleUpdatedAt: z.number().optional(),
1020
+ marketRate: z.boolean()
1021
+ });
1022
+ var cashPayoutInfoJsonSchema = z.object({
1023
+ platform: z.string().optional(),
1024
+ platformHash: z.string(),
1025
+ currency: z.string().optional(),
1026
+ currencyHash: z.string().optional(),
1027
+ payeeHash: z.string(),
1028
+ active: z.boolean(),
1029
+ pricing: cashPayoutPricingJsonSchema
1030
+ });
1031
+ var cashBuyerProfileJsonSchema = z.object({
1032
+ address: z.string(),
1033
+ totalIntents: z.number(),
1034
+ fulfilled: z.number(),
1035
+ pruned: z.number(),
1036
+ signaled: z.number(),
1037
+ successRateBps: z.number().optional(),
1038
+ firstSeenAt: z.number().optional(),
1039
+ lastSeenAt: z.number().optional()
1040
+ });
1041
+ var cashOrderJsonSchema = z.object({
1042
+ depositId: z.string(),
1043
+ state: cashOrderStateSchema,
1044
+ fills: z.array(cashFillJsonSchema),
1045
+ totalAmount: bigintString,
1046
+ filledAmount: bigintString,
1047
+ pendingAmount: bigintString,
1048
+ returnedAmount: bigintString,
1049
+ nextActions: z.array(cashNextActionSchema),
1050
+ primaryIntentHash: z.string().optional(),
1051
+ matchedAt: z.number().optional(),
1052
+ deliveredAt: z.number().optional(),
1053
+ updatedAt: z.number().optional(),
1054
+ intentCount: z.number().optional(),
1055
+ payouts: z.array(cashPayoutInfoJsonSchema).optional(),
1056
+ successRateBps: z.number().optional(),
1057
+ isInFlight: z.boolean(),
1058
+ withdrawn: z.boolean().optional()
1059
+ });
1060
+ var cashEstimateJsonSchema = z.object({
1061
+ kind: z.literal("oracle-estimate"),
1062
+ currency: z.string(),
1063
+ amount: bigintString,
1064
+ rate: z.number(),
1065
+ receiveAmount: z.number(),
1066
+ asOf: z.number(),
1067
+ oracleUpdatedAt: z.number().optional(),
1068
+ stale: z.boolean().optional()
1069
+ });
1070
+ var preparedTransactionJsonSchema = z.object({
1071
+ to: z.string(),
1072
+ data: z.string(),
1073
+ value: bigintString,
1074
+ chainId: z.number()
1075
+ });
1076
+ var cashPreparedStepJsonSchema = z.object({
1077
+ kind: z.enum([
1078
+ "approve",
1079
+ "createDeposit",
1080
+ "pruneExpiredIntents",
1081
+ "withdrawDeposit",
1082
+ "removeFunds",
1083
+ "addFunds"
1084
+ ]),
1085
+ description: z.string()
1086
+ });
1087
+ var cashoutResultJsonSchema = z.object({
1088
+ depositId: z.string(),
1089
+ txHash: z.string(),
1090
+ escrowAddress: z.string(),
1091
+ onchainDepositId: bigintString,
1092
+ order: cashOrderJsonSchema
1093
+ });
1094
+ var prepareResultJsonSchema = z.object({
1095
+ txs: z.array(preparedTransactionJsonSchema),
1096
+ steps: z.array(cashPreparedStepJsonSchema),
1097
+ register: z.object({ hashedOnchainIds: z.array(z.string()) })
1098
+ });
1099
+ var withdrawResultJsonSchema = z.object({
1100
+ depositId: z.string(),
1101
+ pruneTxHash: z.string().optional(),
1102
+ withdrawTxHash: z.string()
1103
+ });
1104
+ var topUpResultJsonSchema = z.object({
1105
+ depositId: z.string(),
1106
+ txHash: z.string()
1107
+ });
1108
+ var cashCapabilitiesJsonSchema = z.object({
1109
+ chainId: z.number(),
1110
+ token: z.object({ address: z.string(), symbol: z.literal("USDC"), decimals: z.number() }),
1111
+ environment: z.enum(["production", "preproduction", "staging"]),
1112
+ platforms: z.array(
1113
+ z.object({
1114
+ platform: z.string(),
1115
+ currencies: z.array(z.string()),
1116
+ payeeHint: z.string(),
1117
+ requiresIdentityAttestation: z.boolean()
1118
+ })
1119
+ ),
1120
+ currencies: z.array(z.string()),
1121
+ amount: z.object({ min: bigintString, recommendedMin: bigintString, max: z.null() }),
1122
+ pricing: z.object({ kind: z.literal("oracle-market-rate"), spreadBps: z.literal(0) })
1123
+ });
1124
+ var cashErrorJsonSchema = z.object({
1125
+ code: z.string(),
1126
+ message: z.string(),
1127
+ retryable: z.boolean(),
1128
+ remediation: z.string()
1129
+ });
1130
+
1131
+ // src/codecs/json.ts
1132
+ function omitUndefined(obj) {
1133
+ return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== void 0));
1134
+ }
1135
+ function fillToJson(fill) {
1136
+ return omitUndefined({
1137
+ intentHash: fill.intentHash,
1138
+ status: fill.status,
1139
+ amount: fill.amount.toString(),
1140
+ buyer: fill.buyer,
1141
+ currency: fill.currency,
1142
+ currencyHash: fill.currencyHash,
1143
+ rate: fill.rate,
1144
+ conversionRate: fill.conversionRate?.toString(),
1145
+ fiatOwed: fill.fiatOwed,
1146
+ fiatPaid: fill.fiatPaid,
1147
+ paidCurrency: fill.paidCurrency,
1148
+ paymentId: fill.paymentId,
1149
+ paidAt: fill.paidAt,
1150
+ releasedAmount: fill.releasedAmount?.toString(),
1151
+ fillLatencySeconds: fill.fillLatencySeconds,
1152
+ isExpired: fill.isExpired,
1153
+ signaledAt: fill.signaledAt,
1154
+ expiresAt: fill.expiresAt,
1155
+ fulfilledAt: fill.fulfilledAt,
1156
+ prunedAt: fill.prunedAt
1157
+ });
1158
+ }
1159
+ function fillFromJson(json) {
1160
+ return omitUndefined({
1161
+ ...json,
1162
+ amount: BigInt(json.amount),
1163
+ conversionRate: json.conversionRate !== void 0 ? BigInt(json.conversionRate) : void 0,
1164
+ releasedAmount: json.releasedAmount !== void 0 ? BigInt(json.releasedAmount) : void 0
1165
+ });
1166
+ }
1167
+ function orderToJson(order) {
1168
+ return omitUndefined({
1169
+ depositId: order.depositId,
1170
+ state: order.state,
1171
+ fills: order.fills.map(fillToJson),
1172
+ totalAmount: order.totalAmount.toString(),
1173
+ filledAmount: order.filledAmount.toString(),
1174
+ pendingAmount: order.pendingAmount.toString(),
1175
+ returnedAmount: order.returnedAmount.toString(),
1176
+ nextActions: order.nextActions,
1177
+ primaryIntentHash: order.primaryIntentHash,
1178
+ matchedAt: order.matchedAt,
1179
+ deliveredAt: order.deliveredAt,
1180
+ updatedAt: order.updatedAt,
1181
+ intentCount: order.intentCount,
1182
+ payouts: order.payouts?.map(
1183
+ (p) => omitUndefined({ ...p, pricing: omitUndefined({ ...p.pricing }) })
1184
+ ),
1185
+ successRateBps: order.successRateBps,
1186
+ isInFlight: order.isInFlight,
1187
+ withdrawn: order.withdrawn
1188
+ });
1189
+ }
1190
+ function orderFromJson(json) {
1191
+ const parsed = cashOrderJsonSchema.parse(json);
1192
+ const data = omitUndefined({
1193
+ ...parsed,
1194
+ fills: parsed.fills.map(fillFromJson),
1195
+ totalAmount: BigInt(parsed.totalAmount),
1196
+ filledAmount: BigInt(parsed.filledAmount),
1197
+ pendingAmount: BigInt(parsed.pendingAmount),
1198
+ returnedAmount: BigInt(parsed.returnedAmount)
1199
+ });
1200
+ return withExplain(data);
1201
+ }
1202
+ function estimateToJson(estimate) {
1203
+ return { ...estimate, amount: estimate.amount.toString() };
1204
+ }
1205
+ function estimateFromJson(json) {
1206
+ const parsed = cashEstimateJsonSchema.parse(json);
1207
+ return omitUndefined({
1208
+ ...parsed,
1209
+ currency: parsed.currency,
1210
+ amount: BigInt(parsed.amount)
1211
+ });
1212
+ }
1213
+ function preparedTxToJson(tx) {
1214
+ return { to: tx.to, data: tx.data, value: tx.value.toString(), chainId: tx.chainId };
1215
+ }
1216
+ function preparedTxFromJson(json) {
1217
+ const parsed = preparedTransactionJsonSchema.parse(json);
1218
+ return {
1219
+ to: parsed.to,
1220
+ data: parsed.data,
1221
+ value: BigInt(parsed.value),
1222
+ chainId: parsed.chainId
1223
+ };
1224
+ }
1225
+ function preparedStepToJson(step) {
1226
+ return { kind: step.kind, description: step.description };
1227
+ }
1228
+ function preparedStepFromJson(json) {
1229
+ return cashPreparedStepJsonSchema.parse(json);
1230
+ }
1231
+ function cashoutResultToJson(result) {
1232
+ return {
1233
+ depositId: result.depositId,
1234
+ txHash: result.txHash,
1235
+ escrowAddress: result.escrowAddress,
1236
+ onchainDepositId: result.onchainDepositId.toString(),
1237
+ order: orderToJson(result.order)
1238
+ };
1239
+ }
1240
+ function cashoutResultFromJson(json) {
1241
+ const parsed = cashoutResultJsonSchema.parse(json);
1242
+ return {
1243
+ depositId: parsed.depositId,
1244
+ txHash: parsed.txHash,
1245
+ escrowAddress: parsed.escrowAddress,
1246
+ onchainDepositId: BigInt(parsed.onchainDepositId),
1247
+ order: orderFromJson(parsed.order)
1248
+ };
1249
+ }
1250
+ function prepareResultToJson(result) {
1251
+ return {
1252
+ txs: result.txs.map(preparedTxToJson),
1253
+ steps: result.steps.map(preparedStepToJson),
1254
+ register: result.register
1255
+ };
1256
+ }
1257
+ function prepareResultFromJson(json) {
1258
+ const parsed = prepareResultJsonSchema.parse(json);
1259
+ return {
1260
+ txs: parsed.txs.map(preparedTxFromJson),
1261
+ steps: parsed.steps.map(preparedStepFromJson),
1262
+ register: parsed.register
1263
+ };
1264
+ }
1265
+ function withdrawResultToJson(result) {
1266
+ return omitUndefined({
1267
+ depositId: result.depositId,
1268
+ pruneTxHash: result.pruneTxHash,
1269
+ withdrawTxHash: result.withdrawTxHash
1270
+ });
1271
+ }
1272
+ function withdrawResultFromJson(json) {
1273
+ const parsed = withdrawResultJsonSchema.parse(json);
1274
+ return omitUndefined({
1275
+ depositId: parsed.depositId,
1276
+ pruneTxHash: parsed.pruneTxHash,
1277
+ withdrawTxHash: parsed.withdrawTxHash
1278
+ });
1279
+ }
1280
+ function buyerProfileToJson(profile) {
1281
+ return omitUndefined({ ...profile });
1282
+ }
1283
+ function buyerProfileFromJson(json) {
1284
+ return omitUndefined(cashBuyerProfileJsonSchema.parse(json));
1285
+ }
1286
+ function topUpResultToJson(result) {
1287
+ return { depositId: result.depositId, txHash: result.txHash };
1288
+ }
1289
+ function topUpResultFromJson(json) {
1290
+ const parsed = topUpResultJsonSchema.parse(json);
1291
+ return { depositId: parsed.depositId, txHash: parsed.txHash };
1292
+ }
1293
+ function capabilitiesToJson(caps) {
1294
+ return {
1295
+ ...caps,
1296
+ amount: {
1297
+ min: caps.amount.min.toString(),
1298
+ recommendedMin: caps.amount.recommendedMin.toString(),
1299
+ max: null
1300
+ }
1301
+ };
1302
+ }
1303
+ function capabilitiesFromJson(json) {
1304
+ const parsed = cashCapabilitiesJsonSchema.parse(json);
1305
+ return {
1306
+ ...parsed,
1307
+ platforms: parsed.platforms.map((p) => ({
1308
+ ...p,
1309
+ currencies: p.currencies
1310
+ })),
1311
+ currencies: parsed.currencies,
1312
+ amount: {
1313
+ min: BigInt(parsed.amount.min),
1314
+ recommendedMin: BigInt(parsed.amount.recommendedMin),
1315
+ max: null
1316
+ }
1317
+ };
1318
+ }
1319
+
1320
+ export { CASH_ATTRIBUTION_CODE, MIN_CASHOUT_AMOUNT, RATE_PRECISION, RECOMMENDED_MIN_CASHOUT_AMOUNT, bigintString, buildCapabilities, buildIntentAmountRange, buildMarketRateCurrencyOverride, buyerProfileFromJson, buyerProfileToJson, capabilitiesFromJson, capabilitiesToJson, cashBuyerProfileJsonSchema, cashCapabilitiesJsonSchema, cashErrorJsonSchema, cashEstimateJsonSchema, cashFillJsonSchema, cashNextActionSchema, cashOrderJsonSchema, cashOrderStateSchema, cashPayoutInfoJsonSchema, cashPayoutPricingJsonSchema, cashPreparedStepJsonSchema, cashoutResultFromJson, cashoutResultJsonSchema, cashoutResultToJson, centsToNumber, createCashClient, deriveBuyerProfile, deriveCashOrder, derivePayouts, estimateFromJson, estimateToJson, explainOrder, fiatFromUsdc, fiatToNumber, fillFromJson, fillToJson, formatUsdc, intentStatusSchema, isFillLive, isMarketRateSupported, orderFromJson, orderToJson, parseCompositeDepositId, prepareCashDepositParams, prepareResultFromJson, prepareResultJsonSchema, prepareResultToJson, preparedStepFromJson, preparedStepToJson, preparedTransactionJsonSchema, preparedTxFromJson, preparedTxToJson, rateToNumber, resolveCashDepositId, topUpResultFromJson, topUpResultJsonSchema, topUpResultToJson, usdc, withExplain, withdrawResultFromJson, withdrawResultJsonSchema, withdrawResultToJson };
1321
+ //# sourceMappingURL=index.js.map
1322
+ //# sourceMappingURL=index.js.map