@zkp2p/cash 0.1.7 → 0.1.8

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 CHANGED
@@ -54,6 +54,9 @@ const relayCaps = await cash.capabilities({ includeRelaySources: true });
54
54
  // 2. Estimate - idempotent, cacheable, no side effects. Includes rolling ETA.
55
55
  const est = await cash.estimate({ amount: usdc(500), currency: 'EUR' });
56
56
 
57
+ // Optional: raw demand + speed evidence per offered platform:currency pair.
58
+ const stats = await cash.fillStats();
59
+
57
60
  // 3. Execute.
58
61
  const { depositId } = await cash.cashout(
59
62
  {
@@ -101,9 +104,9 @@ console.log(routed.source?.transactions?.origin, routed.source?.transactions?.de
101
104
  binding rate resolves at the oracle when a buyer fills. Do not display or
102
105
  log it as a locked price.
103
106
  - **Do not invent an ETA.** Use `estimate().eta`: `{ seconds, label }` backed
104
- by rolling 30-day indexer data from zero-spread (`spreadBps: 0`) market-rate
105
- deposits in the same payout corridor, measured from deposit creation to first
106
- fill. Use `order.explain()` for live order state.
107
+ by the same rolling 30-day, intent-attributed pair sample as `fillStats()`,
108
+ measured from deposit creation to first fill. Use `order.explain()` for live
109
+ order state.
107
110
  - **Do not hardcode Relay source assets.** Use Relay SDK-backed EVM
108
111
  `capabilities({ includeRelaySources: true })` and `cashout({ source, ... })`.
109
112
  Destination is always Base USDC. Non-Base source chains require
package/README.md CHANGED
@@ -76,6 +76,7 @@ console.log(source?.transactions?.origin, source?.transactions?.destination);
76
76
  | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
77
77
  | `capabilities()` | Sync discovery: Base USDC destination/default source, platforms × currencies × payee hints × amount bounds |
78
78
  | `capabilities({ includeRelaySources: true })` | Async discovery: adds live Relay SDK EVM source chains/tokens |
79
+ | `fillStats()` | Raw 30-day fill counts and median first-fill time per `platform:currency` pair |
79
80
  | `quoteSource(input)` / `executeSourceQuote(quote, { signer })` | Relay SDK EVM source routing into Base USDC before cashout |
80
81
  | `relayStatus(requestId)` | Relay request status from the Relay SDK request path |
81
82
  | `estimate({ amount, currency })` | Base USDC oracle estimate plus simple recent-fill ETA |
@@ -97,6 +98,11 @@ must execute and confirm its Relay route before preparing the Base-USDC
97
98
  cashout. Every Peer Cash transaction, including approves, carries ERC-8021
98
99
  attribution: `peer-cash` first, your own `referrer` code(s) after it.
99
100
 
101
+ `capabilities()` presents Zelle as one platform. A cashout with
102
+ `receive.platform: 'zelle'` automatically attaches the generic Zelle method
103
+ and its Chase, Bank of America, and Citi buyer routes to the deposit; the payee
104
+ handle and public API remain bank-agnostic.
105
+
100
106
  The default/minimal flow is unchanged: pass Base USDC base units to
101
107
  `estimate()` and `cashout()`. For any other source asset, pass `source` to
102
108
  `cashout()` with a source-chain signer. The SDK settles the Base allowance,
@@ -172,9 +178,12 @@ awaiting-buyer ──────────► matched ───────
172
178
  fills. `estimate()` says "approximately"; nothing in this API pretends to
173
179
  lock a price.
174
180
  - **ETA is historical.** `estimate().eta` is just `{ seconds, label }`, backed
175
- by rolling 30-day indexer data from zero-spread (`spreadBps: 0`) market-rate
176
- deposits in the same payout corridor, measured from deposit creation to first
177
- fulfilled fill.
181
+ by the same rolling 30-day, intent-attributed pair sampler as `fillStats()`,
182
+ measured from deposit creation to the first fulfilled fill through the pair.
183
+ - **Availability thresholds belong to the consumer.** `fillStats()` returns raw
184
+ evidence. A recommended gate is `fills >= 10 && medianFillSeconds <= 48h`.
185
+ Fail open to the full `capabilities()` catalog when stats are unavailable or
186
+ the gate would empty the offered catalog.
178
187
  - **Everything is resumable.** An order is reconstructed from the chain by
179
188
  `depositId` alone. Close the tab, switch devices, crash the process - then
180
189
  call `order(depositId)`.
@@ -357,6 +357,14 @@ interface CashCapabilities {
357
357
  }
358
358
  declare function buildCapabilities(environment: RuntimeEnv): CashCapabilities;
359
359
 
360
+ interface CashPairFillStats {
361
+ /** Fulfilled intents through this pair inside the rolling 30-day window. */
362
+ fills: number;
363
+ /** Median deposit-to-first-fill seconds, sampled once per deposit for this pair. */
364
+ medianFillSeconds?: number;
365
+ }
366
+ /** Raw demand and speed evidence keyed by `basePlatform:currencyCode`. */
367
+ type CashFillStats = Record<string, CashPairFillStats>;
360
368
  interface CashFillEta {
361
369
  /** Simple headline ETA from recent deposits. Undefined when no recent sample exists. */
362
370
  seconds?: number;
@@ -581,6 +589,13 @@ interface CashClient {
581
589
  capabilities(options: {
582
590
  includeRelaySources: true;
583
591
  }): Promise<CashCapabilities>;
592
+ /**
593
+ * 0c - Raw 30-day demand and first-fill speed evidence keyed by
594
+ * `platform:currency`. A recommended consumer gate is `fills >= 10 &&
595
+ * medianFillSeconds <= 48h`; fail open to the full capability catalog when
596
+ * stats are unavailable or the gate would remove every pair.
597
+ */
598
+ fillStats(): Promise<CashFillStats>;
584
599
  /** Relay-only source discovery helper. */
585
600
  sourceCapabilities(): Promise<CashSourceCapabilities>;
586
601
  /** Quote any Relay-supported EVM source asset into Base USDC. */
@@ -640,4 +655,4 @@ interface CashClient {
640
655
  }
641
656
  declare function createCashClient(options: CashClientOptions): CashClient;
642
657
 
643
- export { type CashoutInput as A, type CashoutOptions as B, type CashPayoutInfo as C, type CuratorPayeeDataInput as D, type EstimateInput as E, RECOMMENDED_MIN_CASHOUT_AMOUNT as F, type RelayOptions as G, type RelayQuoteInput as H, type IntentStatus as I, type RelaySourceInput as J, type RelayTransaction as K, type WatchOptions as L, MIN_CASHOUT_AMOUNT as M, type WithdrawOptions as N, type OrdersOptions as O, type PrepareResult as P, buildCapabilities as Q, type RelayExecutionResult as R, type SignerOptions as S, type TopUpResult as T, createCashClient as U, type WithdrawResult as W, type IntentEntity as a, type CashBuyerProfile as b, type CashDepositInput as c, type CreateDepositParamsArg as d, type CashOrder as e, type CashFill as f, type CashCapabilities as g, type CashoutResult as h, type CashEstimate as i, type CashPreparedStep as j, type RelayQuote as k, type RelayStatus as l, type CashSourceCapabilities as m, CASH_ATTRIBUTION_CODE as n, type CashAsset as o, type CashChain as p, type CashClient as q, type CashClientOptions as r, type CashFillEta as s, type CashLeg as t, type CashNextAction as u, type CashOrderState as v, type CashPayout as w, type CashPayoutPricing as x, type CashPlatformCapability as y, type CashPreparedStepKind as z };
658
+ export { type CashPlatformCapability as A, type CashPreparedStepKind as B, type CashPayoutInfo as C, type CashoutInput as D, type CashoutOptions as E, type CuratorPayeeDataInput as F, type EstimateInput as G, RECOMMENDED_MIN_CASHOUT_AMOUNT as H, type IntentStatus as I, type RelayOptions as J, type RelayQuoteInput as K, type RelaySourceInput as L, MIN_CASHOUT_AMOUNT as M, type RelayTransaction as N, type OrdersOptions as O, type PrepareResult as P, type WatchOptions as Q, type RelayExecutionResult as R, type SignerOptions as S, type TopUpResult as T, type WithdrawOptions as U, buildCapabilities as V, type WithdrawResult as W, createCashClient as X, type IntentEntity as a, type CashBuyerProfile as b, type CashDepositInput as c, type CreateDepositParamsArg as d, type CashOrder as e, type CashFill as f, type CashCapabilities as g, type CashoutResult as h, type CashEstimate as i, type CashFillStats as j, type CashPreparedStep as k, type RelayQuote as l, type RelayStatus as m, type CashSourceCapabilities as n, CASH_ATTRIBUTION_CODE as o, type CashAsset as p, type CashChain as q, type CashClient as r, type CashClientOptions as s, type CashFillEta as t, type CashLeg as u, type CashNextAction as v, type CashOrderState as w, type CashPairFillStats as x, type CashPayout as y, type CashPayoutPricing as z };
@@ -357,6 +357,14 @@ interface CashCapabilities {
357
357
  }
358
358
  declare function buildCapabilities(environment: RuntimeEnv): CashCapabilities;
359
359
 
360
+ interface CashPairFillStats {
361
+ /** Fulfilled intents through this pair inside the rolling 30-day window. */
362
+ fills: number;
363
+ /** Median deposit-to-first-fill seconds, sampled once per deposit for this pair. */
364
+ medianFillSeconds?: number;
365
+ }
366
+ /** Raw demand and speed evidence keyed by `basePlatform:currencyCode`. */
367
+ type CashFillStats = Record<string, CashPairFillStats>;
360
368
  interface CashFillEta {
361
369
  /** Simple headline ETA from recent deposits. Undefined when no recent sample exists. */
362
370
  seconds?: number;
@@ -581,6 +589,13 @@ interface CashClient {
581
589
  capabilities(options: {
582
590
  includeRelaySources: true;
583
591
  }): Promise<CashCapabilities>;
592
+ /**
593
+ * 0c - Raw 30-day demand and first-fill speed evidence keyed by
594
+ * `platform:currency`. A recommended consumer gate is `fills >= 10 &&
595
+ * medianFillSeconds <= 48h`; fail open to the full capability catalog when
596
+ * stats are unavailable or the gate would remove every pair.
597
+ */
598
+ fillStats(): Promise<CashFillStats>;
584
599
  /** Relay-only source discovery helper. */
585
600
  sourceCapabilities(): Promise<CashSourceCapabilities>;
586
601
  /** Quote any Relay-supported EVM source asset into Base USDC. */
@@ -640,4 +655,4 @@ interface CashClient {
640
655
  }
641
656
  declare function createCashClient(options: CashClientOptions): CashClient;
642
657
 
643
- export { type CashoutInput as A, type CashoutOptions as B, type CashPayoutInfo as C, type CuratorPayeeDataInput as D, type EstimateInput as E, RECOMMENDED_MIN_CASHOUT_AMOUNT as F, type RelayOptions as G, type RelayQuoteInput as H, type IntentStatus as I, type RelaySourceInput as J, type RelayTransaction as K, type WatchOptions as L, MIN_CASHOUT_AMOUNT as M, type WithdrawOptions as N, type OrdersOptions as O, type PrepareResult as P, buildCapabilities as Q, type RelayExecutionResult as R, type SignerOptions as S, type TopUpResult as T, createCashClient as U, type WithdrawResult as W, type IntentEntity as a, type CashBuyerProfile as b, type CashDepositInput as c, type CreateDepositParamsArg as d, type CashOrder as e, type CashFill as f, type CashCapabilities as g, type CashoutResult as h, type CashEstimate as i, type CashPreparedStep as j, type RelayQuote as k, type RelayStatus as l, type CashSourceCapabilities as m, CASH_ATTRIBUTION_CODE as n, type CashAsset as o, type CashChain as p, type CashClient as q, type CashClientOptions as r, type CashFillEta as s, type CashLeg as t, type CashNextAction as u, type CashOrderState as v, type CashPayout as w, type CashPayoutPricing as x, type CashPlatformCapability as y, type CashPreparedStepKind as z };
658
+ export { type CashPlatformCapability as A, type CashPreparedStepKind as B, type CashPayoutInfo as C, type CashoutInput as D, type CashoutOptions as E, type CuratorPayeeDataInput as F, type EstimateInput as G, RECOMMENDED_MIN_CASHOUT_AMOUNT as H, type IntentStatus as I, type RelayOptions as J, type RelayQuoteInput as K, type RelaySourceInput as L, MIN_CASHOUT_AMOUNT as M, type RelayTransaction as N, type OrdersOptions as O, type PrepareResult as P, type WatchOptions as Q, type RelayExecutionResult as R, type SignerOptions as S, type TopUpResult as T, type WithdrawOptions as U, buildCapabilities as V, type WithdrawResult as W, createCashClient as X, type IntentEntity as a, type CashBuyerProfile as b, type CashDepositInput as c, type CreateDepositParamsArg as d, type CashOrder as e, type CashFill as f, type CashCapabilities as g, type CashoutResult as h, type CashEstimate as i, type CashFillStats as j, type CashPreparedStep as k, type RelayQuote as l, type RelayStatus as m, type CashSourceCapabilities as n, CASH_ATTRIBUTION_CODE as o, type CashAsset as p, type CashChain as q, type CashClient as r, type CashClientOptions as s, type CashFillEta as t, type CashLeg as u, type CashNextAction as v, type CashOrderState as w, type CashPairFillStats as x, type CashPayout as y, type CashPayoutPricing as z };
package/dist/index.cjs CHANGED
@@ -403,6 +403,26 @@ function parseCompositeDepositId(compositeId) {
403
403
  const onchainDepositId = BigInt(rawDepositId);
404
404
  return { escrowAddress: canonicalEscrowAddress, onchainDepositId };
405
405
  }
406
+
407
+ // src/client/platformGroups.ts
408
+ var PLATFORM_METHOD_GROUPS = {
409
+ zelle: ["zelle", "zelle-chase", "zelle-bofa", "zelle-citi"]
410
+ };
411
+ var METHOD_TO_BASE_PLATFORM = new Map(
412
+ Object.entries(PLATFORM_METHOD_GROUPS).flatMap(
413
+ ([platform, methods]) => methods.map((method) => [method, platform])
414
+ )
415
+ );
416
+ function basePlatformForMethod(method) {
417
+ return METHOD_TO_BASE_PLATFORM.get(method) ?? method;
418
+ }
419
+ function paymentMethodsForPlatform(platform, catalog) {
420
+ const configured = PLATFORM_METHOD_GROUPS[platform];
421
+ const methods = configured ?? [platform];
422
+ return methods.filter((method) => catalog[method] !== void 0);
423
+ }
424
+
425
+ // src/client/capabilities.ts
406
426
  var MIN_CASHOUT_AMOUNT = 10000n;
407
427
  var RECOMMENDED_MIN_CASHOUT_AMOUNT = 1000000n;
408
428
  var PAYEE_HINTS = {
@@ -421,13 +441,20 @@ var PAYEE_HINTS = {
421
441
  var IDENTITY_ATTESTATION_PLATFORMS = /* @__PURE__ */ new Set(["wise", "paypal"]);
422
442
  function buildCapabilities(environment) {
423
443
  const catalog = sdk.getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
424
- const platforms = Object.entries(catalog).map(([platform, entry]) => {
444
+ const currenciesByPlatform = /* @__PURE__ */ new Map();
445
+ for (const [method, entry] of Object.entries(catalog)) {
446
+ const platform = basePlatformForMethod(method);
425
447
  const currencies2 = (entry.currencies ?? []).map((hash) => sdk.getCurrencyCodeFromHash(hash)).filter(
426
448
  (code) => code != null && isMarketRateSupported(code)
427
449
  );
450
+ const aggregate = currenciesByPlatform.get(platform) ?? /* @__PURE__ */ new Set();
451
+ for (const currency of currencies2) aggregate.add(currency);
452
+ currenciesByPlatform.set(platform, aggregate);
453
+ }
454
+ const platforms = [...currenciesByPlatform.entries()].map(([platform, currencies2]) => {
428
455
  return {
429
456
  platform,
430
- currencies: [...new Set(currencies2)],
457
+ currencies: [...currencies2].sort(),
431
458
  payeeHint: PAYEE_HINTS[platform] ?? "Your payment handle for this platform",
432
459
  requiresIdentityAttestation: IDENTITY_ATTESTATION_PLATFORMS.has(platform)
433
460
  };
@@ -809,11 +836,8 @@ function mapChainError(verb, err, context = {}) {
809
836
  }
810
837
  return errors.chainCallFailed(verb, err);
811
838
  }
812
- var ETA_WINDOW_DAYS = 30;
813
- var ETA_WINDOW_SECONDS = ETA_WINDOW_DAYS * 24 * 60 * 60;
814
- var ETA_PAGE_LIMIT = 250;
815
- var ETA_MAX_DEPOSIT_SCAN = 2e3;
816
- var FULFILLED = /* @__PURE__ */ new Set(["FULFILLED", "MANUALLY_RELEASED"]);
839
+ var FILL_STATS_WINDOW_SECONDS = 30 * 24 * 60 * 60;
840
+ var FILL_STATS_PAGE_LIMIT = 250;
817
841
  function toUnixSeconds2(value) {
818
842
  if (value === null || value === void 0 || value === "") return void 0;
819
843
  if (value instanceof Date) {
@@ -824,12 +848,22 @@ function toUnixSeconds2(value) {
824
848
  const parsed = Date.parse(value);
825
849
  if (Number.isFinite(parsed)) return Math.floor(parsed / 1e3);
826
850
  }
827
- const n = Number(value);
828
- return Number.isFinite(n) && n > 0 ? n : void 0;
851
+ const numeric = Number(value);
852
+ return Number.isFinite(numeric) && numeric > 0 ? numeric : void 0;
853
+ }
854
+ function normalizeCurrencyCode(value) {
855
+ const raw = value?.trim();
856
+ if (!raw) return void 0;
857
+ if (!raw.toLowerCase().startsWith("0x")) return raw.toUpperCase();
858
+ try {
859
+ return sdk.getCurrencyCodeFromHash(raw)?.toUpperCase();
860
+ } catch {
861
+ return void 0;
862
+ }
829
863
  }
830
864
  function median(values) {
831
865
  if (values.length === 0) return void 0;
832
- const sorted = [...values].sort((a, b) => a - b);
866
+ const sorted = [...values].sort((left, right) => left - right);
833
867
  const mid = Math.floor(sorted.length / 2);
834
868
  return sorted.length % 2 === 1 ? sorted[mid] : Math.round((sorted[mid - 1] + sorted[mid]) / 2);
835
869
  }
@@ -841,47 +875,97 @@ function etaLabel(seconds) {
841
875
  const hours = Math.max(1, Math.round(minutes / 60));
842
876
  return `Usually starts in about ${hours} hr`;
843
877
  }
844
- function matchesPayout(deposit, environment, platform, currency) {
845
- const payouts = derivePayouts(
846
- deposit.paymentMethods ?? [],
847
- deposit.currencies ?? [],
848
- sdk.getPaymentMethodsCatalog(BASE_CHAIN_ID, environment)
849
- );
850
- return payouts.some(
851
- (payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0 && (platform === void 0 || payout.platform === platform) && (currency === void 0 || payout.currency === currency)
852
- );
878
+ function computeFillStatsSample(deposits, nowSeconds, environment) {
879
+ const catalog = sdk.getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
880
+ const windowStart = nowSeconds - FILL_STATS_WINDOW_SECONDS;
881
+ const fillCounts = /* @__PURE__ */ new Map();
882
+ const latenciesByPair = /* @__PURE__ */ new Map();
883
+ const latenciesByCurrency = /* @__PURE__ */ new Map();
884
+ for (const deposit of deposits) {
885
+ const createdAt = toUnixSeconds2(deposit.createdAt ?? deposit.timestamp);
886
+ const firstFillByPair = /* @__PURE__ */ new Map();
887
+ const firstFillByCurrency = /* @__PURE__ */ new Map();
888
+ for (const intent of deposit.intents ?? []) {
889
+ const fulfilledAt = toUnixSeconds2(intent.fulfillTimestamp);
890
+ if (fulfilledAt === void 0 || fulfilledAt < windowStart) continue;
891
+ let method;
892
+ try {
893
+ method = intent.paymentMethodHash ? sdk.resolvePaymentMethodNameFromHash(intent.paymentMethodHash, catalog) : void 0;
894
+ } catch {
895
+ method = void 0;
896
+ }
897
+ const currency = normalizeCurrencyCode(intent.fiatCurrency);
898
+ if (!method || !currency) continue;
899
+ const pair = `${basePlatformForMethod(method)}:${currency}`;
900
+ fillCounts.set(pair, (fillCounts.get(pair) ?? 0) + 1);
901
+ if (createdAt === void 0 || createdAt < windowStart || fulfilledAt < createdAt) continue;
902
+ const previousPairFill = firstFillByPair.get(pair);
903
+ if (previousPairFill === void 0 || fulfilledAt < previousPairFill) {
904
+ firstFillByPair.set(pair, fulfilledAt);
905
+ }
906
+ const previousCurrencyFill = firstFillByCurrency.get(currency);
907
+ if (previousCurrencyFill === void 0 || fulfilledAt < previousCurrencyFill) {
908
+ firstFillByCurrency.set(currency, fulfilledAt);
909
+ }
910
+ }
911
+ if (createdAt === void 0) continue;
912
+ for (const [pair, firstFill] of firstFillByPair) {
913
+ const latencies = latenciesByPair.get(pair) ?? [];
914
+ latencies.push(firstFill - createdAt);
915
+ latenciesByPair.set(pair, latencies);
916
+ }
917
+ for (const [currency, firstFill] of firstFillByCurrency) {
918
+ const latencies = latenciesByCurrency.get(currency) ?? [];
919
+ latencies.push(firstFill - createdAt);
920
+ latenciesByCurrency.set(currency, latencies);
921
+ }
922
+ }
923
+ const stats = {};
924
+ for (const [pair, fills] of fillCounts) {
925
+ const medianFillSeconds = median(latenciesByPair.get(pair) ?? []);
926
+ stats[pair] = {
927
+ fills,
928
+ ...medianFillSeconds !== void 0 ? { medianFillSeconds } : {}
929
+ };
930
+ }
931
+ const medianFillSecondsByCurrency = /* @__PURE__ */ new Map();
932
+ for (const [currency, latencies] of latenciesByCurrency) {
933
+ const value = median(latencies);
934
+ if (value !== void 0) medianFillSecondsByCurrency.set(currency, value);
935
+ }
936
+ return { stats, medianFillSecondsByCurrency };
853
937
  }
854
- async function readFillEta(client, input) {
938
+ async function readFillStatsSample(client, environment) {
855
939
  const now = Math.floor(Date.now() / 1e3);
856
- const windowStart = now - ETA_WINDOW_SECONDS;
940
+ const windowStart = now - FILL_STATS_WINDOW_SECONDS;
857
941
  const deposits = [];
858
- for (let offset = 0; offset < ETA_MAX_DEPOSIT_SCAN; offset += ETA_PAGE_LIMIT) {
942
+ for (let offset = 0; ; offset += FILL_STATS_PAGE_LIMIT) {
859
943
  const page = await client.indexer.getDepositsWithRelations(
860
944
  { chainId: BASE_CHAIN_ID },
861
- { limit: ETA_PAGE_LIMIT, offset, orderBy: "timestamp", orderDirection: "desc" },
945
+ {
946
+ limit: FILL_STATS_PAGE_LIMIT,
947
+ offset,
948
+ orderBy: "updatedAt",
949
+ orderDirection: "desc"
950
+ },
862
951
  { includeIntents: true, intentStatuses: ["FULFILLED", "MANUALLY_RELEASED"] }
863
952
  );
864
953
  deposits.push(...page);
865
- if (page.length < ETA_PAGE_LIMIT) break;
866
- const oldestCreatedAt = Math.min(
867
- ...page.map((deposit) => toUnixSeconds2(deposit.createdAt ?? deposit.timestamp) ?? Infinity)
954
+ if (page.length < FILL_STATS_PAGE_LIMIT) break;
955
+ const oldestUpdatedAt = Math.min(
956
+ ...page.map((deposit) => toUnixSeconds2(deposit.updatedAt) ?? Infinity)
868
957
  );
869
- if (oldestCreatedAt < windowStart) break;
958
+ if (oldestUpdatedAt < windowStart) break;
870
959
  }
871
- const firstFillLatencies = [];
872
- for (const deposit of deposits) {
873
- const createdAt = toUnixSeconds2(deposit.createdAt ?? deposit.timestamp);
874
- if (createdAt === void 0 || createdAt < windowStart) continue;
875
- if (!matchesPayout(deposit, input.environment, input.platform, input.currency)) continue;
876
- const fulfilled = (deposit.intents ?? []).filter((intent) => intent.status != null && FULFILLED.has(intent.status)).map((intent) => ({
877
- fulfilledAt: toUnixSeconds2(intent.fulfillTimestamp)
878
- })).filter(
879
- (intent) => intent.fulfilledAt !== void 0 && intent.fulfilledAt >= createdAt
880
- ).sort((a, b) => a.fulfilledAt - b.fulfilledAt);
881
- if (fulfilled.length === 0) continue;
882
- firstFillLatencies.push(fulfilled[0].fulfilledAt - createdAt);
883
- }
884
- const seconds = median(firstFillLatencies);
960
+ return computeFillStatsSample(deposits, now, environment);
961
+ }
962
+ async function readFillStats(client, environment) {
963
+ return (await readFillStatsSample(client, environment)).stats;
964
+ }
965
+ async function readFillEta(client, input) {
966
+ const sample = await readFillStatsSample(client, input.environment);
967
+ const currency = input.currency.toUpperCase();
968
+ const seconds = input.platform ? sample.stats[`${basePlatformForMethod(input.platform)}:${currency}`]?.medianFillSeconds : sample.medianFillSecondsByCurrency.get(currency);
885
969
  return {
886
970
  ...seconds !== void 0 ? { seconds } : {},
887
971
  label: etaLabel(seconds)
@@ -1549,6 +1633,7 @@ function createCashClient(options) {
1549
1633
  }
1550
1634
  function validatePayout(input) {
1551
1635
  const { receive } = input;
1636
+ const catalog = sdk.getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
1552
1637
  const platform = buildCapabilities(environment).platforms.find(
1553
1638
  (capability) => capability.platform === receive.platform
1554
1639
  );
@@ -1559,14 +1644,13 @@ function createCashClient(options) {
1559
1644
  if (!platform.currencies.includes(receive.currency)) {
1560
1645
  throw errors.unsupportedPlatformCurrency(receive.platform, receive.currency);
1561
1646
  }
1647
+ const paymentMethods = paymentMethodsForPlatform(receive.platform, catalog);
1562
1648
  return {
1563
- payouts: [
1564
- {
1565
- processorName: receive.platform,
1566
- currency: receive.currency,
1567
- payeeData: receive.payee
1568
- }
1569
- ]
1649
+ payouts: paymentMethods.map((processorName) => ({
1650
+ processorName,
1651
+ currency: receive.currency,
1652
+ payeeData: receive.payee
1653
+ }))
1570
1654
  };
1571
1655
  }
1572
1656
  function validateDepositInput(amount, input, payoutInput = validatePayout(input)) {
@@ -1844,6 +1928,13 @@ function createCashClient(options) {
1844
1928
  ...options.relay ? { relay: options.relay } : {}
1845
1929
  });
1846
1930
  },
1931
+ async fillStats() {
1932
+ try {
1933
+ return await readFillStats(readClient, environment);
1934
+ } catch (err) {
1935
+ throw errors.indexerUnavailable("fill stats", err);
1936
+ }
1937
+ },
1847
1938
  async cashout(input, opts) {
1848
1939
  const client = await signingClient("cashout", opts);
1849
1940
  const owner = opts.signer.account.address;
@@ -2443,6 +2534,11 @@ var cashEstimateJsonSchema = zod.z.object({
2443
2534
  label: zod.z.string()
2444
2535
  }).optional()
2445
2536
  });
2537
+ var cashPairFillStatsJsonSchema = zod.z.object({
2538
+ fills: zod.z.number().int().nonnegative(),
2539
+ medianFillSeconds: zod.z.number().int().nonnegative().optional()
2540
+ }).strict();
2541
+ var cashFillStatsJsonSchema = zod.z.record(zod.z.string(), cashPairFillStatsJsonSchema);
2446
2542
  var preparedTransactionJsonSchema = zod.z.object({
2447
2543
  to: zod.z.string(),
2448
2544
  data: zod.z.string(),
@@ -2747,6 +2843,21 @@ function estimateFromJson(json) {
2747
2843
  } : void 0
2748
2844
  });
2749
2845
  }
2846
+ function fillStatsToJson(stats) {
2847
+ return cashFillStatsJsonSchema.parse(stats);
2848
+ }
2849
+ function fillStatsFromJson(json) {
2850
+ const parsed = cashFillStatsJsonSchema.parse(json);
2851
+ return Object.fromEntries(
2852
+ Object.entries(parsed).map(([pair, stats]) => [
2853
+ pair,
2854
+ {
2855
+ fills: stats.fills,
2856
+ ...stats.medianFillSeconds !== void 0 ? { medianFillSeconds: stats.medianFillSeconds } : {}
2857
+ }
2858
+ ])
2859
+ );
2860
+ }
2750
2861
  function cashAssetFromJson(asset) {
2751
2862
  return {
2752
2863
  chainId: asset.chainId,
@@ -3055,9 +3166,11 @@ exports.cashErrorRecoveryJsonSchema = cashErrorRecoveryJsonSchema;
3055
3166
  exports.cashErrorToJson = cashErrorToJson;
3056
3167
  exports.cashEstimateJsonSchema = cashEstimateJsonSchema;
3057
3168
  exports.cashFillJsonSchema = cashFillJsonSchema;
3169
+ exports.cashFillStatsJsonSchema = cashFillStatsJsonSchema;
3058
3170
  exports.cashNextActionSchema = cashNextActionSchema;
3059
3171
  exports.cashOrderJsonSchema = cashOrderJsonSchema;
3060
3172
  exports.cashOrderStateSchema = cashOrderStateSchema;
3173
+ exports.cashPairFillStatsJsonSchema = cashPairFillStatsJsonSchema;
3061
3174
  exports.cashPayoutInfoJsonSchema = cashPayoutInfoJsonSchema;
3062
3175
  exports.cashPayoutPricingJsonSchema = cashPayoutPricingJsonSchema;
3063
3176
  exports.cashPreparedStepJsonSchema = cashPreparedStepJsonSchema;
@@ -3077,6 +3190,8 @@ exports.explainOrder = explainOrder;
3077
3190
  exports.fiatFromUsdc = fiatFromUsdc;
3078
3191
  exports.fiatToNumber = fiatToNumber;
3079
3192
  exports.fillFromJson = fillFromJson;
3193
+ exports.fillStatsFromJson = fillStatsFromJson;
3194
+ exports.fillStatsToJson = fillStatsToJson;
3080
3195
  exports.fillToJson = fillToJson;
3081
3196
  exports.formatUsdc = formatUsdc;
3082
3197
  exports.intentStatusSchema = intentStatusSchema;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as IntentStatus, C as CashPayoutInfo, a as IntentEntity, b as CashBuyerProfile, c as CashDepositInput, d as CreateDepositParamsArg, e as CashOrder, f as CashFill, g as CashCapabilities, h as CashoutResult, i as CashEstimate, P as PrepareResult, j as CashPreparedStep, R as RelayExecutionResult, k as RelayQuote, l as RelayStatus, m as CashSourceCapabilities, T as TopUpResult, W as WithdrawResult } from './createCashClient-jUA_GNdh.cjs';
2
- export { n as CASH_ATTRIBUTION_CODE, o as CashAsset, p as CashChain, q as CashClient, r as CashClientOptions, s as CashFillEta, t as CashLeg, u as CashNextAction, v as CashOrderState, w as CashPayout, x as CashPayoutPricing, y as CashPlatformCapability, z as CashPreparedStepKind, A as CashoutInput, B as CashoutOptions, D as CuratorPayeeDataInput, E as EstimateInput, M as MIN_CASHOUT_AMOUNT, O as OrdersOptions, F as RECOMMENDED_MIN_CASHOUT_AMOUNT, G as RelayOptions, H as RelayQuoteInput, J as RelaySourceInput, K as RelayTransaction, S as SignerOptions, L as WatchOptions, N as WithdrawOptions, Q as buildCapabilities, U as createCashClient } from './createCashClient-jUA_GNdh.cjs';
1
+ import { I as IntentStatus, C as CashPayoutInfo, a as IntentEntity, b as CashBuyerProfile, c as CashDepositInput, d as CreateDepositParamsArg, e as CashOrder, f as CashFill, g as CashCapabilities, h as CashoutResult, i as CashEstimate, j as CashFillStats, P as PrepareResult, k as CashPreparedStep, R as RelayExecutionResult, l as RelayQuote, m as RelayStatus, n as CashSourceCapabilities, T as TopUpResult, W as WithdrawResult } from './createCashClient-BIzOLHjF.cjs';
2
+ export { o as CASH_ATTRIBUTION_CODE, p as CashAsset, q as CashChain, r as CashClient, s as CashClientOptions, t as CashFillEta, u as CashLeg, v as CashNextAction, w as CashOrderState, x as CashPairFillStats, y as CashPayout, z as CashPayoutPricing, A as CashPlatformCapability, B as CashPreparedStepKind, D as CashoutInput, E as CashoutOptions, F as CuratorPayeeDataInput, G as EstimateInput, M as MIN_CASHOUT_AMOUNT, O as OrdersOptions, H as RECOMMENDED_MIN_CASHOUT_AMOUNT, J as RelayOptions, K as RelayQuoteInput, L as RelaySourceInput, N as RelayTransaction, S as SignerOptions, Q as WatchOptions, U as WithdrawOptions, V as buildCapabilities, X as createCashClient } from './createCashClient-BIzOLHjF.cjs';
3
3
  import { PaymentMethodCatalog, CurrencyType, OracleAdapterOverrides, OnchainCurrency, Zkp2pClient, PreparedTransaction } from '@zkp2p/sdk';
4
4
  export { CurrencyType, PreparedTransaction, RuntimeEnv } from '@zkp2p/sdk';
5
5
  import { Log, Abi } from 'viem';
@@ -1432,6 +1432,26 @@ declare const cashEstimateJsonSchema: z.ZodObject<{
1432
1432
  seconds?: number | undefined;
1433
1433
  } | undefined;
1434
1434
  }>;
1435
+ declare const cashPairFillStatsJsonSchema: z.ZodObject<{
1436
+ fills: z.ZodNumber;
1437
+ medianFillSeconds: z.ZodOptional<z.ZodNumber>;
1438
+ }, "strict", z.ZodTypeAny, {
1439
+ fills: number;
1440
+ medianFillSeconds?: number | undefined;
1441
+ }, {
1442
+ fills: number;
1443
+ medianFillSeconds?: number | undefined;
1444
+ }>;
1445
+ declare const cashFillStatsJsonSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
1446
+ fills: z.ZodNumber;
1447
+ medianFillSeconds: z.ZodOptional<z.ZodNumber>;
1448
+ }, "strict", z.ZodTypeAny, {
1449
+ fills: number;
1450
+ medianFillSeconds?: number | undefined;
1451
+ }, {
1452
+ fills: number;
1453
+ medianFillSeconds?: number | undefined;
1454
+ }>>;
1435
1455
  declare const preparedTransactionJsonSchema: z.ZodObject<{
1436
1456
  to: z.ZodString;
1437
1457
  data: z.ZodString;
@@ -2688,8 +2708,8 @@ declare const cashCapabilitiesJsonSchema: z.ZodObject<{
2688
2708
  decimals: number;
2689
2709
  };
2690
2710
  };
2691
- environment: "production" | "preproduction" | "staging";
2692
2711
  currencies: string[];
2712
+ environment: "production" | "preproduction" | "staging";
2693
2713
  platforms: {
2694
2714
  platform: string;
2695
2715
  currencies: string[];
@@ -2759,8 +2779,8 @@ declare const cashCapabilitiesJsonSchema: z.ZodObject<{
2759
2779
  decimals: number;
2760
2780
  };
2761
2781
  };
2762
- environment: "production" | "preproduction" | "staging";
2763
2782
  currencies: string[];
2783
+ environment: "production" | "preproduction" | "staging";
2764
2784
  platforms: {
2765
2785
  platform: string;
2766
2786
  currencies: string[];
@@ -3707,6 +3727,7 @@ declare const cashErrorJsonSchema: z.ZodObject<{
3707
3727
  type CashOrderJson = z.infer<typeof cashOrderJsonSchema>;
3708
3728
  type CashFillJson = z.infer<typeof cashFillJsonSchema>;
3709
3729
  type CashEstimateJson = z.infer<typeof cashEstimateJsonSchema>;
3730
+ type CashFillStatsJson = z.infer<typeof cashFillStatsJsonSchema>;
3710
3731
  type PreparedTransactionJson = z.infer<typeof preparedTransactionJsonSchema>;
3711
3732
  type CashoutResultJson = z.infer<typeof cashoutResultJsonSchema>;
3712
3733
  type PrepareResultJson = z.infer<typeof prepareResultJsonSchema>;
@@ -3739,6 +3760,8 @@ declare function orderToJson(order: CashOrder): CashOrderJson;
3739
3760
  declare function orderFromJson(json: unknown): CashOrder;
3740
3761
  declare function estimateToJson(estimate: CashEstimate): CashEstimateJson;
3741
3762
  declare function estimateFromJson(json: unknown): CashEstimate;
3763
+ declare function fillStatsToJson(stats: CashFillStats): CashFillStatsJson;
3764
+ declare function fillStatsFromJson(json: unknown): CashFillStats;
3742
3765
  declare function relayQuoteToJson(quote: RelayQuote): RelayQuoteJson;
3743
3766
  declare function relayQuoteFromJson(json: unknown): RelayQuote;
3744
3767
  declare function sourceCapabilitiesToJson(capabilities: CashSourceCapabilities): CashSourceCapabilitiesJson;
@@ -3766,4 +3789,4 @@ declare function capabilitiesFromJson(json: unknown): CashCapabilities;
3766
3789
  declare function cashErrorToJson(error: CashErrorShape): CashErrorJson;
3767
3790
  declare function cashErrorFromJson(json: unknown): CashError;
3768
3791
 
3769
- export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RETAIN_ON_EMPTY, type CashAssetJson, CashBuyerProfile, type CashBuyerProfileJson, CashCapabilities, type CashCapabilitiesJson, type CashChainJson, CashDepositInput, CashError, type CashErrorCode, type CashErrorJson, type CashErrorRecovery, type CashErrorRecoveryJson, type CashErrorShape, CashEstimate, type CashEstimateJson, CashFill, type CashFillJson, CashOrder, type CashOrderData, type CashOrderJson, CashPayoutInfo, type CashPayoutInfoJson, CashPreparedStep, type CashPreparedStepJson, CashSourceCapabilities, type CashSourceCapabilitiesJson, CashoutResult, type CashoutResultJson, type DeriveCashOrderOptions, MARKET_SPREAD_BPS, type MethodCurrencyLike, ORACLE_MIN_CONVERSION_RATE_SENTINEL, type PaymentMethodLike, PrepareResult, type PrepareResultJson, type PreparedTransactionJson, RATE_PRECISION, RelayExecutionResult, type RelayExecutionResultJson, RelayQuote, type RelayQuoteJson, RelayStatus, type RelayStatusJson, type RelayTransactionJson, type RelayTransactionsJson, type ResolvedCashDeposit, TopUpResult, type TopUpResultJson, USDC_DECIMALS, WithdrawResult, type WithdrawResultJson, bigintString, buildIntentAmountRange, buildMarketRateCurrencyOverride, buyerProfileFromJson, buyerProfileToJson, capabilitiesFromJson, capabilitiesToJson, cashAssetJsonSchema, cashBuyerProfileJsonSchema, cashCapabilitiesJsonSchema, cashChainJsonSchema, cashErrorFromJson, cashErrorJsonSchema, cashErrorRecoveryJsonSchema, cashErrorToJson, cashEstimateJsonSchema, cashFillJsonSchema, cashNextActionSchema, cashOrderJsonSchema, cashOrderStateSchema, cashPayoutInfoJsonSchema, cashPayoutPricingJsonSchema, cashPreparedStepJsonSchema, cashSourceCapabilitiesJsonSchema, cashoutResultFromJson, cashoutResultJsonSchema, cashoutResultToJson, centsToNumber, deriveBuyerProfile, deriveCashOrder, derivePayouts, errors, estimateFromJson, estimateToJson, explainOrder, fiatFromUsdc, fiatToNumber, fillFromJson, fillToJson, formatUsdc, intentStatusSchema, isCashError, isFillLive, isMarketRateSupported, nonNegativeBigintString, orderFromJson, orderToJson, parseCompositeDepositId, prepareCashDepositParams, prepareResultFromJson, prepareResultJsonSchema, prepareResultToJson, preparedStepFromJson, preparedStepToJson, preparedTransactionJsonSchema, preparedTxFromJson, preparedTxToJson, rateToNumber, relayExecutionResultFromJson, relayExecutionResultJsonSchema, relayExecutionResultToJson, relayQuoteFromJson, relayQuoteJsonSchema, relayQuoteToJson, relayStatusFromJson, relayStatusJsonSchema, relayStatusToJson, relayTransactionJsonSchema, relayTransactionsJsonSchema, resolveCashDepositId, sourceCapabilitiesFromJson, sourceCapabilitiesToJson, topUpResultFromJson, topUpResultJsonSchema, topUpResultToJson, usdc, withExplain, withdrawResultFromJson, withdrawResultJsonSchema, withdrawResultToJson };
3792
+ export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RETAIN_ON_EMPTY, type CashAssetJson, CashBuyerProfile, type CashBuyerProfileJson, CashCapabilities, type CashCapabilitiesJson, type CashChainJson, CashDepositInput, CashError, type CashErrorCode, type CashErrorJson, type CashErrorRecovery, type CashErrorRecoveryJson, type CashErrorShape, CashEstimate, type CashEstimateJson, CashFill, type CashFillJson, CashFillStats, type CashFillStatsJson, CashOrder, type CashOrderData, type CashOrderJson, CashPayoutInfo, type CashPayoutInfoJson, CashPreparedStep, type CashPreparedStepJson, CashSourceCapabilities, type CashSourceCapabilitiesJson, CashoutResult, type CashoutResultJson, type DeriveCashOrderOptions, MARKET_SPREAD_BPS, type MethodCurrencyLike, ORACLE_MIN_CONVERSION_RATE_SENTINEL, type PaymentMethodLike, PrepareResult, type PrepareResultJson, type PreparedTransactionJson, RATE_PRECISION, RelayExecutionResult, type RelayExecutionResultJson, RelayQuote, type RelayQuoteJson, RelayStatus, type RelayStatusJson, type RelayTransactionJson, type RelayTransactionsJson, type ResolvedCashDeposit, TopUpResult, type TopUpResultJson, USDC_DECIMALS, WithdrawResult, type WithdrawResultJson, bigintString, buildIntentAmountRange, buildMarketRateCurrencyOverride, buyerProfileFromJson, buyerProfileToJson, capabilitiesFromJson, capabilitiesToJson, cashAssetJsonSchema, cashBuyerProfileJsonSchema, cashCapabilitiesJsonSchema, cashChainJsonSchema, cashErrorFromJson, cashErrorJsonSchema, cashErrorRecoveryJsonSchema, cashErrorToJson, cashEstimateJsonSchema, cashFillJsonSchema, cashFillStatsJsonSchema, cashNextActionSchema, cashOrderJsonSchema, cashOrderStateSchema, cashPairFillStatsJsonSchema, cashPayoutInfoJsonSchema, cashPayoutPricingJsonSchema, cashPreparedStepJsonSchema, cashSourceCapabilitiesJsonSchema, cashoutResultFromJson, cashoutResultJsonSchema, cashoutResultToJson, centsToNumber, deriveBuyerProfile, deriveCashOrder, derivePayouts, errors, estimateFromJson, estimateToJson, explainOrder, fiatFromUsdc, fiatToNumber, fillFromJson, fillStatsFromJson, fillStatsToJson, fillToJson, formatUsdc, intentStatusSchema, isCashError, isFillLive, isMarketRateSupported, nonNegativeBigintString, orderFromJson, orderToJson, parseCompositeDepositId, prepareCashDepositParams, prepareResultFromJson, prepareResultJsonSchema, prepareResultToJson, preparedStepFromJson, preparedStepToJson, preparedTransactionJsonSchema, preparedTxFromJson, preparedTxToJson, rateToNumber, relayExecutionResultFromJson, relayExecutionResultJsonSchema, relayExecutionResultToJson, relayQuoteFromJson, relayQuoteJsonSchema, relayQuoteToJson, relayStatusFromJson, relayStatusJsonSchema, relayStatusToJson, relayTransactionJsonSchema, relayTransactionsJsonSchema, resolveCashDepositId, sourceCapabilitiesFromJson, sourceCapabilitiesToJson, topUpResultFromJson, topUpResultJsonSchema, topUpResultToJson, usdc, withExplain, withdrawResultFromJson, withdrawResultJsonSchema, withdrawResultToJson };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { I as IntentStatus, C as CashPayoutInfo, a as IntentEntity, b as CashBuyerProfile, c as CashDepositInput, d as CreateDepositParamsArg, e as CashOrder, f as CashFill, g as CashCapabilities, h as CashoutResult, i as CashEstimate, P as PrepareResult, j as CashPreparedStep, R as RelayExecutionResult, k as RelayQuote, l as RelayStatus, m as CashSourceCapabilities, T as TopUpResult, W as WithdrawResult } from './createCashClient-jUA_GNdh.js';
2
- export { n as CASH_ATTRIBUTION_CODE, o as CashAsset, p as CashChain, q as CashClient, r as CashClientOptions, s as CashFillEta, t as CashLeg, u as CashNextAction, v as CashOrderState, w as CashPayout, x as CashPayoutPricing, y as CashPlatformCapability, z as CashPreparedStepKind, A as CashoutInput, B as CashoutOptions, D as CuratorPayeeDataInput, E as EstimateInput, M as MIN_CASHOUT_AMOUNT, O as OrdersOptions, F as RECOMMENDED_MIN_CASHOUT_AMOUNT, G as RelayOptions, H as RelayQuoteInput, J as RelaySourceInput, K as RelayTransaction, S as SignerOptions, L as WatchOptions, N as WithdrawOptions, Q as buildCapabilities, U as createCashClient } from './createCashClient-jUA_GNdh.js';
1
+ import { I as IntentStatus, C as CashPayoutInfo, a as IntentEntity, b as CashBuyerProfile, c as CashDepositInput, d as CreateDepositParamsArg, e as CashOrder, f as CashFill, g as CashCapabilities, h as CashoutResult, i as CashEstimate, j as CashFillStats, P as PrepareResult, k as CashPreparedStep, R as RelayExecutionResult, l as RelayQuote, m as RelayStatus, n as CashSourceCapabilities, T as TopUpResult, W as WithdrawResult } from './createCashClient-BIzOLHjF.js';
2
+ export { o as CASH_ATTRIBUTION_CODE, p as CashAsset, q as CashChain, r as CashClient, s as CashClientOptions, t as CashFillEta, u as CashLeg, v as CashNextAction, w as CashOrderState, x as CashPairFillStats, y as CashPayout, z as CashPayoutPricing, A as CashPlatformCapability, B as CashPreparedStepKind, D as CashoutInput, E as CashoutOptions, F as CuratorPayeeDataInput, G as EstimateInput, M as MIN_CASHOUT_AMOUNT, O as OrdersOptions, H as RECOMMENDED_MIN_CASHOUT_AMOUNT, J as RelayOptions, K as RelayQuoteInput, L as RelaySourceInput, N as RelayTransaction, S as SignerOptions, Q as WatchOptions, U as WithdrawOptions, V as buildCapabilities, X as createCashClient } from './createCashClient-BIzOLHjF.js';
3
3
  import { PaymentMethodCatalog, CurrencyType, OracleAdapterOverrides, OnchainCurrency, Zkp2pClient, PreparedTransaction } from '@zkp2p/sdk';
4
4
  export { CurrencyType, PreparedTransaction, RuntimeEnv } from '@zkp2p/sdk';
5
5
  import { Log, Abi } from 'viem';
@@ -1432,6 +1432,26 @@ declare const cashEstimateJsonSchema: z.ZodObject<{
1432
1432
  seconds?: number | undefined;
1433
1433
  } | undefined;
1434
1434
  }>;
1435
+ declare const cashPairFillStatsJsonSchema: z.ZodObject<{
1436
+ fills: z.ZodNumber;
1437
+ medianFillSeconds: z.ZodOptional<z.ZodNumber>;
1438
+ }, "strict", z.ZodTypeAny, {
1439
+ fills: number;
1440
+ medianFillSeconds?: number | undefined;
1441
+ }, {
1442
+ fills: number;
1443
+ medianFillSeconds?: number | undefined;
1444
+ }>;
1445
+ declare const cashFillStatsJsonSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
1446
+ fills: z.ZodNumber;
1447
+ medianFillSeconds: z.ZodOptional<z.ZodNumber>;
1448
+ }, "strict", z.ZodTypeAny, {
1449
+ fills: number;
1450
+ medianFillSeconds?: number | undefined;
1451
+ }, {
1452
+ fills: number;
1453
+ medianFillSeconds?: number | undefined;
1454
+ }>>;
1435
1455
  declare const preparedTransactionJsonSchema: z.ZodObject<{
1436
1456
  to: z.ZodString;
1437
1457
  data: z.ZodString;
@@ -2688,8 +2708,8 @@ declare const cashCapabilitiesJsonSchema: z.ZodObject<{
2688
2708
  decimals: number;
2689
2709
  };
2690
2710
  };
2691
- environment: "production" | "preproduction" | "staging";
2692
2711
  currencies: string[];
2712
+ environment: "production" | "preproduction" | "staging";
2693
2713
  platforms: {
2694
2714
  platform: string;
2695
2715
  currencies: string[];
@@ -2759,8 +2779,8 @@ declare const cashCapabilitiesJsonSchema: z.ZodObject<{
2759
2779
  decimals: number;
2760
2780
  };
2761
2781
  };
2762
- environment: "production" | "preproduction" | "staging";
2763
2782
  currencies: string[];
2783
+ environment: "production" | "preproduction" | "staging";
2764
2784
  platforms: {
2765
2785
  platform: string;
2766
2786
  currencies: string[];
@@ -3707,6 +3727,7 @@ declare const cashErrorJsonSchema: z.ZodObject<{
3707
3727
  type CashOrderJson = z.infer<typeof cashOrderJsonSchema>;
3708
3728
  type CashFillJson = z.infer<typeof cashFillJsonSchema>;
3709
3729
  type CashEstimateJson = z.infer<typeof cashEstimateJsonSchema>;
3730
+ type CashFillStatsJson = z.infer<typeof cashFillStatsJsonSchema>;
3710
3731
  type PreparedTransactionJson = z.infer<typeof preparedTransactionJsonSchema>;
3711
3732
  type CashoutResultJson = z.infer<typeof cashoutResultJsonSchema>;
3712
3733
  type PrepareResultJson = z.infer<typeof prepareResultJsonSchema>;
@@ -3739,6 +3760,8 @@ declare function orderToJson(order: CashOrder): CashOrderJson;
3739
3760
  declare function orderFromJson(json: unknown): CashOrder;
3740
3761
  declare function estimateToJson(estimate: CashEstimate): CashEstimateJson;
3741
3762
  declare function estimateFromJson(json: unknown): CashEstimate;
3763
+ declare function fillStatsToJson(stats: CashFillStats): CashFillStatsJson;
3764
+ declare function fillStatsFromJson(json: unknown): CashFillStats;
3742
3765
  declare function relayQuoteToJson(quote: RelayQuote): RelayQuoteJson;
3743
3766
  declare function relayQuoteFromJson(json: unknown): RelayQuote;
3744
3767
  declare function sourceCapabilitiesToJson(capabilities: CashSourceCapabilities): CashSourceCapabilitiesJson;
@@ -3766,4 +3789,4 @@ declare function capabilitiesFromJson(json: unknown): CashCapabilities;
3766
3789
  declare function cashErrorToJson(error: CashErrorShape): CashErrorJson;
3767
3790
  declare function cashErrorFromJson(json: unknown): CashError;
3768
3791
 
3769
- export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RETAIN_ON_EMPTY, type CashAssetJson, CashBuyerProfile, type CashBuyerProfileJson, CashCapabilities, type CashCapabilitiesJson, type CashChainJson, CashDepositInput, CashError, type CashErrorCode, type CashErrorJson, type CashErrorRecovery, type CashErrorRecoveryJson, type CashErrorShape, CashEstimate, type CashEstimateJson, CashFill, type CashFillJson, CashOrder, type CashOrderData, type CashOrderJson, CashPayoutInfo, type CashPayoutInfoJson, CashPreparedStep, type CashPreparedStepJson, CashSourceCapabilities, type CashSourceCapabilitiesJson, CashoutResult, type CashoutResultJson, type DeriveCashOrderOptions, MARKET_SPREAD_BPS, type MethodCurrencyLike, ORACLE_MIN_CONVERSION_RATE_SENTINEL, type PaymentMethodLike, PrepareResult, type PrepareResultJson, type PreparedTransactionJson, RATE_PRECISION, RelayExecutionResult, type RelayExecutionResultJson, RelayQuote, type RelayQuoteJson, RelayStatus, type RelayStatusJson, type RelayTransactionJson, type RelayTransactionsJson, type ResolvedCashDeposit, TopUpResult, type TopUpResultJson, USDC_DECIMALS, WithdrawResult, type WithdrawResultJson, bigintString, buildIntentAmountRange, buildMarketRateCurrencyOverride, buyerProfileFromJson, buyerProfileToJson, capabilitiesFromJson, capabilitiesToJson, cashAssetJsonSchema, cashBuyerProfileJsonSchema, cashCapabilitiesJsonSchema, cashChainJsonSchema, cashErrorFromJson, cashErrorJsonSchema, cashErrorRecoveryJsonSchema, cashErrorToJson, cashEstimateJsonSchema, cashFillJsonSchema, cashNextActionSchema, cashOrderJsonSchema, cashOrderStateSchema, cashPayoutInfoJsonSchema, cashPayoutPricingJsonSchema, cashPreparedStepJsonSchema, cashSourceCapabilitiesJsonSchema, cashoutResultFromJson, cashoutResultJsonSchema, cashoutResultToJson, centsToNumber, deriveBuyerProfile, deriveCashOrder, derivePayouts, errors, estimateFromJson, estimateToJson, explainOrder, fiatFromUsdc, fiatToNumber, fillFromJson, fillToJson, formatUsdc, intentStatusSchema, isCashError, isFillLive, isMarketRateSupported, nonNegativeBigintString, orderFromJson, orderToJson, parseCompositeDepositId, prepareCashDepositParams, prepareResultFromJson, prepareResultJsonSchema, prepareResultToJson, preparedStepFromJson, preparedStepToJson, preparedTransactionJsonSchema, preparedTxFromJson, preparedTxToJson, rateToNumber, relayExecutionResultFromJson, relayExecutionResultJsonSchema, relayExecutionResultToJson, relayQuoteFromJson, relayQuoteJsonSchema, relayQuoteToJson, relayStatusFromJson, relayStatusJsonSchema, relayStatusToJson, relayTransactionJsonSchema, relayTransactionsJsonSchema, resolveCashDepositId, sourceCapabilitiesFromJson, sourceCapabilitiesToJson, topUpResultFromJson, topUpResultJsonSchema, topUpResultToJson, usdc, withExplain, withdrawResultFromJson, withdrawResultJsonSchema, withdrawResultToJson };
3792
+ export { BASE_CHAIN_ID, BASE_USDC_ADDRESS, CASH_ORDER_POLL_INTERVAL_MS, CASH_ORDER_STATUSES, CASH_RETAIN_ON_EMPTY, type CashAssetJson, CashBuyerProfile, type CashBuyerProfileJson, CashCapabilities, type CashCapabilitiesJson, type CashChainJson, CashDepositInput, CashError, type CashErrorCode, type CashErrorJson, type CashErrorRecovery, type CashErrorRecoveryJson, type CashErrorShape, CashEstimate, type CashEstimateJson, CashFill, type CashFillJson, CashFillStats, type CashFillStatsJson, CashOrder, type CashOrderData, type CashOrderJson, CashPayoutInfo, type CashPayoutInfoJson, CashPreparedStep, type CashPreparedStepJson, CashSourceCapabilities, type CashSourceCapabilitiesJson, CashoutResult, type CashoutResultJson, type DeriveCashOrderOptions, MARKET_SPREAD_BPS, type MethodCurrencyLike, ORACLE_MIN_CONVERSION_RATE_SENTINEL, type PaymentMethodLike, PrepareResult, type PrepareResultJson, type PreparedTransactionJson, RATE_PRECISION, RelayExecutionResult, type RelayExecutionResultJson, RelayQuote, type RelayQuoteJson, RelayStatus, type RelayStatusJson, type RelayTransactionJson, type RelayTransactionsJson, type ResolvedCashDeposit, TopUpResult, type TopUpResultJson, USDC_DECIMALS, WithdrawResult, type WithdrawResultJson, bigintString, buildIntentAmountRange, buildMarketRateCurrencyOverride, buyerProfileFromJson, buyerProfileToJson, capabilitiesFromJson, capabilitiesToJson, cashAssetJsonSchema, cashBuyerProfileJsonSchema, cashCapabilitiesJsonSchema, cashChainJsonSchema, cashErrorFromJson, cashErrorJsonSchema, cashErrorRecoveryJsonSchema, cashErrorToJson, cashEstimateJsonSchema, cashFillJsonSchema, cashFillStatsJsonSchema, cashNextActionSchema, cashOrderJsonSchema, cashOrderStateSchema, cashPairFillStatsJsonSchema, cashPayoutInfoJsonSchema, cashPayoutPricingJsonSchema, cashPreparedStepJsonSchema, cashSourceCapabilitiesJsonSchema, cashoutResultFromJson, cashoutResultJsonSchema, cashoutResultToJson, centsToNumber, deriveBuyerProfile, deriveCashOrder, derivePayouts, errors, estimateFromJson, estimateToJson, explainOrder, fiatFromUsdc, fiatToNumber, fillFromJson, fillStatsFromJson, fillStatsToJson, fillToJson, formatUsdc, intentStatusSchema, isCashError, isFillLive, isMarketRateSupported, nonNegativeBigintString, orderFromJson, orderToJson, parseCompositeDepositId, prepareCashDepositParams, prepareResultFromJson, prepareResultJsonSchema, prepareResultToJson, preparedStepFromJson, preparedStepToJson, preparedTransactionJsonSchema, preparedTxFromJson, preparedTxToJson, rateToNumber, relayExecutionResultFromJson, relayExecutionResultJsonSchema, relayExecutionResultToJson, relayQuoteFromJson, relayQuoteJsonSchema, relayQuoteToJson, relayStatusFromJson, relayStatusJsonSchema, relayStatusToJson, relayTransactionJsonSchema, relayTransactionsJsonSchema, resolveCashDepositId, sourceCapabilitiesFromJson, sourceCapabilitiesToJson, topUpResultFromJson, topUpResultJsonSchema, topUpResultToJson, usdc, withExplain, withdrawResultFromJson, withdrawResultJsonSchema, withdrawResultToJson };
package/dist/index.js CHANGED
@@ -387,6 +387,26 @@ function parseCompositeDepositId(compositeId) {
387
387
  const onchainDepositId = BigInt(rawDepositId);
388
388
  return { escrowAddress: canonicalEscrowAddress, onchainDepositId };
389
389
  }
390
+
391
+ // src/client/platformGroups.ts
392
+ var PLATFORM_METHOD_GROUPS = {
393
+ zelle: ["zelle", "zelle-chase", "zelle-bofa", "zelle-citi"]
394
+ };
395
+ var METHOD_TO_BASE_PLATFORM = new Map(
396
+ Object.entries(PLATFORM_METHOD_GROUPS).flatMap(
397
+ ([platform, methods]) => methods.map((method) => [method, platform])
398
+ )
399
+ );
400
+ function basePlatformForMethod(method) {
401
+ return METHOD_TO_BASE_PLATFORM.get(method) ?? method;
402
+ }
403
+ function paymentMethodsForPlatform(platform, catalog) {
404
+ const configured = PLATFORM_METHOD_GROUPS[platform];
405
+ const methods = configured ?? [platform];
406
+ return methods.filter((method) => catalog[method] !== void 0);
407
+ }
408
+
409
+ // src/client/capabilities.ts
390
410
  var MIN_CASHOUT_AMOUNT = 10000n;
391
411
  var RECOMMENDED_MIN_CASHOUT_AMOUNT = 1000000n;
392
412
  var PAYEE_HINTS = {
@@ -405,13 +425,20 @@ var PAYEE_HINTS = {
405
425
  var IDENTITY_ATTESTATION_PLATFORMS = /* @__PURE__ */ new Set(["wise", "paypal"]);
406
426
  function buildCapabilities(environment) {
407
427
  const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
408
- const platforms = Object.entries(catalog).map(([platform, entry]) => {
428
+ const currenciesByPlatform = /* @__PURE__ */ new Map();
429
+ for (const [method, entry] of Object.entries(catalog)) {
430
+ const platform = basePlatformForMethod(method);
409
431
  const currencies2 = (entry.currencies ?? []).map((hash) => getCurrencyCodeFromHash(hash)).filter(
410
432
  (code) => code != null && isMarketRateSupported(code)
411
433
  );
434
+ const aggregate = currenciesByPlatform.get(platform) ?? /* @__PURE__ */ new Set();
435
+ for (const currency of currencies2) aggregate.add(currency);
436
+ currenciesByPlatform.set(platform, aggregate);
437
+ }
438
+ const platforms = [...currenciesByPlatform.entries()].map(([platform, currencies2]) => {
412
439
  return {
413
440
  platform,
414
- currencies: [...new Set(currencies2)],
441
+ currencies: [...currencies2].sort(),
415
442
  payeeHint: PAYEE_HINTS[platform] ?? "Your payment handle for this platform",
416
443
  requiresIdentityAttestation: IDENTITY_ATTESTATION_PLATFORMS.has(platform)
417
444
  };
@@ -430,11 +457,8 @@ function buildCapabilities(environment) {
430
457
  pricing: { kind: "oracle-market-rate", spreadBps: 0 }
431
458
  };
432
459
  }
433
- var ETA_WINDOW_DAYS = 30;
434
- var ETA_WINDOW_SECONDS = ETA_WINDOW_DAYS * 24 * 60 * 60;
435
- var ETA_PAGE_LIMIT = 250;
436
- var ETA_MAX_DEPOSIT_SCAN = 2e3;
437
- var FULFILLED = /* @__PURE__ */ new Set(["FULFILLED", "MANUALLY_RELEASED"]);
460
+ var FILL_STATS_WINDOW_SECONDS = 30 * 24 * 60 * 60;
461
+ var FILL_STATS_PAGE_LIMIT = 250;
438
462
  function toUnixSeconds2(value) {
439
463
  if (value === null || value === void 0 || value === "") return void 0;
440
464
  if (value instanceof Date) {
@@ -445,12 +469,22 @@ function toUnixSeconds2(value) {
445
469
  const parsed = Date.parse(value);
446
470
  if (Number.isFinite(parsed)) return Math.floor(parsed / 1e3);
447
471
  }
448
- const n = Number(value);
449
- return Number.isFinite(n) && n > 0 ? n : void 0;
472
+ const numeric = Number(value);
473
+ return Number.isFinite(numeric) && numeric > 0 ? numeric : void 0;
474
+ }
475
+ function normalizeCurrencyCode(value) {
476
+ const raw = value?.trim();
477
+ if (!raw) return void 0;
478
+ if (!raw.toLowerCase().startsWith("0x")) return raw.toUpperCase();
479
+ try {
480
+ return getCurrencyCodeFromHash(raw)?.toUpperCase();
481
+ } catch {
482
+ return void 0;
483
+ }
450
484
  }
451
485
  function median(values) {
452
486
  if (values.length === 0) return void 0;
453
- const sorted = [...values].sort((a, b) => a - b);
487
+ const sorted = [...values].sort((left, right) => left - right);
454
488
  const mid = Math.floor(sorted.length / 2);
455
489
  return sorted.length % 2 === 1 ? sorted[mid] : Math.round((sorted[mid - 1] + sorted[mid]) / 2);
456
490
  }
@@ -462,47 +496,97 @@ function etaLabel(seconds) {
462
496
  const hours = Math.max(1, Math.round(minutes / 60));
463
497
  return `Usually starts in about ${hours} hr`;
464
498
  }
465
- function matchesPayout(deposit, environment, platform, currency) {
466
- const payouts = derivePayouts(
467
- deposit.paymentMethods ?? [],
468
- deposit.currencies ?? [],
469
- getPaymentMethodsCatalog(BASE_CHAIN_ID, environment)
470
- );
471
- return payouts.some(
472
- (payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0 && (platform === void 0 || payout.platform === platform) && (currency === void 0 || payout.currency === currency)
473
- );
499
+ function computeFillStatsSample(deposits, nowSeconds, environment) {
500
+ const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
501
+ const windowStart = nowSeconds - FILL_STATS_WINDOW_SECONDS;
502
+ const fillCounts = /* @__PURE__ */ new Map();
503
+ const latenciesByPair = /* @__PURE__ */ new Map();
504
+ const latenciesByCurrency = /* @__PURE__ */ new Map();
505
+ for (const deposit of deposits) {
506
+ const createdAt = toUnixSeconds2(deposit.createdAt ?? deposit.timestamp);
507
+ const firstFillByPair = /* @__PURE__ */ new Map();
508
+ const firstFillByCurrency = /* @__PURE__ */ new Map();
509
+ for (const intent of deposit.intents ?? []) {
510
+ const fulfilledAt = toUnixSeconds2(intent.fulfillTimestamp);
511
+ if (fulfilledAt === void 0 || fulfilledAt < windowStart) continue;
512
+ let method;
513
+ try {
514
+ method = intent.paymentMethodHash ? resolvePaymentMethodNameFromHash(intent.paymentMethodHash, catalog) : void 0;
515
+ } catch {
516
+ method = void 0;
517
+ }
518
+ const currency = normalizeCurrencyCode(intent.fiatCurrency);
519
+ if (!method || !currency) continue;
520
+ const pair = `${basePlatformForMethod(method)}:${currency}`;
521
+ fillCounts.set(pair, (fillCounts.get(pair) ?? 0) + 1);
522
+ if (createdAt === void 0 || createdAt < windowStart || fulfilledAt < createdAt) continue;
523
+ const previousPairFill = firstFillByPair.get(pair);
524
+ if (previousPairFill === void 0 || fulfilledAt < previousPairFill) {
525
+ firstFillByPair.set(pair, fulfilledAt);
526
+ }
527
+ const previousCurrencyFill = firstFillByCurrency.get(currency);
528
+ if (previousCurrencyFill === void 0 || fulfilledAt < previousCurrencyFill) {
529
+ firstFillByCurrency.set(currency, fulfilledAt);
530
+ }
531
+ }
532
+ if (createdAt === void 0) continue;
533
+ for (const [pair, firstFill] of firstFillByPair) {
534
+ const latencies = latenciesByPair.get(pair) ?? [];
535
+ latencies.push(firstFill - createdAt);
536
+ latenciesByPair.set(pair, latencies);
537
+ }
538
+ for (const [currency, firstFill] of firstFillByCurrency) {
539
+ const latencies = latenciesByCurrency.get(currency) ?? [];
540
+ latencies.push(firstFill - createdAt);
541
+ latenciesByCurrency.set(currency, latencies);
542
+ }
543
+ }
544
+ const stats = {};
545
+ for (const [pair, fills] of fillCounts) {
546
+ const medianFillSeconds = median(latenciesByPair.get(pair) ?? []);
547
+ stats[pair] = {
548
+ fills,
549
+ ...medianFillSeconds !== void 0 ? { medianFillSeconds } : {}
550
+ };
551
+ }
552
+ const medianFillSecondsByCurrency = /* @__PURE__ */ new Map();
553
+ for (const [currency, latencies] of latenciesByCurrency) {
554
+ const value = median(latencies);
555
+ if (value !== void 0) medianFillSecondsByCurrency.set(currency, value);
556
+ }
557
+ return { stats, medianFillSecondsByCurrency };
474
558
  }
475
- async function readFillEta(client, input) {
559
+ async function readFillStatsSample(client, environment) {
476
560
  const now = Math.floor(Date.now() / 1e3);
477
- const windowStart = now - ETA_WINDOW_SECONDS;
561
+ const windowStart = now - FILL_STATS_WINDOW_SECONDS;
478
562
  const deposits = [];
479
- for (let offset = 0; offset < ETA_MAX_DEPOSIT_SCAN; offset += ETA_PAGE_LIMIT) {
563
+ for (let offset = 0; ; offset += FILL_STATS_PAGE_LIMIT) {
480
564
  const page = await client.indexer.getDepositsWithRelations(
481
565
  { chainId: BASE_CHAIN_ID },
482
- { limit: ETA_PAGE_LIMIT, offset, orderBy: "timestamp", orderDirection: "desc" },
566
+ {
567
+ limit: FILL_STATS_PAGE_LIMIT,
568
+ offset,
569
+ orderBy: "updatedAt",
570
+ orderDirection: "desc"
571
+ },
483
572
  { includeIntents: true, intentStatuses: ["FULFILLED", "MANUALLY_RELEASED"] }
484
573
  );
485
574
  deposits.push(...page);
486
- if (page.length < ETA_PAGE_LIMIT) break;
487
- const oldestCreatedAt = Math.min(
488
- ...page.map((deposit) => toUnixSeconds2(deposit.createdAt ?? deposit.timestamp) ?? Infinity)
575
+ if (page.length < FILL_STATS_PAGE_LIMIT) break;
576
+ const oldestUpdatedAt = Math.min(
577
+ ...page.map((deposit) => toUnixSeconds2(deposit.updatedAt) ?? Infinity)
489
578
  );
490
- if (oldestCreatedAt < windowStart) break;
579
+ if (oldestUpdatedAt < windowStart) break;
491
580
  }
492
- const firstFillLatencies = [];
493
- for (const deposit of deposits) {
494
- const createdAt = toUnixSeconds2(deposit.createdAt ?? deposit.timestamp);
495
- if (createdAt === void 0 || createdAt < windowStart) continue;
496
- if (!matchesPayout(deposit, input.environment, input.platform, input.currency)) continue;
497
- const fulfilled = (deposit.intents ?? []).filter((intent) => intent.status != null && FULFILLED.has(intent.status)).map((intent) => ({
498
- fulfilledAt: toUnixSeconds2(intent.fulfillTimestamp)
499
- })).filter(
500
- (intent) => intent.fulfilledAt !== void 0 && intent.fulfilledAt >= createdAt
501
- ).sort((a, b) => a.fulfilledAt - b.fulfilledAt);
502
- if (fulfilled.length === 0) continue;
503
- firstFillLatencies.push(fulfilled[0].fulfilledAt - createdAt);
504
- }
505
- const seconds = median(firstFillLatencies);
581
+ return computeFillStatsSample(deposits, now, environment);
582
+ }
583
+ async function readFillStats(client, environment) {
584
+ return (await readFillStatsSample(client, environment)).stats;
585
+ }
586
+ async function readFillEta(client, input) {
587
+ const sample = await readFillStatsSample(client, input.environment);
588
+ const currency = input.currency.toUpperCase();
589
+ const seconds = input.platform ? sample.stats[`${basePlatformForMethod(input.platform)}:${currency}`]?.medianFillSeconds : sample.medianFillSecondsByCurrency.get(currency);
506
590
  return {
507
591
  ...seconds !== void 0 ? { seconds } : {},
508
592
  label: etaLabel(seconds)
@@ -1170,6 +1254,7 @@ function createCashClient(options) {
1170
1254
  }
1171
1255
  function validatePayout(input) {
1172
1256
  const { receive } = input;
1257
+ const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
1173
1258
  const platform = buildCapabilities(environment).platforms.find(
1174
1259
  (capability) => capability.platform === receive.platform
1175
1260
  );
@@ -1180,14 +1265,13 @@ function createCashClient(options) {
1180
1265
  if (!platform.currencies.includes(receive.currency)) {
1181
1266
  throw errors.unsupportedPlatformCurrency(receive.platform, receive.currency);
1182
1267
  }
1268
+ const paymentMethods = paymentMethodsForPlatform(receive.platform, catalog);
1183
1269
  return {
1184
- payouts: [
1185
- {
1186
- processorName: receive.platform,
1187
- currency: receive.currency,
1188
- payeeData: receive.payee
1189
- }
1190
- ]
1270
+ payouts: paymentMethods.map((processorName) => ({
1271
+ processorName,
1272
+ currency: receive.currency,
1273
+ payeeData: receive.payee
1274
+ }))
1191
1275
  };
1192
1276
  }
1193
1277
  function validateDepositInput(amount, input, payoutInput = validatePayout(input)) {
@@ -1465,6 +1549,13 @@ function createCashClient(options) {
1465
1549
  ...options.relay ? { relay: options.relay } : {}
1466
1550
  });
1467
1551
  },
1552
+ async fillStats() {
1553
+ try {
1554
+ return await readFillStats(readClient, environment);
1555
+ } catch (err) {
1556
+ throw errors.indexerUnavailable("fill stats", err);
1557
+ }
1558
+ },
1468
1559
  async cashout(input, opts) {
1469
1560
  const client = await signingClient("cashout", opts);
1470
1561
  const owner = opts.signer.account.address;
@@ -2064,6 +2155,11 @@ var cashEstimateJsonSchema = z.object({
2064
2155
  label: z.string()
2065
2156
  }).optional()
2066
2157
  });
2158
+ var cashPairFillStatsJsonSchema = z.object({
2159
+ fills: z.number().int().nonnegative(),
2160
+ medianFillSeconds: z.number().int().nonnegative().optional()
2161
+ }).strict();
2162
+ var cashFillStatsJsonSchema = z.record(z.string(), cashPairFillStatsJsonSchema);
2067
2163
  var preparedTransactionJsonSchema = z.object({
2068
2164
  to: z.string(),
2069
2165
  data: z.string(),
@@ -2368,6 +2464,21 @@ function estimateFromJson(json) {
2368
2464
  } : void 0
2369
2465
  });
2370
2466
  }
2467
+ function fillStatsToJson(stats) {
2468
+ return cashFillStatsJsonSchema.parse(stats);
2469
+ }
2470
+ function fillStatsFromJson(json) {
2471
+ const parsed = cashFillStatsJsonSchema.parse(json);
2472
+ return Object.fromEntries(
2473
+ Object.entries(parsed).map(([pair, stats]) => [
2474
+ pair,
2475
+ {
2476
+ fills: stats.fills,
2477
+ ...stats.medianFillSeconds !== void 0 ? { medianFillSeconds: stats.medianFillSeconds } : {}
2478
+ }
2479
+ ])
2480
+ );
2481
+ }
2371
2482
  function cashAssetFromJson(asset) {
2372
2483
  return {
2373
2484
  chainId: asset.chainId,
@@ -2645,4 +2756,4 @@ function cashErrorFromJson(json) {
2645
2756
  });
2646
2757
  }
2647
2758
 
2648
- export { CASH_ATTRIBUTION_CODE, MIN_CASHOUT_AMOUNT, RATE_PRECISION, RECOMMENDED_MIN_CASHOUT_AMOUNT, bigintString, buildCapabilities, buildIntentAmountRange, buildMarketRateCurrencyOverride, buyerProfileFromJson, buyerProfileToJson, capabilitiesFromJson, capabilitiesToJson, cashAssetJsonSchema, cashBuyerProfileJsonSchema, cashCapabilitiesJsonSchema, cashChainJsonSchema, cashErrorFromJson, cashErrorJsonSchema, cashErrorRecoveryJsonSchema, cashErrorToJson, cashEstimateJsonSchema, cashFillJsonSchema, cashNextActionSchema, cashOrderJsonSchema, cashOrderStateSchema, cashPayoutInfoJsonSchema, cashPayoutPricingJsonSchema, cashPreparedStepJsonSchema, cashSourceCapabilitiesJsonSchema, cashoutResultFromJson, cashoutResultJsonSchema, cashoutResultToJson, centsToNumber, createCashClient, deriveBuyerProfile, deriveCashOrder, derivePayouts, estimateFromJson, estimateToJson, explainOrder, fiatFromUsdc, fiatToNumber, fillFromJson, fillToJson, formatUsdc, intentStatusSchema, isFillLive, isMarketRateSupported, nonNegativeBigintString, orderFromJson, orderToJson, parseCompositeDepositId, prepareCashDepositParams, prepareResultFromJson, prepareResultJsonSchema, prepareResultToJson, preparedStepFromJson, preparedStepToJson, preparedTransactionJsonSchema, preparedTxFromJson, preparedTxToJson, rateToNumber, relayExecutionResultFromJson, relayExecutionResultJsonSchema, relayExecutionResultToJson, relayQuoteFromJson, relayQuoteJsonSchema, relayQuoteToJson, relayStatusFromJson, relayStatusJsonSchema, relayStatusToJson, relayTransactionJsonSchema, relayTransactionsJsonSchema, resolveCashDepositId, sourceCapabilitiesFromJson, sourceCapabilitiesToJson, topUpResultFromJson, topUpResultJsonSchema, topUpResultToJson, usdc, withExplain, withdrawResultFromJson, withdrawResultJsonSchema, withdrawResultToJson };
2759
+ export { CASH_ATTRIBUTION_CODE, MIN_CASHOUT_AMOUNT, RATE_PRECISION, RECOMMENDED_MIN_CASHOUT_AMOUNT, bigintString, buildCapabilities, buildIntentAmountRange, buildMarketRateCurrencyOverride, buyerProfileFromJson, buyerProfileToJson, capabilitiesFromJson, capabilitiesToJson, cashAssetJsonSchema, cashBuyerProfileJsonSchema, cashCapabilitiesJsonSchema, cashChainJsonSchema, cashErrorFromJson, cashErrorJsonSchema, cashErrorRecoveryJsonSchema, cashErrorToJson, cashEstimateJsonSchema, cashFillJsonSchema, cashFillStatsJsonSchema, cashNextActionSchema, cashOrderJsonSchema, cashOrderStateSchema, cashPairFillStatsJsonSchema, cashPayoutInfoJsonSchema, cashPayoutPricingJsonSchema, cashPreparedStepJsonSchema, cashSourceCapabilitiesJsonSchema, cashoutResultFromJson, cashoutResultJsonSchema, cashoutResultToJson, centsToNumber, createCashClient, deriveBuyerProfile, deriveCashOrder, derivePayouts, estimateFromJson, estimateToJson, explainOrder, fiatFromUsdc, fiatToNumber, fillFromJson, fillStatsFromJson, fillStatsToJson, fillToJson, formatUsdc, intentStatusSchema, isFillLive, isMarketRateSupported, nonNegativeBigintString, orderFromJson, orderToJson, parseCompositeDepositId, prepareCashDepositParams, prepareResultFromJson, prepareResultJsonSchema, prepareResultToJson, preparedStepFromJson, preparedStepToJson, preparedTransactionJsonSchema, preparedTxFromJson, preparedTxToJson, rateToNumber, relayExecutionResultFromJson, relayExecutionResultJsonSchema, relayExecutionResultToJson, relayQuoteFromJson, relayQuoteJsonSchema, relayQuoteToJson, relayStatusFromJson, relayStatusJsonSchema, relayStatusToJson, relayTransactionJsonSchema, relayTransactionsJsonSchema, resolveCashDepositId, sourceCapabilitiesFromJson, sourceCapabilitiesToJson, topUpResultFromJson, topUpResultJsonSchema, topUpResultToJson, usdc, withExplain, withdrawResultFromJson, withdrawResultJsonSchema, withdrawResultToJson };
package/dist/react.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CurrencyType } from '@zkp2p/sdk';
2
- import { q as CashClient, E as EstimateInput, i as CashEstimate, B as CashoutOptions, h as CashoutResult, A as CashoutInput, T as TopUpResult, W as WithdrawResult, e as CashOrder } from './createCashClient-jUA_GNdh.cjs';
2
+ import { r as CashClient, G as EstimateInput, i as CashEstimate, E as CashoutOptions, h as CashoutResult, D as CashoutInput, T as TopUpResult, W as WithdrawResult, e as CashOrder } from './createCashClient-BIzOLHjF.cjs';
3
3
  import { WalletClient } from 'viem';
4
4
  import '@relayprotocol/relay-sdk';
5
5
 
package/dist/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CurrencyType } from '@zkp2p/sdk';
2
- import { q as CashClient, E as EstimateInput, i as CashEstimate, B as CashoutOptions, h as CashoutResult, A as CashoutInput, T as TopUpResult, W as WithdrawResult, e as CashOrder } from './createCashClient-jUA_GNdh.js';
2
+ import { r as CashClient, G as EstimateInput, i as CashEstimate, E as CashoutOptions, h as CashoutResult, D as CashoutInput, T as TopUpResult, W as WithdrawResult, e as CashOrder } from './createCashClient-BIzOLHjF.js';
3
3
  import { WalletClient } from 'viem';
4
4
  import '@relayprotocol/relay-sdk';
5
5
 
package/dist/tools.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // package.json
4
4
  var package_default = {
5
- version: "0.1.7"};
5
+ version: "0.1.8"};
6
6
 
7
7
  // src/tools/index.ts
8
8
  var bigintString = {
@@ -112,6 +112,15 @@ var builtInCashTools = [
112
112
  additionalProperties: false
113
113
  }
114
114
  },
115
+ {
116
+ name: "cash_fill_stats",
117
+ description: "Read raw 30-day demand and first-fill speed evidence for every observed platform:currency pair. Consumers should apply their own threshold and fail open to cash_capabilities when stats are unavailable or filtering would empty the catalog.",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {},
121
+ additionalProperties: false
122
+ }
123
+ },
115
124
  {
116
125
  name: "cash_cashout",
117
126
  description: "Start a Base-USDC cash-out using the custody-separated prepare path. Returns UNSIGNED transactions plus same-index steps [approve, createDeposit]; signing and ordered submission stay host-side. For another source asset, complete cash_source_quote and cash_source_status first, then pass the guaranteed Base USDC output amount here.",
package/dist/tools.d.cts CHANGED
@@ -135,6 +135,14 @@ declare const builtInCashTools: readonly [{
135
135
  readonly required: readonly ["amount", "currency"];
136
136
  readonly additionalProperties: false;
137
137
  };
138
+ }, {
139
+ readonly name: "cash_fill_stats";
140
+ readonly description: "Read raw 30-day demand and first-fill speed evidence for every observed platform:currency pair. Consumers should apply their own threshold and fail open to cash_capabilities when stats are unavailable or filtering would empty the catalog.";
141
+ readonly inputSchema: {
142
+ readonly type: "object";
143
+ readonly properties: {};
144
+ readonly additionalProperties: false;
145
+ };
138
146
  }, {
139
147
  readonly name: "cash_cashout";
140
148
  readonly description: "Start a Base-USDC cash-out using the custody-separated prepare path. Returns UNSIGNED transactions plus same-index steps [approve, createDeposit]; signing and ordered submission stay host-side. For another source asset, complete cash_source_quote and cash_source_status first, then pass the guaranteed Base USDC output amount here.";
package/dist/tools.d.ts CHANGED
@@ -135,6 +135,14 @@ declare const builtInCashTools: readonly [{
135
135
  readonly required: readonly ["amount", "currency"];
136
136
  readonly additionalProperties: false;
137
137
  };
138
+ }, {
139
+ readonly name: "cash_fill_stats";
140
+ readonly description: "Read raw 30-day demand and first-fill speed evidence for every observed platform:currency pair. Consumers should apply their own threshold and fail open to cash_capabilities when stats are unavailable or filtering would empty the catalog.";
141
+ readonly inputSchema: {
142
+ readonly type: "object";
143
+ readonly properties: {};
144
+ readonly additionalProperties: false;
145
+ };
138
146
  }, {
139
147
  readonly name: "cash_cashout";
140
148
  readonly description: "Start a Base-USDC cash-out using the custody-separated prepare path. Returns UNSIGNED transactions plus same-index steps [approve, createDeposit]; signing and ordered submission stay host-side. For another source asset, complete cash_source_quote and cash_source_status first, then pass the guaranteed Base USDC output amount here.";
package/dist/tools.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // package.json
2
2
  var package_default = {
3
- version: "0.1.7"};
3
+ version: "0.1.8"};
4
4
 
5
5
  // src/tools/index.ts
6
6
  var bigintString = {
@@ -110,6 +110,15 @@ var builtInCashTools = [
110
110
  additionalProperties: false
111
111
  }
112
112
  },
113
+ {
114
+ name: "cash_fill_stats",
115
+ description: "Read raw 30-day demand and first-fill speed evidence for every observed platform:currency pair. Consumers should apply their own threshold and fail open to cash_capabilities when stats are unavailable or filtering would empty the catalog.",
116
+ inputSchema: {
117
+ type: "object",
118
+ properties: {},
119
+ additionalProperties: false
120
+ }
121
+ },
113
122
  {
114
123
  name: "cash_cashout",
115
124
  description: "Start a Base-USDC cash-out using the custody-separated prepare path. Returns UNSIGNED transactions plus same-index steps [approve, createDeposit]; signing and ordered submission stay host-side. For another source asset, complete cash_source_quote and cash_source_status first, then pass the guaranteed Base USDC output amount here.",
@@ -124,10 +124,17 @@ passes through `delivering` until the last one completes. `filledAmount`,
124
124
  ## The ETA principle
125
125
 
126
126
  `estimate().eta` is historical, not a promise. It uses rolling 30-day indexer
127
- data from deposit/order creation to the first fulfilled fill. It deliberately
128
- does **not** measure buyer signal to fulfillment; that would miss the
129
- buyer-arrival wait that users actually care about. The public shape is small:
130
- `{ seconds, label }`.
127
+ data from deposit/order creation to the first fulfilled fill through the
128
+ intent's actual platform and currency pair. It deliberately does **not**
129
+ measure buyer signal to fulfillment; that would miss the buyer-arrival wait
130
+ that users actually care about. The public shape is small: `{ seconds, label }`.
131
+
132
+ `fillStats()` exposes the sampler's raw evidence for catalog filtering as
133
+ `Record<"platform:currency", { fills, medianFillSeconds? }>`. Bank-scoped Zelle
134
+ methods aggregate to `zelle:USD`. Consumers own thresholding; the recommended
135
+ gate is `fills >= 10 && medianFillSeconds <= 48h`, with a fail-open fallback to
136
+ the full capability catalog when the read fails or filtering would empty it.
137
+ Medians are per-deposit first-fill latencies, never means or censored cohorts.
131
138
 
132
139
  - **Buyer arrival time is market-driven.** A deposit at market rate should
133
140
  fill fast, but the ETA is only a recent historical sample.
package/llms.txt CHANGED
@@ -19,9 +19,13 @@ Key facts:
19
19
  source.amount is Relay's guaranteed minimum Base USDC output and the exact
20
20
  order deposit amount, not the route's actual output.
21
21
  - There is NO locked fiat quote. estimate() reads the oracle; the binding rate
22
- resolves at fill time. ETA is `{ seconds, label }` from rolling 30-day,
23
- zero-spread market-rate deposits in the same payout corridor, not a
24
- guarantee.
22
+ resolves at fill time. ETA is `{ seconds, label }` from the same rolling
23
+ 30-day, intent-attributed pair sampler as fillStats(), not a guarantee.
24
+ - fillStats() returns raw `{ fills, medianFillSeconds? }` evidence keyed by
25
+ `platform:currency`. Recommended consumer gate: fills >= 10 and median <=
26
+ 48h; fail open to capabilities() if unavailable or filtering empties it.
27
+ - capabilities() exposes one Zelle platform. A zelle cashout internally attaches
28
+ the generic method plus Chase, Bank of America, and Citi buyer routes.
25
29
  - Resume any order from its depositId alone (composite escrow_onchainId).
26
30
  - One unwind verb: withdraw(depositId) - prunes expired intents automatically;
27
31
  pass amount for a partial withdrawal of the unlocked balance.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zkp2p/cash",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Peer Cash - offramp-only SDK for routing crypto to Base USDC, then cashing out to fiat at the live oracle market rate.",
5
5
  "license": "MIT",
6
6
  "author": "Peer (https://peer.xyz)",
@@ -92,13 +92,15 @@
92
92
  "scripts": {
93
93
  "build": "tsup",
94
94
  "typecheck": "tsc --noEmit",
95
- "lint": "eslint src test examples",
95
+ "lint": "eslint src test examples scripts",
96
96
  "format": "prettier --write .",
97
97
  "format:check": "prettier --check .",
98
98
  "test": "vitest run",
99
99
  "test:watch": "vitest",
100
100
  "audit": "bun audit --production",
101
101
  "pack:check": "bun scripts/check-packed-package.ts",
102
+ "verify:production-crosschain-relay": "bun scripts/verify-production-crosschain-relay.ts",
103
+ "verify:production-maker": "bun scripts/verify-production-maker.ts",
102
104
  "prepack": "bun run build",
103
105
  "ci": "bun run typecheck && bun run lint && bun run format:check && bun run test && bun run audit && bun run build && bun run pack:check"
104
106
  },