@zkp2p/cash 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
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-FKVPZVFH.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-FKVPZVFH.js';
3
- import { parseAbi, parseEventLogs, http, createWalletClient, encodeFunctionData } from 'viem';
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, CashError } from './chunk-P3KYZ2FX.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-P3KYZ2FX.js';
3
+ import { parseAbi, parseEventLogs, isAddress, http, createWalletClient, encodeFunctionData } from 'viem';
4
4
  import { base } from 'viem/chains';
5
5
  import { getSpreadOracleConfig, currencyInfo, getPaymentMethodsCatalog, getGatingServiceAddress, resolvePaymentMethodHashFromCatalog, resolvePaymentMethodNameFromHash, getCurrencyCodeFromHash, createCompositeDepositId, appendAttributionToCalldata, Zkp2pClient, CHAINLINK_ORACLE_FEEDS } from '@zkp2p/sdk';
6
6
  import { createClient, MAINNET_RELAY_API } from '@relayprotocol/relay-sdk';
@@ -221,10 +221,12 @@ function deriveCashOrder(depositId, intents, options = {}) {
221
221
  const total = options.totalAmount ?? remaining + outstanding + taken + withdrawn;
222
222
  const status = options.status;
223
223
  const isTerminal = status === "CLOSED" || status === "WITHDRAWN";
224
- const hasLiveFunds = remaining > DUST_THRESHOLD || outstanding > 0n;
224
+ const hasLiveFunds = remaining >= DUST_THRESHOLD || outstanding > 0n;
225
225
  let state;
226
226
  if (outstanding > 0n) {
227
227
  state = taken > 0n ? "delivering" : "matched";
228
+ } else if (!hasLiveFunds && withdrawn > 0n) {
229
+ state = "returned";
228
230
  } else if (taken > 0n && !hasLiveFunds) {
229
231
  state = "delivered";
230
232
  } else if (taken > 0n && hasLiveFunds) {
@@ -266,7 +268,7 @@ function deriveCashOrder(depositId, intents, options = {}) {
266
268
  ...options.payouts !== void 0 ? { payouts: options.payouts } : {},
267
269
  ...options.successRateBps !== void 0 ? { successRateBps: options.successRateBps } : {},
268
270
  isInFlight,
269
- withdrawn: isTerminal
271
+ withdrawn: state === "returned" && withdrawn > 0n
270
272
  });
271
273
  }
272
274
  var ORACLE_KINDS = /* @__PURE__ */ new Set(["oracle_chainlink", "oracle_pyth"]);
@@ -376,12 +378,14 @@ function resolveCashDepositId(params) {
376
378
  }
377
379
  function parseCompositeDepositId(compositeId) {
378
380
  const idx = compositeId.lastIndexOf("_");
379
- if (idx === -1) {
380
- return { escrowAddress: "", onchainDepositId: BigInt(compositeId) };
381
- }
382
381
  const escrowAddress = compositeId.slice(0, idx);
383
- const onchainDepositId = BigInt(compositeId.slice(idx + 1) || "0");
384
- return { escrowAddress, onchainDepositId };
382
+ const rawDepositId = compositeId.slice(idx + 1);
383
+ if (idx <= 0 || compositeId.indexOf("_") !== idx || !isAddress(escrowAddress, { strict: false }) || !/^\d+$/.test(rawDepositId)) {
384
+ throw new Error(`Invalid deposit id: '${compositeId}'`);
385
+ }
386
+ const canonicalEscrowAddress = escrowAddress.toLowerCase();
387
+ const onchainDepositId = BigInt(rawDepositId);
388
+ return { escrowAddress: canonicalEscrowAddress, onchainDepositId };
385
389
  }
386
390
  var MIN_CASHOUT_AMOUNT = 10000n;
387
391
  var RECOMMENDED_MIN_CASHOUT_AMOUNT = 1000000n;
@@ -399,9 +403,6 @@ var PAYEE_HINTS = {
399
403
  n26: "MoneyBeam email or phone number"
400
404
  };
401
405
  var IDENTITY_ATTESTATION_PLATFORMS = /* @__PURE__ */ new Set(["wise", "paypal"]);
402
- function platformRequiresIdentityAttestation(platform) {
403
- return IDENTITY_ATTESTATION_PLATFORMS.has(platform);
404
- }
405
406
  function buildCapabilities(environment) {
406
407
  const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
407
408
  const platforms = Object.entries(catalog).map(([platform, entry]) => {
@@ -429,9 +430,10 @@ function buildCapabilities(environment) {
429
430
  pricing: { kind: "oracle-market-rate", spreadBps: 0 }
430
431
  };
431
432
  }
432
- var ETA_WINDOW_DAYS = 7;
433
+ var ETA_WINDOW_DAYS = 30;
433
434
  var ETA_WINDOW_SECONDS = ETA_WINDOW_DAYS * 24 * 60 * 60;
434
- var ETA_SAMPLE_LIMIT = 250;
435
+ var ETA_PAGE_LIMIT = 250;
436
+ var ETA_MAX_DEPOSIT_SCAN = 2e3;
435
437
  var FULFILLED = /* @__PURE__ */ new Set(["FULFILLED", "MANUALLY_RELEASED"]);
436
438
  function toUnixSeconds2(value) {
437
439
  if (value === null || value === void 0 || value === "") return void 0;
@@ -466,19 +468,27 @@ function matchesPayout(deposit, environment, platform, currency) {
466
468
  deposit.currencies ?? [],
467
469
  getPaymentMethodsCatalog(BASE_CHAIN_ID, environment)
468
470
  );
469
- if (payouts.length === 0) return true;
470
471
  return payouts.some(
471
- (payout) => (platform === void 0 || payout.platform === platform) && (currency === void 0 || payout.currency === currency)
472
+ (payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0 && (platform === void 0 || payout.platform === platform) && (currency === void 0 || payout.currency === currency)
472
473
  );
473
474
  }
474
475
  async function readFillEta(client, input) {
475
476
  const now = Math.floor(Date.now() / 1e3);
476
477
  const windowStart = now - ETA_WINDOW_SECONDS;
477
- const deposits = await client.indexer.getDepositsWithRelations(
478
- { chainId: BASE_CHAIN_ID },
479
- { limit: ETA_SAMPLE_LIMIT, orderBy: "updatedAt", orderDirection: "desc" },
480
- { includeIntents: true, intentStatuses: ["FULFILLED", "MANUALLY_RELEASED"] }
481
- );
478
+ const deposits = [];
479
+ for (let offset = 0; offset < ETA_MAX_DEPOSIT_SCAN; offset += ETA_PAGE_LIMIT) {
480
+ const page = await client.indexer.getDepositsWithRelations(
481
+ { chainId: BASE_CHAIN_ID },
482
+ { limit: ETA_PAGE_LIMIT, offset, orderBy: "timestamp", orderDirection: "desc" },
483
+ { includeIntents: true, intentStatuses: ["FULFILLED", "MANUALLY_RELEASED"] }
484
+ );
485
+ 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)
489
+ );
490
+ if (oldestCreatedAt < windowStart) break;
491
+ }
482
492
  const firstFillLatencies = [];
483
493
  for (const deposit of deposits) {
484
494
  const createdAt = toUnixSeconds2(deposit.createdAt ?? deposit.timestamp);
@@ -582,20 +592,175 @@ function normalizeChain(chain) {
582
592
  function isSupportedEvmChain(chain) {
583
593
  return chain.vmType === void 0 || chain.vmType === "evm";
584
594
  }
595
+ function isExecutableSourceChain(chain) {
596
+ return isSupportedEvmChain(chain) && !chain.disabled && chain.depositEnabled && !chain.blockProductionLagging;
597
+ }
585
598
  function quoteRequestId(quote) {
586
599
  return quote.steps.map((step) => step.requestId).find((id) => id !== void 0);
587
600
  }
601
+ function collectRelayTransactions(steps, sourceChainId) {
602
+ const origin = [];
603
+ const destination = [];
604
+ const record = (tx) => {
605
+ (tx.chainId === sourceChainId ? origin : destination).push(tx);
606
+ };
607
+ for (const step of steps) {
608
+ for (const item of step.items) {
609
+ for (const tx of item.internalTxHashes ?? []) {
610
+ record({ hash: tx.txHash, chainId: tx.chainId });
611
+ }
612
+ for (const tx of item.txHashes ?? []) {
613
+ record({ hash: tx.txHash, chainId: tx.chainId });
614
+ }
615
+ }
616
+ }
617
+ const dedupe = (txs) => [
618
+ ...new Map(txs.map((tx) => [`${tx.chainId}:${tx.hash.toLowerCase()}`, tx])).values()
619
+ ];
620
+ return { origin: dedupe(origin), destination: dedupe(destination) };
621
+ }
622
+ function relayTransactionHashes(transactions) {
623
+ return [
624
+ ...new Set([...transactions.origin, ...transactions.destination].map(({ hash }) => hash))
625
+ ];
626
+ }
588
627
  function quoteSourceChainId(quote) {
589
628
  const details = asRecord(quote.details);
590
629
  const currencyIn = asRecord(details.currencyIn);
591
630
  const sourceCurrency = asRecord(currencyIn.currency);
592
631
  return asNumber(sourceCurrency.chainId);
593
632
  }
633
+ function assertCanonicalRelayDestination(quote) {
634
+ const details = asRecord(quote.details);
635
+ const currencyOut = asRecord(details.currencyOut);
636
+ const destination = asRecord(currencyOut.currency);
637
+ if (asNumber(destination.chainId) !== BASE_CHAIN_ID || asString(destination.address)?.toLowerCase() !== BASE_USDC_ADDRESS.toLowerCase()) {
638
+ throw new Error("Relay quote destination is not canonical Base USDC");
639
+ }
640
+ }
641
+ async function assertWalletChainId(wallet, expectedChainId, operation) {
642
+ let actualChainId;
643
+ try {
644
+ actualChainId = await wallet.getChainId();
645
+ } catch (err) {
646
+ throw errors.signerChainUnavailable(operation, expectedChainId, err);
647
+ }
648
+ if (actualChainId !== expectedChainId) {
649
+ throw errors.signerChainMismatch(operation, expectedChainId, actualChainId);
650
+ }
651
+ }
652
+ async function assertRelayExecutionIdentity(quote, wallet, expectedRecipient) {
653
+ const signer = wallet.account?.address;
654
+ if (!signer) throw new Error("Relay execution requires a wallet account");
655
+ const sourceChainId = quoteSourceChainId(quote);
656
+ if (sourceChainId !== void 0) {
657
+ await assertWalletChainId(wallet, sourceChainId, "Relay execution");
658
+ }
659
+ const details = asRecord(quote.details);
660
+ const sender = asString(details.sender);
661
+ const recipient = asString(details.recipient);
662
+ if (!sender || sender.toLowerCase() !== signer.toLowerCase()) {
663
+ throw new Error("Relay quote sender does not match the execution signer");
664
+ }
665
+ const destinationOwner = expectedRecipient ?? signer;
666
+ if (!recipient || recipient.toLowerCase() !== destinationOwner.toLowerCase()) {
667
+ throw new Error("Relay quote recipient does not match the expected Base recipient");
668
+ }
669
+ }
670
+ function isRelaySecretKey(key) {
671
+ const normalized = key.toLowerCase();
672
+ return normalized === "headers" || normalized === "apikey";
673
+ }
674
+ function redactRelayValue(value, seen = /* @__PURE__ */ new WeakMap()) {
675
+ if (value === null || typeof value !== "object") return value;
676
+ if (value instanceof Date || value instanceof Error) return value;
677
+ const existing = seen.get(value);
678
+ if (existing !== void 0) return existing;
679
+ if (Array.isArray(value)) {
680
+ const output2 = [];
681
+ seen.set(value, output2);
682
+ for (const entry of value) output2.push(redactRelayValue(entry, seen));
683
+ return output2;
684
+ }
685
+ const output = {};
686
+ seen.set(value, output);
687
+ for (const [key, entry] of Object.entries(value)) {
688
+ if (!isRelaySecretKey(key)) output[key] = redactRelayValue(entry, seen);
689
+ }
690
+ return output;
691
+ }
692
+ var RELAY_WIRE_TYPE = "__zkp2pCashType";
693
+ function sanitizeRelayValue(value, seen = /* @__PURE__ */ new WeakSet()) {
694
+ if (typeof value === "bigint") {
695
+ return { [RELAY_WIRE_TYPE]: "bigint", value: value.toString() };
696
+ }
697
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
698
+ return value;
699
+ }
700
+ if (typeof value === "number") {
701
+ return Number.isFinite(value) ? value : { [RELAY_WIRE_TYPE]: "number", value: String(value) };
702
+ }
703
+ if (typeof value === "undefined") return { [RELAY_WIRE_TYPE]: "undefined" };
704
+ if (value instanceof Date) {
705
+ return { [RELAY_WIRE_TYPE]: "date", value: value.toISOString() };
706
+ }
707
+ if (value instanceof Error) {
708
+ return {
709
+ [RELAY_WIRE_TYPE]: "error",
710
+ name: value.name,
711
+ message: value.message
712
+ };
713
+ }
714
+ if (typeof value !== "object") return void 0;
715
+ if (seen.has(value)) throw new TypeError("Relay payload contains a circular reference");
716
+ seen.add(value);
717
+ if (Array.isArray(value)) {
718
+ const output2 = value.map((entry) => sanitizeRelayValue(entry, seen));
719
+ seen.delete(value);
720
+ return output2;
721
+ }
722
+ const output = {};
723
+ for (const [key, entry] of Object.entries(value)) {
724
+ if (isRelaySecretKey(key)) continue;
725
+ const sanitized = sanitizeRelayValue(entry, seen);
726
+ if (sanitized !== void 0) output[key] = sanitized;
727
+ }
728
+ seen.delete(value);
729
+ return output;
730
+ }
731
+ function restoreRelayValue(value) {
732
+ if (Array.isArray(value)) return value.map(restoreRelayValue);
733
+ if (value === null || typeof value !== "object") return value;
734
+ const row = value;
735
+ const wireType = row[RELAY_WIRE_TYPE];
736
+ const keyCount = Object.keys(row).length;
737
+ if (keyCount === 2 && wireType === "bigint" && typeof row.value === "string") {
738
+ return BigInt(row.value);
739
+ }
740
+ if (keyCount === 2 && wireType === "date" && typeof row.value === "string") {
741
+ return new Date(row.value);
742
+ }
743
+ if (keyCount === 2 && wireType === "number" && typeof row.value === "string") {
744
+ return Number(row.value);
745
+ }
746
+ if (keyCount === 1 && wireType === "undefined") return void 0;
747
+ if (keyCount === 3 && wireType === "error" && typeof row.message === "string") {
748
+ const error = new Error(row.message);
749
+ if (typeof row.name === "string") error.name = row.name;
750
+ return error;
751
+ }
752
+ return Object.fromEntries(
753
+ Object.entries(row).map(([key, entry]) => [key, restoreRelayValue(entry)])
754
+ );
755
+ }
756
+ function redactRelayQuoteRaw(quote) {
757
+ return redactRelayValue(quote);
758
+ }
594
759
  function sanitizeRelayQuoteRaw(quote) {
595
- if (!quote.request) return quote;
596
- const request = { ...quote.request };
597
- delete request.headers;
598
- return { ...quote, request };
760
+ return sanitizeRelayValue(quote);
761
+ }
762
+ function restoreRelayQuoteRaw(value) {
763
+ return restoreRelayValue(value);
599
764
  }
600
765
  async function resolveRelayChains(options, client, config = {}) {
601
766
  const preferInjectedClientChains = config.preferInjectedClientChains ?? true;
@@ -609,19 +774,38 @@ function relayQuoteFromExecute(input, quote) {
609
774
  const currencyOut = asRecord(details.currencyOut);
610
775
  const sourceCurrency = asRecord(currencyIn.currency);
611
776
  const destinationCurrency = asRecord(currencyOut.currency);
612
- const source = normalizeToken(input.source.chainId, sourceCurrency) ?? {
613
- chainId: input.source.chainId,
614
- address: input.source.currency,
615
- symbol: "TOKEN",
616
- decimals: 0
617
- };
618
- const destination = normalizeToken(BASE_CHAIN_ID, destinationCurrency) ?? BASE_USDC_ASSET;
777
+ const sourceChainId = asNumber(sourceCurrency.chainId);
778
+ const sourceAddress = asString(sourceCurrency.address);
779
+ const sender = asString(details.sender);
780
+ const recipient = asString(details.recipient);
781
+ const expectedRecipient = input.recipient ?? input.user;
782
+ const destinationChainId = asNumber(destinationCurrency.chainId);
783
+ const destinationAddress = asString(destinationCurrency.address);
784
+ if (sourceChainId !== input.source.chainId || sourceAddress?.toLowerCase() !== input.source.currency.toLowerCase()) {
785
+ throw new Error("Relay quote source does not match the requested asset");
786
+ }
787
+ if (!sender || sender.toLowerCase() !== input.user.toLowerCase()) {
788
+ throw new Error("Relay quote sender does not match the requested wallet");
789
+ }
790
+ if (!recipient || recipient.toLowerCase() !== expectedRecipient.toLowerCase()) {
791
+ throw new Error("Relay quote recipient does not match the requested Base recipient");
792
+ }
793
+ if (destinationChainId !== BASE_CHAIN_ID || destinationAddress?.toLowerCase() !== BASE_USDC_ADDRESS.toLowerCase()) {
794
+ throw new Error("Relay quote destination is not canonical Base USDC");
795
+ }
796
+ const source = normalizeToken(input.source.chainId, sourceCurrency);
797
+ if (!source) throw new Error("Relay quote source metadata is malformed");
798
+ const destination = normalizeToken(destinationChainId, destinationCurrency);
799
+ if (!destination) throw new Error("Relay quote destination metadata is malformed");
619
800
  const txs = quote.steps.flatMap(
620
801
  (step) => step.items.map((item) => normalizeTx(item.data, input.source.chainId)).filter((tx) => tx !== null)
621
802
  );
622
- const outputAmount = BigInt(
623
- String(currencyOut.minimumAmount ?? currencyOut.amount ?? input.amount.toString())
624
- );
803
+ const rawOutputAmount = currencyOut.minimumAmount ?? currencyOut.amount;
804
+ if (rawOutputAmount === void 0 || rawOutputAmount === null) {
805
+ throw new Error("Relay quote is missing an output amount");
806
+ }
807
+ const outputAmount = BigInt(String(rawOutputAmount));
808
+ if (outputAmount <= 0n) throw new Error("Relay quote output amount must be positive");
625
809
  const requestId = quoteRequestId(quote);
626
810
  const rate = asNumber(details.rate);
627
811
  const timeEstimateSeconds = asNumber(details.timeEstimate);
@@ -629,92 +813,135 @@ function relayQuoteFromExecute(input, quote) {
629
813
  ...requestId ? { requestId } : {},
630
814
  source,
631
815
  destination,
632
- inputAmount: BigInt(String(currencyIn.amount ?? input.amount.toString())),
816
+ inputAmount: BigInt(String(currencyIn.amount)),
633
817
  outputAmount,
634
818
  ...rate !== void 0 ? { rate } : {},
635
819
  ...timeEstimateSeconds !== void 0 ? { timeEstimateSeconds } : {},
636
820
  ...quote.fees !== void 0 ? { fees: quote.fees } : {},
637
821
  txs,
638
- raw: sanitizeRelayQuoteRaw(quote)
822
+ raw: redactRelayQuoteRaw(quote)
639
823
  };
640
824
  }
641
825
  async function readRelaySourceCapabilities(options = {}) {
642
- const client = relayClient(options);
643
- const chains = await resolveRelayChains(options, client);
644
- return {
645
- destination: BASE_USDC_ASSET,
646
- chains: chains.map(normalizeChain).filter((chain) => chain.tokens.length > 0 && isSupportedEvmChain(chain)).sort((a, b) => a.displayName.localeCompare(b.displayName)),
647
- source: "relay-sdk",
648
- asOf: Math.floor(Date.now() / 1e3)
649
- };
826
+ try {
827
+ const client = relayClient(options);
828
+ const chains = await resolveRelayChains(options, client);
829
+ return {
830
+ destination: BASE_USDC_ASSET,
831
+ chains: chains.map(normalizeChain).filter((chain) => chain.tokens.length > 0 && isExecutableSourceChain(chain)).sort((a, b) => a.displayName.localeCompare(b.displayName)),
832
+ source: "relay-sdk",
833
+ asOf: Math.floor(Date.now() / 1e3)
834
+ };
835
+ } catch (err) {
836
+ if (isCashError(err)) throw err;
837
+ throw errors.sourceCapabilitiesFailed(err);
838
+ }
650
839
  }
651
840
  async function quoteRelayToBaseUsdc(input, options = {}) {
652
- const client = relayClient(options);
653
- const quote = await client.actions.getQuote(
654
- {
655
- chainId: input.source.chainId,
656
- currency: input.source.currency,
657
- toChainId: BASE_CHAIN_ID,
658
- toCurrency: BASE_USDC_ADDRESS,
659
- user: input.user,
660
- recipient: input.recipient ?? input.user,
661
- amount: input.amount.toString(),
662
- tradeType: input.tradeType ?? "EXACT_INPUT"
663
- },
664
- false
665
- );
666
- return relayQuoteFromExecute(input, quote);
841
+ try {
842
+ if (input.amount <= 0n) throw new Error("Relay quote amount must be positive");
843
+ const client = relayClient(options);
844
+ const quote = await client.actions.getQuote(
845
+ {
846
+ chainId: input.source.chainId,
847
+ currency: input.source.currency,
848
+ toChainId: BASE_CHAIN_ID,
849
+ toCurrency: BASE_USDC_ADDRESS,
850
+ user: input.user,
851
+ recipient: input.recipient ?? input.user,
852
+ amount: input.amount.toString(),
853
+ tradeType: input.tradeType ?? "EXACT_INPUT"
854
+ },
855
+ false
856
+ );
857
+ return relayQuoteFromExecute(input, quote);
858
+ } catch (err) {
859
+ if (isCashError(err)) throw err;
860
+ throw errors.sourceQuoteFailed(err);
861
+ }
667
862
  }
668
863
  async function executeRelayQuote(quote, wallet, options = {}) {
669
- const client = relayClient(options.relay);
670
- const sourceChainId = quoteSourceChainId(quote);
671
- if (sourceChainId !== void 0 && !(client.chains ?? []).some((chain) => chain.id === sourceChainId)) {
672
- await resolveRelayChains(options.relay ?? {}, client, { preferInjectedClientChains: false });
673
- }
674
- const { data } = await client.actions.execute({
675
- quote,
676
- wallet,
677
- ...options.onProgress ? { onProgress: options.onProgress } : {},
678
- ...options.disableCapabilitiesCheck !== void 0 ? { disableCapabilitiesCheck: options.disableCapabilitiesCheck } : {}
679
- });
680
- const requestId = quoteRequestId(data);
681
- return {
682
- ...requestId ? { requestId } : {},
683
- txHashes: data.steps.flatMap(
684
- (step) => step.items.flatMap((item) => (item.txHashes ?? []).map((tx) => tx.txHash))
685
- ),
686
- quote: data
687
- };
864
+ let observedRequestId;
865
+ let observedTransactions = { origin: [], destination: [] };
866
+ try {
867
+ const rawQuote = "raw" in quote ? quote.raw : quote;
868
+ observedRequestId = quoteRequestId(rawQuote);
869
+ assertCanonicalRelayDestination(rawQuote);
870
+ await assertRelayExecutionIdentity(rawQuote, wallet, options.recipient);
871
+ const client = relayClient(options.relay);
872
+ const sourceChainId = quoteSourceChainId(rawQuote);
873
+ if (sourceChainId !== void 0 && !(client.chains ?? []).some((chain) => chain.id === sourceChainId)) {
874
+ await resolveRelayChains(options.relay ?? {}, client, { preferInjectedClientChains: false });
875
+ }
876
+ const onProgress = (data2) => {
877
+ const progressSteps = Array.isArray(data2.steps) ? data2.steps : [];
878
+ observedRequestId = progressSteps.map((step) => step.requestId).find((id) => id !== void 0) ?? observedRequestId;
879
+ observedTransactions = collectRelayTransactions(progressSteps, sourceChainId);
880
+ if (options.onProgress) {
881
+ try {
882
+ options.onProgress(data2);
883
+ } catch {
884
+ }
885
+ }
886
+ };
887
+ const { data } = await client.actions.execute({
888
+ quote: rawQuote,
889
+ wallet,
890
+ onProgress,
891
+ ...options.disableCapabilitiesCheck !== void 0 ? { disableCapabilitiesCheck: options.disableCapabilitiesCheck } : {}
892
+ });
893
+ const requestId = quoteRequestId(data) ?? observedRequestId;
894
+ const transactions = collectRelayTransactions(data.steps, sourceChainId);
895
+ return {
896
+ ...requestId ? { requestId } : {},
897
+ txHashes: relayTransactionHashes(transactions),
898
+ transactions,
899
+ quote: redactRelayQuoteRaw(data)
900
+ };
901
+ } catch (err) {
902
+ if (isCashError(err)) throw err;
903
+ const txHashes = relayTransactionHashes(observedTransactions);
904
+ throw errors.sourceExecutionFailed(err, {
905
+ ...observedRequestId ? { requestId: observedRequestId } : {},
906
+ txHashes,
907
+ ...txHashes.length > 0 ? { transactions: observedTransactions } : {}
908
+ });
909
+ }
688
910
  }
689
911
  async function readRelayStatus(requestId, options = {}) {
690
- const client = relayClient(options);
691
- const response = await client.utils.request({
692
- url: `${client.baseApiUrl}/intents/status/v3`,
693
- method: "get",
694
- params: { requestId }
695
- });
696
- const root = asRecord(response.data);
697
- const status = asString(root.status);
698
- if (status !== "refund" && status !== "waiting" && status !== "depositing" && status !== "failure" && status !== "pending" && status !== "submitted" && status !== "success") {
699
- throw new Error(`Relay returned unknown status: ${String(root.status)}`);
700
- }
701
- const details = asString(root.details);
702
- const updatedAt = asNumber(root.updatedAt);
703
- const originChainId = asNumber(root.originChainId);
704
- const destinationChainId = asNumber(root.destinationChainId);
705
- const quoteCreatedAt = asNumber(root.quoteCreatedAt);
706
- return {
707
- requestId,
708
- status,
709
- ...details ? { details } : {},
710
- inTxHashes: Array.isArray(root.inTxHashes) ? root.inTxHashes.map(String) : [],
711
- txHashes: Array.isArray(root.txHashes) ? root.txHashes.map(String) : [],
712
- ...updatedAt !== void 0 ? { updatedAt } : {},
713
- ...originChainId !== void 0 ? { originChainId } : {},
714
- ...destinationChainId !== void 0 ? { destinationChainId } : {},
715
- ...quoteCreatedAt !== void 0 ? { quoteCreatedAt } : {},
716
- raw: response.data
717
- };
912
+ try {
913
+ const client = relayClient(options);
914
+ const response = await client.utils.request({
915
+ url: `${client.baseApiUrl}/intents/status/v3`,
916
+ method: "get",
917
+ params: { requestId }
918
+ });
919
+ const root = asRecord(response.data);
920
+ const status = asString(root.status);
921
+ if (status !== "refund" && status !== "waiting" && status !== "depositing" && status !== "failure" && status !== "pending" && status !== "submitted" && status !== "success") {
922
+ throw new Error(`Relay returned unknown status: ${String(root.status)}`);
923
+ }
924
+ const details = asString(root.details);
925
+ const updatedAt = asNumber(root.updatedAt);
926
+ const originChainId = asNumber(root.originChainId);
927
+ const destinationChainId = asNumber(root.destinationChainId);
928
+ const quoteCreatedAt = asNumber(root.quoteCreatedAt);
929
+ return {
930
+ requestId,
931
+ status,
932
+ ...details ? { details } : {},
933
+ inTxHashes: Array.isArray(root.inTxHashes) ? root.inTxHashes.map(String) : [],
934
+ txHashes: Array.isArray(root.txHashes) ? root.txHashes.map(String) : [],
935
+ ...updatedAt !== void 0 ? { updatedAt } : {},
936
+ ...originChainId !== void 0 ? { originChainId } : {},
937
+ ...destinationChainId !== void 0 ? { destinationChainId } : {},
938
+ ...quoteCreatedAt !== void 0 ? { quoteCreatedAt } : {},
939
+ raw: response.data
940
+ };
941
+ } catch (err) {
942
+ if (isCashError(err)) throw err;
943
+ throw errors.sourceStatusFailed(requestId, err);
944
+ }
718
945
  }
719
946
 
720
947
  // src/client/estimate.ts
@@ -761,11 +988,16 @@ async function readEstimate(publicClient, input, context = {}) {
761
988
  if (!feedConfig || feedConfig.feed.toLowerCase() === ZERO_ADDRESS) {
762
989
  rate = 1;
763
990
  } else {
764
- const result = await publicClient.readContract({
765
- address: feedConfig.feed,
766
- abi: CHAINLINK_LATEST_ROUND_ABI,
767
- functionName: "latestRoundData"
768
- });
991
+ let result;
992
+ try {
993
+ result = await publicClient.readContract({
994
+ address: feedConfig.feed,
995
+ abi: CHAINLINK_LATEST_ROUND_ABI,
996
+ functionName: "latestRoundData"
997
+ });
998
+ } catch (err) {
999
+ throw errors.oracleReadFailed(currency, err);
1000
+ }
769
1001
  const answer = Number(result[1]);
770
1002
  const price = answer / 10 ** feedConfig.decimals;
771
1003
  if (!Number.isFinite(price) || price <= 0) {
@@ -811,6 +1043,7 @@ async function readEstimate(publicClient, input, context = {}) {
811
1043
  var DEFAULT_RPC_URL = "https://mainnet.base.org";
812
1044
  var CASH_ATTRIBUTION_CODE = "peer-cash";
813
1045
  var DEFAULT_CURATOR_URLS = {
1046
+ preproduction: "https://api-preprod.zkp2p.xyz",
814
1047
  staging: "https://api-staging.zkp2p.xyz"
815
1048
  };
816
1049
  var ERC20_APPROVE_ABI = parseAbi([
@@ -845,12 +1078,29 @@ async function submitAndConfirm(client, verb, send) {
845
1078
  try {
846
1079
  hash = await send();
847
1080
  } catch (err) {
848
- throw mapChainError(verb, err);
1081
+ const mapped = mapChainError(verb, err);
1082
+ if (isKnownPreBroadcastFailure(err, mapped)) throw mapped;
1083
+ throw errors.transactionSubmissionUnknown(verb, err, {
1084
+ kind: "inspect-base-operation-submission",
1085
+ operation: verb
1086
+ });
1087
+ }
1088
+ let receipt;
1089
+ try {
1090
+ receipt = await client.publicClient.waitForTransactionReceipt({ hash });
1091
+ } catch (err) {
1092
+ throw errors.transactionStatusUnknown(hash, err, verb);
849
1093
  }
850
- const receipt = await client.publicClient.waitForTransactionReceipt({ hash });
851
1094
  if (receipt.status === "reverted") throw errors.transactionFailed(hash);
852
1095
  return hash;
853
1096
  }
1097
+ function isKnownPreBroadcastFailure(err, mapped) {
1098
+ if (mapped.code === "INSUFFICIENT_TOKEN_BALANCE" || mapped.code === "ALLOWANCE_NOT_VISIBLE" || mapped.code === "ESCROW_PAUSED") {
1099
+ return true;
1100
+ }
1101
+ const message = err instanceof Error ? err.message : String(err);
1102
+ return /user rejected|user denied|rejected request|action_rejected/i.test(message);
1103
+ }
854
1104
  function depositOrderOptions(deposit) {
855
1105
  const remaining = toBigIntOrUndefined(deposit.remainingDeposits);
856
1106
  const outstanding = toBigIntOrUndefined(deposit.outstandingIntentAmount);
@@ -890,9 +1140,10 @@ function createCashClient(options) {
890
1140
  }
891
1141
  const readClient = buildSdkClient(createWalletClient({ chain: base, transport }));
892
1142
  const signingClients = /* @__PURE__ */ new WeakMap();
893
- function signingClient(verb, opts) {
1143
+ async function signingClient(verb, opts) {
894
1144
  const signer = opts?.signer;
895
1145
  if (!signer?.account) throw errors.signerRequired(verb);
1146
+ await assertWalletChainId(signer, BASE_CHAIN_ID, verb);
896
1147
  let client = signingClients.get(signer);
897
1148
  if (!client) {
898
1149
  client = buildSdkClient(signer);
@@ -902,13 +1153,15 @@ function createCashClient(options) {
902
1153
  }
903
1154
  function validatePayout(input) {
904
1155
  const { receive } = input;
905
- const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
906
- if (!catalog[receive.platform]) throw errors.unsupportedPlatform(receive.platform);
1156
+ const platform = buildCapabilities(environment).platforms.find(
1157
+ (capability) => capability.platform === receive.platform
1158
+ );
1159
+ if (!platform) throw errors.unsupportedPlatform(receive.platform);
907
1160
  if (!isMarketRateSupported(receive.currency)) {
908
1161
  throw errors.oracleUnsupportedCurrency(receive.currency);
909
1162
  }
910
- if (platformRequiresIdentityAttestation(receive.platform) && !receive.payee.identityAttestation) {
911
- throw errors.payeeVerificationRequired(receive.platform);
1163
+ if (!platform.currencies.includes(receive.currency)) {
1164
+ throw errors.unsupportedPlatformCurrency(receive.platform, receive.currency);
912
1165
  }
913
1166
  return {
914
1167
  payouts: [
@@ -917,15 +1170,22 @@ function createCashClient(options) {
917
1170
  currency: receive.currency,
918
1171
  payeeData: receive.payee
919
1172
  }
920
- ],
921
- ...input.intentAmountRange ? { intentAmountRange: input.intentAmountRange } : {}
1173
+ ]
922
1174
  };
923
1175
  }
924
- function validateInput(input) {
925
- if (input.amount < MIN_CASHOUT_AMOUNT) {
926
- throw errors.amountBelowMinimum(input.amount, MIN_CASHOUT_AMOUNT);
1176
+ function validateDepositInput(amount, input, payoutInput = validatePayout(input)) {
1177
+ if (amount < MIN_CASHOUT_AMOUNT) {
1178
+ throw errors.amountBelowMinimum(amount, MIN_CASHOUT_AMOUNT);
927
1179
  }
928
- return { amount: input.amount, ...validatePayout(input) };
1180
+ const range = input.intentAmountRange;
1181
+ if (range && (range.min <= 0n || range.max < range.min || range.max > amount)) {
1182
+ throw errors.invalidIntentAmountRange(amount, range.min, range.max);
1183
+ }
1184
+ return {
1185
+ amount,
1186
+ ...payoutInput,
1187
+ ...range ? { intentAmountRange: range } : {}
1188
+ };
929
1189
  }
930
1190
  async function buildDepositParams(client, depositInput) {
931
1191
  try {
@@ -940,32 +1200,54 @@ function createCashClient(options) {
940
1200
  throw errors.payeeRegistrationFailed(err);
941
1201
  }
942
1202
  }
1203
+ function parseDepositId(depositId) {
1204
+ try {
1205
+ const parsed = parseCompositeDepositId(depositId);
1206
+ return {
1207
+ ...parsed,
1208
+ compositeId: createCompositeDepositId(parsed.escrowAddress, parsed.onchainDepositId)
1209
+ };
1210
+ } catch (err) {
1211
+ throw errors.invalidDepositId(depositId, err);
1212
+ }
1213
+ }
943
1214
  async function fetchOrder(depositId) {
944
- const deposits = await readClient.indexer.getDepositsByIdsWithRelations([depositId], {
945
- includeIntents: true,
946
- intentStatuses: CASH_ORDER_STATUSES
947
- });
1215
+ const { compositeId } = parseDepositId(depositId);
1216
+ let deposits;
1217
+ try {
1218
+ deposits = await readClient.indexer.getDepositsByIdsWithRelations([compositeId], {
1219
+ includeIntents: true,
1220
+ intentStatuses: CASH_ORDER_STATUSES
1221
+ });
1222
+ } catch (err) {
1223
+ throw errors.indexerUnavailable("order", err);
1224
+ }
948
1225
  const deposit = deposits[0];
949
1226
  if (!deposit) {
950
- const intents = await readClient.indexer.getIntentsForDeposits(
951
- [depositId],
952
- CASH_ORDER_STATUSES
953
- );
954
- if (intents.length === 0) throw errors.orderNotFound(depositId);
955
- return deriveCashOrder(depositId, intents);
1227
+ let intents;
1228
+ try {
1229
+ intents = await readClient.indexer.getIntentsForDeposits(
1230
+ [compositeId],
1231
+ CASH_ORDER_STATUSES
1232
+ );
1233
+ } catch (err) {
1234
+ throw errors.indexerUnavailable("order intents", err);
1235
+ }
1236
+ if (intents.length === 0) throw errors.orderNotFound(compositeId);
1237
+ return deriveCashOrder(compositeId, intents);
956
1238
  }
957
1239
  const payouts = derivePayouts(
958
1240
  deposit.paymentMethods ?? [],
959
1241
  deposit.currencies ?? [],
960
1242
  getPaymentMethodsCatalog(BASE_CHAIN_ID, environment)
961
1243
  );
962
- return deriveCashOrder(depositId, deposit.intents ?? [], {
1244
+ return deriveCashOrder(compositeId, deposit.intents ?? [], {
963
1245
  ...depositOrderOptions(deposit),
964
1246
  ...payouts.length > 0 ? { payouts } : {}
965
1247
  });
966
1248
  }
967
1249
  function escrowContext(depositId) {
968
- const { escrowAddress, onchainDepositId } = parseCompositeDepositId(depositId);
1250
+ const { escrowAddress, onchainDepositId } = parseDepositId(depositId);
969
1251
  return {
970
1252
  onchainDepositId,
971
1253
  escrowArg: escrowAddress ? { escrowAddress } : {}
@@ -980,7 +1262,7 @@ function createCashClient(options) {
980
1262
  const signaled = order.fills.filter((f) => f.status === "SIGNALED");
981
1263
  const liveIntent = signaled.some((f) => isFillLive(f, nowSeconds));
982
1264
  const expiredIntent = signaled.length > 0 && !liveIntent;
983
- if (order.pendingAmount > 0n && liveIntent) {
1265
+ if (liveIntent || order.pendingAmount > 0n && signaled.length === 0) {
984
1266
  throw errors.activeIntentBlocksWithdrawal(depositId);
985
1267
  }
986
1268
  if (availableAmount(order) <= 0n && order.pendingAmount === 0n) {
@@ -1021,22 +1303,32 @@ function createCashClient(options) {
1021
1303
  txOverrides: attribution
1022
1304
  });
1023
1305
  } catch (err) {
1024
- throw mapChainError("approve", err);
1306
+ throw mapChainError("approve", err, { requiredAmount: amount });
1025
1307
  }
1026
1308
  if (allowance.hadAllowance || !allowance.hash) return;
1027
- const receipt = await client.publicClient.waitForTransactionReceipt({ hash: allowance.hash });
1309
+ let receipt;
1310
+ try {
1311
+ receipt = await client.publicClient.waitForTransactionReceipt({ hash: allowance.hash });
1312
+ } catch (err) {
1313
+ throw errors.transactionStatusUnknown(allowance.hash, err, "approve");
1314
+ }
1028
1315
  if (receipt.status === "reverted") throw errors.transactionFailed(allowance.hash);
1316
+ let lastReadError;
1029
1317
  for (let attempt = 0; attempt < 15; attempt++) {
1030
- const visible = await client.publicClient.readContract({
1031
- address: token,
1032
- abi: ERC20_ALLOWANCE_ABI,
1033
- functionName: "allowance",
1034
- args: [owner, escrow]
1035
- });
1036
- if (visible >= amount) return;
1318
+ try {
1319
+ const visible = await client.publicClient.readContract({
1320
+ address: token,
1321
+ abi: ERC20_ALLOWANCE_ABI,
1322
+ functionName: "allowance",
1323
+ args: [owner, escrow]
1324
+ });
1325
+ if (visible >= amount) return;
1326
+ } catch (err) {
1327
+ lastReadError = err;
1328
+ }
1037
1329
  await sleep(1e3);
1038
1330
  }
1039
- throw errors.allowanceNotVisible(amount);
1331
+ throw errors.allowanceNotVisible(amount, lastReadError);
1040
1332
  }
1041
1333
  return {
1042
1334
  capabilities,
@@ -1047,8 +1339,10 @@ function createCashClient(options) {
1047
1339
  return quoteRelayToBaseUsdc(input, options.relay);
1048
1340
  },
1049
1341
  async executeSourceQuote(quote, opts) {
1342
+ if (!opts.signer.account) throw errors.signerRequired("executeSourceQuote");
1050
1343
  return executeRelayQuote(quote, opts.signer, {
1051
1344
  ...options.relay ? { relay: options.relay } : {},
1345
+ ...opts.recipient ? { recipient: opts.recipient } : {},
1052
1346
  ...opts.onProgress ? { onProgress: opts.onProgress } : {},
1053
1347
  ...opts.disableCapabilitiesCheck !== void 0 ? { disableCapabilitiesCheck: opts.disableCapabilitiesCheck } : {}
1054
1348
  });
@@ -1064,7 +1358,7 @@ function createCashClient(options) {
1064
1358
  });
1065
1359
  },
1066
1360
  async cashout(input, opts) {
1067
- const client = signingClient("cashout", opts);
1361
+ const client = await signingClient("cashout", opts);
1068
1362
  const owner = opts.signer.account.address;
1069
1363
  const payoutInput = validatePayout(input);
1070
1364
  let sourceResult;
@@ -1072,6 +1366,7 @@ function createCashClient(options) {
1072
1366
  if (input.source) {
1073
1367
  const sourceSigner = opts.sourceSigner ?? (input.source.chainId === BASE_CHAIN_ID ? opts.signer : void 0);
1074
1368
  if (!sourceSigner?.account) throw errors.signerRequired("source cashout");
1369
+ await assertWalletChainId(sourceSigner, input.source.chainId, "source cashout");
1075
1370
  if (input.source.recipient !== void 0 && input.source.recipient.toLowerCase() !== owner.toLowerCase()) {
1076
1371
  throw errors.sourceRecipientMismatch(input.source.recipient, owner);
1077
1372
  }
@@ -1089,20 +1384,23 @@ function createCashClient(options) {
1089
1384
  throw errors.amountBelowMinimum(relayQuote.outputAmount, MIN_CASHOUT_AMOUNT);
1090
1385
  }
1091
1386
  cashoutAmount = relayQuote.outputAmount;
1092
- const depositInput2 = { amount: cashoutAmount, ...payoutInput };
1387
+ const depositInput2 = validateDepositInput(cashoutAmount, input, payoutInput);
1093
1388
  const params2 = await buildDepositParams(client, depositInput2);
1094
1389
  const escrow2 = client.escrowV2Address ?? client.escrowAddress;
1095
1390
  await settleAllowance(client, params2.token, owner, escrow2, depositInput2.amount);
1096
1391
  const executed = await executeRelayQuote(relayQuote.raw, sourceSigner, {
1097
1392
  ...options.relay ? { relay: options.relay } : {},
1393
+ recipient: owner,
1098
1394
  ...opts.onSourceProgress ? { onProgress: opts.onSourceProgress } : {},
1099
1395
  ...opts.disableSourceCapabilitiesCheck !== void 0 ? { disableCapabilitiesCheck: opts.disableSourceCapabilitiesCheck } : {}
1100
1396
  });
1101
- sourceResult = {
1397
+ const routedSource = {
1102
1398
  amount: cashoutAmount,
1103
1399
  ...executed.requestId ? { requestId: executed.requestId } : {},
1104
- txHashes: executed.txHashes
1400
+ txHashes: executed.txHashes,
1401
+ ...executed.transactions ? { transactions: executed.transactions } : {}
1105
1402
  };
1403
+ sourceResult = routedSource;
1106
1404
  const attributedParams2 = { ...params2, txOverrides: attribution };
1107
1405
  const send2 = async () => {
1108
1406
  try {
@@ -1119,10 +1417,26 @@ function createCashClient(options) {
1119
1417
  try {
1120
1418
  hash2 = await send2();
1121
1419
  } catch (err) {
1122
- throw mapChainError("createDeposit", err);
1420
+ const mapped = mapChainError("createDeposit", err, {
1421
+ requiredAmount: depositInput2.amount
1422
+ });
1423
+ if (isKnownPreBroadcastFailure(err, mapped)) {
1424
+ throw errors.sourceRouteCompletedCashoutFailed(routedSource, mapped);
1425
+ }
1426
+ throw errors.sourceCashoutSubmissionUnknown(routedSource, owner, mapped);
1427
+ }
1428
+ let receipt2;
1429
+ try {
1430
+ receipt2 = await client.publicClient.waitForTransactionReceipt({ hash: hash2 });
1431
+ } catch (err) {
1432
+ throw errors.sourceCashoutStatusUnknown(routedSource, hash2, err);
1433
+ }
1434
+ if (receipt2.status === "reverted") {
1435
+ throw errors.sourceRouteCompletedCashoutFailed(
1436
+ routedSource,
1437
+ errors.transactionFailed(hash2)
1438
+ );
1123
1439
  }
1124
- const receipt2 = await client.publicClient.waitForTransactionReceipt({ hash: hash2 });
1125
- if (receipt2.status === "reverted") throw errors.transactionFailed(hash2);
1126
1440
  const abi2 = client.escrowV2Abi ?? client.escrowAbi;
1127
1441
  const resolved2 = resolveCashDepositId({ logs: receipt2.logs, abi: abi2 });
1128
1442
  if (!resolved2) throw errors.depositResolutionFailed(hash2);
@@ -1136,10 +1450,10 @@ function createCashClient(options) {
1136
1450
  escrowAddress: resolved2.escrowAddress,
1137
1451
  onchainDepositId: resolved2.onchainDepositId,
1138
1452
  order: order2,
1139
- source: sourceResult
1453
+ source: routedSource
1140
1454
  };
1141
1455
  }
1142
- const depositInput = validateInput(input);
1456
+ const depositInput = validateDepositInput(input.amount, input, payoutInput);
1143
1457
  const params = await buildDepositParams(client, depositInput);
1144
1458
  const escrow = client.escrowV2Address ?? client.escrowAddress;
1145
1459
  await settleAllowance(client, params.token, owner, escrow, depositInput.amount);
@@ -1159,9 +1473,23 @@ function createCashClient(options) {
1159
1473
  try {
1160
1474
  hash = await send();
1161
1475
  } catch (err) {
1162
- throw mapChainError("createDeposit", err);
1476
+ const mapped = mapChainError("createDeposit", err, {
1477
+ requiredAmount: depositInput.amount
1478
+ });
1479
+ if (isKnownPreBroadcastFailure(err, mapped)) throw mapped;
1480
+ throw errors.transactionSubmissionUnknown("cashout", err, {
1481
+ kind: "inspect-base-cashout-submission",
1482
+ amount: depositInput.amount.toString(),
1483
+ depositor: owner,
1484
+ txHashes: []
1485
+ });
1486
+ }
1487
+ let receipt;
1488
+ try {
1489
+ receipt = await client.publicClient.waitForTransactionReceipt({ hash });
1490
+ } catch (err) {
1491
+ throw errors.transactionStatusUnknown(hash, err, "cashout");
1163
1492
  }
1164
- const receipt = await client.publicClient.waitForTransactionReceipt({ hash });
1165
1493
  if (receipt.status === "reverted") throw errors.transactionFailed(hash);
1166
1494
  const abi = client.escrowV2Abi ?? client.escrowAbi;
1167
1495
  const resolved = resolveCashDepositId({ logs: receipt.logs, abi });
@@ -1181,7 +1509,7 @@ function createCashClient(options) {
1181
1509
  },
1182
1510
  async prepare(input) {
1183
1511
  if (input.source) throw errors.sourceRouteUnsupportedInPrepare();
1184
- const depositInput = validateInput(input);
1512
+ const depositInput = validateDepositInput(input.amount, input);
1185
1513
  const params = await buildDepositParams(readClient, depositInput);
1186
1514
  const { prepared } = await readClient.prepareCreateDeposit({
1187
1515
  ...params,
@@ -1220,13 +1548,46 @@ function createCashClient(options) {
1220
1548
  return fetchOrder(depositId);
1221
1549
  },
1222
1550
  async buyer(address) {
1223
- const intents = await readClient.indexer.getOwnerIntents(address, CASH_ORDER_STATUSES);
1551
+ let intents;
1552
+ try {
1553
+ intents = await readClient.indexer.getOwnerIntents(address, CASH_ORDER_STATUSES);
1554
+ } catch (err) {
1555
+ throw errors.indexerUnavailable("buyer profile", err);
1556
+ }
1224
1557
  return deriveBuyerProfile(address, intents);
1225
1558
  },
1226
1559
  async orders(owner, opts = {}) {
1227
1560
  const { inFlight = false, limit = 100 } = opts;
1228
- const deposits = await readClient.indexer.getDeposits({ depositor: owner }, { limit });
1229
- 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));
1561
+ let deposits;
1562
+ try {
1563
+ deposits = await readClient.indexer.getDepositsWithRelations(
1564
+ { depositor: owner },
1565
+ { limit }
1566
+ );
1567
+ } catch (err) {
1568
+ throw errors.indexerUnavailable("orders", err);
1569
+ }
1570
+ const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
1571
+ const derived = deposits.flatMap((deposit) => {
1572
+ if (deposit.token.toLowerCase() !== BASE_USDC_ADDRESS.toLowerCase()) return [];
1573
+ const payouts = derivePayouts(
1574
+ deposit.paymentMethods ?? [],
1575
+ deposit.currencies ?? [],
1576
+ catalog
1577
+ );
1578
+ if (payouts.length !== 1 || !payouts.every((payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0)) {
1579
+ return [];
1580
+ }
1581
+ return [
1582
+ deriveCashOrder(deposit.id, [], {
1583
+ ...depositOrderOptions(deposit),
1584
+ payouts,
1585
+ // List rows carry no intent detail - a positive outstanding
1586
+ // amount is treated conservatively as a live lock.
1587
+ fillsIncluded: false
1588
+ })
1589
+ ];
1590
+ }).filter((o) => o.totalAmount >= MIN_CASHOUT_AMOUNT).sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
1230
1591
  return inFlight ? derived.filter((o) => o.isInFlight) : derived;
1231
1592
  },
1232
1593
  async *watch(depositId, opts = {}) {
@@ -1256,7 +1617,7 @@ function createCashClient(options) {
1256
1617
  }
1257
1618
  },
1258
1619
  async withdraw(depositId, opts) {
1259
- const client = signingClient("withdraw", opts);
1620
+ const client = await signingClient("withdraw", opts);
1260
1621
  if (opts.amount !== void 0) {
1261
1622
  const { onchainDepositId: onchainDepositId2, escrowArg: escrowArg2 } = await partialWithdrawContext(
1262
1623
  depositId,
@@ -1354,7 +1715,7 @@ function createCashClient(options) {
1354
1715
  return { txs, steps };
1355
1716
  },
1356
1717
  async topUp(depositId, amount, opts) {
1357
- const client = signingClient("topUp", opts);
1718
+ const client = await signingClient("topUp", opts);
1358
1719
  const { onchainDepositId, escrowArg } = await topUpContext(depositId, amount);
1359
1720
  const owner = opts.signer.account.address;
1360
1721
  const escrow = escrowArg.escrowAddress ?? client.escrowV2Address ?? client.escrowAddress;
@@ -1409,6 +1770,39 @@ function createCashClient(options) {
1409
1770
  };
1410
1771
  }
1411
1772
  var bigintString = z.string().regex(/^-?\d+$/, "expected a decimal bigint string");
1773
+ var nonNegativeBigintString = z.string().regex(/^\d+$/, "expected a non-negative decimal bigint string");
1774
+ var relayTransactionJsonSchema = z.object({
1775
+ hash: z.string(),
1776
+ chainId: z.number()
1777
+ });
1778
+ var relayTransactionsJsonSchema = z.object({
1779
+ origin: z.array(relayTransactionJsonSchema),
1780
+ destination: z.array(relayTransactionJsonSchema)
1781
+ }).strict();
1782
+ var cashAssetJsonSchema = z.object({
1783
+ chainId: z.number(),
1784
+ address: z.string(),
1785
+ symbol: z.string(),
1786
+ decimals: z.number(),
1787
+ name: z.string().optional(),
1788
+ isNative: z.boolean().optional()
1789
+ });
1790
+ var cashChainJsonSchema = z.object({
1791
+ id: z.number(),
1792
+ name: z.string(),
1793
+ displayName: z.string(),
1794
+ disabled: z.boolean(),
1795
+ depositEnabled: z.boolean(),
1796
+ blockProductionLagging: z.boolean(),
1797
+ vmType: z.string().optional(),
1798
+ tokens: z.array(cashAssetJsonSchema)
1799
+ });
1800
+ var cashSourceCapabilitiesJsonSchema = z.object({
1801
+ destination: cashAssetJsonSchema,
1802
+ chains: z.array(cashChainJsonSchema),
1803
+ source: z.literal("relay-sdk"),
1804
+ asOf: z.number()
1805
+ });
1412
1806
  var cashOrderStateSchema = z.enum([
1413
1807
  "awaiting-buyer",
1414
1808
  "matched",
@@ -1421,18 +1815,18 @@ var intentStatusSchema = z.enum(["SIGNALED", "FULFILLED", "PRUNED", "MANUALLY_RE
1421
1815
  var cashFillJsonSchema = z.object({
1422
1816
  intentHash: z.string(),
1423
1817
  status: intentStatusSchema,
1424
- amount: bigintString,
1818
+ amount: nonNegativeBigintString,
1425
1819
  buyer: z.string(),
1426
1820
  currency: z.string().optional(),
1427
1821
  currencyHash: z.string().optional(),
1428
1822
  rate: z.number().optional(),
1429
- conversionRate: bigintString.optional(),
1823
+ conversionRate: nonNegativeBigintString.optional(),
1430
1824
  fiatOwed: z.number().optional(),
1431
1825
  fiatPaid: z.number().optional(),
1432
1826
  paidCurrency: z.string().optional(),
1433
1827
  paymentId: z.string().optional(),
1434
1828
  paidAt: z.number().optional(),
1435
- releasedAmount: bigintString.optional(),
1829
+ releasedAmount: nonNegativeBigintString.optional(),
1436
1830
  fillLatencySeconds: z.number().optional(),
1437
1831
  isExpired: z.boolean().optional(),
1438
1832
  signaledAt: z.number().optional(),
@@ -1471,10 +1865,10 @@ var cashOrderJsonSchema = z.object({
1471
1865
  depositId: z.string(),
1472
1866
  state: cashOrderStateSchema,
1473
1867
  fills: z.array(cashFillJsonSchema),
1474
- totalAmount: bigintString,
1475
- filledAmount: bigintString,
1476
- pendingAmount: bigintString,
1477
- returnedAmount: bigintString,
1868
+ totalAmount: nonNegativeBigintString,
1869
+ filledAmount: nonNegativeBigintString,
1870
+ pendingAmount: nonNegativeBigintString,
1871
+ returnedAmount: nonNegativeBigintString,
1478
1872
  nextActions: z.array(cashNextActionSchema),
1479
1873
  primaryIntentHash: z.string().optional(),
1480
1874
  matchedAt: z.number().optional(),
@@ -1489,7 +1883,7 @@ var cashOrderJsonSchema = z.object({
1489
1883
  var cashEstimateJsonSchema = z.object({
1490
1884
  kind: z.literal("oracle-estimate"),
1491
1885
  currency: z.string(),
1492
- amount: bigintString,
1886
+ amount: nonNegativeBigintString,
1493
1887
  rate: z.number(),
1494
1888
  receiveAmount: z.number(),
1495
1889
  asOf: z.number(),
@@ -1505,7 +1899,7 @@ var cashEstimateJsonSchema = z.object({
1505
1899
  name: z.string().optional(),
1506
1900
  isNative: z.boolean().optional()
1507
1901
  }),
1508
- inputAmount: bigintString,
1902
+ inputAmount: nonNegativeBigintString,
1509
1903
  relayQuote: z.object({
1510
1904
  requestId: z.string().optional(),
1511
1905
  source: z.object({
@@ -1524,8 +1918,8 @@ var cashEstimateJsonSchema = z.object({
1524
1918
  name: z.string().optional(),
1525
1919
  isNative: z.boolean().optional()
1526
1920
  }),
1527
- inputAmount: bigintString,
1528
- outputAmount: bigintString,
1921
+ inputAmount: nonNegativeBigintString,
1922
+ outputAmount: nonNegativeBigintString,
1529
1923
  rate: z.number().optional(),
1530
1924
  timeEstimateSeconds: z.number().optional(),
1531
1925
  fees: z.unknown().optional(),
@@ -1533,7 +1927,7 @@ var cashEstimateJsonSchema = z.object({
1533
1927
  z.object({
1534
1928
  to: z.string(),
1535
1929
  data: z.string(),
1536
- value: bigintString,
1930
+ value: nonNegativeBigintString,
1537
1931
  chainId: z.number()
1538
1932
  })
1539
1933
  ),
@@ -1548,9 +1942,39 @@ var cashEstimateJsonSchema = z.object({
1548
1942
  var preparedTransactionJsonSchema = z.object({
1549
1943
  to: z.string(),
1550
1944
  data: z.string(),
1551
- value: bigintString,
1945
+ value: nonNegativeBigintString,
1552
1946
  chainId: z.number()
1553
1947
  });
1948
+ var relayQuoteJsonSchema = z.object({
1949
+ requestId: z.string().optional(),
1950
+ source: cashAssetJsonSchema,
1951
+ destination: cashAssetJsonSchema,
1952
+ inputAmount: nonNegativeBigintString,
1953
+ outputAmount: nonNegativeBigintString,
1954
+ rate: z.number().optional(),
1955
+ timeEstimateSeconds: z.number().optional(),
1956
+ fees: z.unknown().optional(),
1957
+ txs: z.array(preparedTransactionJsonSchema),
1958
+ raw: z.unknown()
1959
+ });
1960
+ var relayStatusJsonSchema = z.object({
1961
+ requestId: z.string(),
1962
+ status: z.enum(["refund", "waiting", "depositing", "failure", "pending", "submitted", "success"]),
1963
+ details: z.string().optional(),
1964
+ inTxHashes: z.array(z.string()),
1965
+ txHashes: z.array(z.string()),
1966
+ updatedAt: z.number().optional(),
1967
+ originChainId: z.number().optional(),
1968
+ destinationChainId: z.number().optional(),
1969
+ quoteCreatedAt: z.number().optional(),
1970
+ raw: z.unknown()
1971
+ });
1972
+ var relayExecutionResultJsonSchema = z.object({
1973
+ requestId: z.string().optional(),
1974
+ txHashes: z.array(z.string()),
1975
+ transactions: relayTransactionsJsonSchema.optional(),
1976
+ quote: z.unknown()
1977
+ });
1554
1978
  var cashPreparedStepJsonSchema = z.object({
1555
1979
  kind: z.enum([
1556
1980
  "approve",
@@ -1566,12 +1990,13 @@ var cashoutResultJsonSchema = z.object({
1566
1990
  depositId: z.string(),
1567
1991
  txHash: z.string(),
1568
1992
  escrowAddress: z.string(),
1569
- onchainDepositId: bigintString,
1993
+ onchainDepositId: nonNegativeBigintString,
1570
1994
  order: cashOrderJsonSchema,
1571
1995
  source: z.object({
1572
- amount: bigintString,
1996
+ amount: nonNegativeBigintString,
1573
1997
  requestId: z.string().optional(),
1574
- txHashes: z.array(z.string())
1998
+ txHashes: z.array(z.string()),
1999
+ transactions: relayTransactionsJsonSchema.optional()
1575
2000
  }).optional()
1576
2001
  });
1577
2002
  var prepareResultJsonSchema = z.object({
@@ -1601,39 +2026,7 @@ var cashCapabilitiesJsonSchema = z.object({
1601
2026
  chainId: z.number(),
1602
2027
  token: z.object({ address: z.string(), symbol: z.literal("USDC"), decimals: z.number() })
1603
2028
  }),
1604
- relay: z.object({
1605
- destination: z.object({
1606
- chainId: z.number(),
1607
- address: z.string(),
1608
- symbol: z.string(),
1609
- decimals: z.number(),
1610
- name: z.string().optional(),
1611
- isNative: z.boolean().optional()
1612
- }),
1613
- chains: z.array(
1614
- z.object({
1615
- id: z.number(),
1616
- name: z.string(),
1617
- displayName: z.string(),
1618
- disabled: z.boolean(),
1619
- depositEnabled: z.boolean(),
1620
- blockProductionLagging: z.boolean(),
1621
- vmType: z.string().optional(),
1622
- tokens: z.array(
1623
- z.object({
1624
- chainId: z.number(),
1625
- address: z.string(),
1626
- symbol: z.string(),
1627
- decimals: z.number(),
1628
- name: z.string().optional(),
1629
- isNative: z.boolean().optional()
1630
- })
1631
- )
1632
- })
1633
- ),
1634
- source: z.literal("relay-sdk"),
1635
- asOf: z.number()
1636
- }).optional()
2029
+ relay: cashSourceCapabilitiesJsonSchema.optional()
1637
2030
  }),
1638
2031
  platforms: z.array(
1639
2032
  z.object({
@@ -1644,15 +2037,98 @@ var cashCapabilitiesJsonSchema = z.object({
1644
2037
  })
1645
2038
  ),
1646
2039
  currencies: z.array(z.string()),
1647
- amount: z.object({ min: bigintString, recommendedMin: bigintString, max: z.null() }),
2040
+ amount: z.object({
2041
+ min: nonNegativeBigintString,
2042
+ recommendedMin: nonNegativeBigintString,
2043
+ max: z.null()
2044
+ }),
1648
2045
  pricing: z.object({ kind: z.literal("oracle-market-rate"), spreadBps: z.literal(0) })
1649
2046
  });
2047
+ function defineCashErrorCodes(codes) {
2048
+ return codes;
2049
+ }
2050
+ var CASH_ERROR_CODES = defineCashErrorCodes([
2051
+ "ORACLE_UNSUPPORTED_CURRENCY",
2052
+ "ORACLE_READ_FAILED",
2053
+ "UNSUPPORTED_PLATFORM",
2054
+ "UNSUPPORTED_PLATFORM_CURRENCY",
2055
+ "AMOUNT_BELOW_MINIMUM",
2056
+ "INVALID_INTENT_AMOUNT_RANGE",
2057
+ "ACTIVE_INTENT_BLOCKS_WITHDRAWAL",
2058
+ "NOTHING_TO_WITHDRAW",
2059
+ "INSUFFICIENT_AVAILABLE_FUNDS",
2060
+ "INSUFFICIENT_TOKEN_BALANCE",
2061
+ "ORDER_NOT_ACTIVE",
2062
+ "INVALID_DEPOSIT_ID",
2063
+ "ESCROW_PAUSED",
2064
+ "INDEXER_LAG",
2065
+ "INDEXER_UNAVAILABLE",
2066
+ "ORDER_NOT_FOUND",
2067
+ "PAYEE_REGISTRATION_FAILED",
2068
+ "PAYEE_VERIFICATION_REQUIRED",
2069
+ "SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE",
2070
+ "SOURCE_RECIPIENT_MISMATCH",
2071
+ "SOURCE_CAPABILITIES_FAILED",
2072
+ "SOURCE_QUOTE_FAILED",
2073
+ "SOURCE_EXECUTION_FAILED",
2074
+ "SOURCE_STATUS_FAILED",
2075
+ "SOURCE_ROUTE_COMPLETED_CASHOUT_FAILED",
2076
+ "SOURCE_CASHOUT_SUBMISSION_UNKNOWN",
2077
+ "SOURCE_CASHOUT_STATUS_UNKNOWN",
2078
+ "DEPOSIT_RESOLUTION_FAILED",
2079
+ "ALLOWANCE_NOT_VISIBLE",
2080
+ "SIGNER_REQUIRED",
2081
+ "SIGNER_CHAIN_MISMATCH",
2082
+ "SIGNER_CHAIN_UNAVAILABLE",
2083
+ "WATCH_TIMEOUT",
2084
+ "TRANSACTION_FAILED",
2085
+ "TRANSACTION_SUBMISSION_UNKNOWN",
2086
+ "TRANSACTION_STATUS_UNKNOWN"
2087
+ ]);
2088
+ var cashSourceRecoveryJsonShape = {
2089
+ amount: nonNegativeBigintString,
2090
+ requestId: z.string().optional(),
2091
+ txHashes: z.array(z.string()),
2092
+ transactions: relayTransactionsJsonSchema.optional()
2093
+ };
2094
+ var cashErrorRecoveryJsonSchema = z.discriminatedUnion("kind", [
2095
+ z.object({
2096
+ ...cashSourceRecoveryJsonShape,
2097
+ kind: z.literal("retry-base-usdc-cashout")
2098
+ }).strict(),
2099
+ z.object({
2100
+ ...cashSourceRecoveryJsonShape,
2101
+ kind: z.literal("inspect-base-cashout-transaction"),
2102
+ depositTxHash: z.string()
2103
+ }).strict(),
2104
+ z.object({
2105
+ ...cashSourceRecoveryJsonShape,
2106
+ kind: z.literal("inspect-base-cashout-submission"),
2107
+ depositor: z.string()
2108
+ }).strict(),
2109
+ z.object({
2110
+ kind: z.literal("inspect-relay-route"),
2111
+ requestId: z.string().optional(),
2112
+ txHashes: z.array(z.string()),
2113
+ transactions: relayTransactionsJsonSchema.optional()
2114
+ }).strict(),
2115
+ z.object({
2116
+ kind: z.literal("inspect-base-operation-submission"),
2117
+ operation: z.string()
2118
+ }).strict(),
2119
+ z.object({
2120
+ kind: z.literal("inspect-base-transaction"),
2121
+ transactionHash: z.string(),
2122
+ operation: z.string()
2123
+ }).strict()
2124
+ ]);
1650
2125
  var cashErrorJsonSchema = z.object({
1651
- code: z.string(),
2126
+ code: z.enum(CASH_ERROR_CODES),
1652
2127
  message: z.string(),
1653
2128
  retryable: z.boolean(),
1654
- remediation: z.string()
1655
- });
2129
+ remediation: z.string(),
2130
+ recovery: cashErrorRecoveryJsonSchema.optional()
2131
+ }).strict();
1656
2132
 
1657
2133
  // src/codecs/json.ts
1658
2134
  function omitUndefined(obj) {
@@ -1683,35 +2159,38 @@ function fillToJson(fill) {
1683
2159
  });
1684
2160
  }
1685
2161
  function fillFromJson(json) {
2162
+ const parsed = cashFillJsonSchema.parse(json);
1686
2163
  return omitUndefined({
1687
- ...json,
1688
- amount: BigInt(json.amount),
1689
- conversionRate: json.conversionRate !== void 0 ? BigInt(json.conversionRate) : void 0,
1690
- releasedAmount: json.releasedAmount !== void 0 ? BigInt(json.releasedAmount) : void 0
2164
+ ...parsed,
2165
+ amount: BigInt(parsed.amount),
2166
+ conversionRate: parsed.conversionRate !== void 0 ? BigInt(parsed.conversionRate) : void 0,
2167
+ releasedAmount: parsed.releasedAmount !== void 0 ? BigInt(parsed.releasedAmount) : void 0
1691
2168
  });
1692
2169
  }
1693
2170
  function orderToJson(order) {
1694
- return omitUndefined({
1695
- depositId: order.depositId,
1696
- state: order.state,
1697
- fills: order.fills.map(fillToJson),
1698
- totalAmount: order.totalAmount.toString(),
1699
- filledAmount: order.filledAmount.toString(),
1700
- pendingAmount: order.pendingAmount.toString(),
1701
- returnedAmount: order.returnedAmount.toString(),
1702
- nextActions: order.nextActions,
1703
- primaryIntentHash: order.primaryIntentHash,
1704
- matchedAt: order.matchedAt,
1705
- deliveredAt: order.deliveredAt,
1706
- updatedAt: order.updatedAt,
1707
- intentCount: order.intentCount,
1708
- payouts: order.payouts?.map(
1709
- (p) => omitUndefined({ ...p, pricing: omitUndefined({ ...p.pricing }) })
1710
- ),
1711
- successRateBps: order.successRateBps,
1712
- isInFlight: order.isInFlight,
1713
- withdrawn: order.withdrawn
1714
- });
2171
+ return cashOrderJsonSchema.parse(
2172
+ omitUndefined({
2173
+ depositId: order.depositId,
2174
+ state: order.state,
2175
+ fills: order.fills.map(fillToJson),
2176
+ totalAmount: order.totalAmount.toString(),
2177
+ filledAmount: order.filledAmount.toString(),
2178
+ pendingAmount: order.pendingAmount.toString(),
2179
+ returnedAmount: order.returnedAmount.toString(),
2180
+ nextActions: order.nextActions,
2181
+ primaryIntentHash: order.primaryIntentHash,
2182
+ matchedAt: order.matchedAt,
2183
+ deliveredAt: order.deliveredAt,
2184
+ updatedAt: order.updatedAt,
2185
+ intentCount: order.intentCount,
2186
+ payouts: order.payouts?.map(
2187
+ (p) => omitUndefined({ ...p, pricing: omitUndefined({ ...p.pricing }) })
2188
+ ),
2189
+ successRateBps: order.successRateBps,
2190
+ isInFlight: order.isInFlight,
2191
+ withdrawn: order.withdrawn
2192
+ })
2193
+ );
1715
2194
  }
1716
2195
  function orderFromJson(json) {
1717
2196
  const parsed = cashOrderJsonSchema.parse(json);
@@ -1737,6 +2216,7 @@ function estimateToJson(estimate) {
1737
2216
  inputAmount: estimate.source.relayQuote.inputAmount.toString(),
1738
2217
  outputAmount: estimate.source.relayQuote.outputAmount.toString(),
1739
2218
  txs: estimate.source.relayQuote.txs.map(preparedTxToJson),
2219
+ ...estimate.source.relayQuote.fees !== void 0 ? { fees: sanitizeRelayValue(estimate.source.relayQuote.fees) } : {},
1740
2220
  raw: sanitizeRelayQuoteRaw(estimate.source.relayQuote.raw)
1741
2221
  }
1742
2222
  } : void 0
@@ -1755,11 +2235,108 @@ function estimateFromJson(json) {
1755
2235
  ...parsed.source.relayQuote,
1756
2236
  inputAmount: BigInt(parsed.source.relayQuote.inputAmount),
1757
2237
  outputAmount: BigInt(parsed.source.relayQuote.outputAmount),
1758
- txs: parsed.source.relayQuote.txs.map(preparedTxFromJson)
2238
+ txs: parsed.source.relayQuote.txs.map(preparedTxFromJson),
2239
+ ...parsed.source.relayQuote.fees !== void 0 ? { fees: restoreRelayValue(parsed.source.relayQuote.fees) } : {},
2240
+ raw: restoreRelayQuoteRaw(parsed.source.relayQuote.raw)
1759
2241
  }
1760
2242
  } : void 0
1761
2243
  });
1762
2244
  }
2245
+ function cashAssetFromJson(asset) {
2246
+ return {
2247
+ chainId: asset.chainId,
2248
+ address: asset.address,
2249
+ symbol: asset.symbol,
2250
+ decimals: asset.decimals,
2251
+ ...asset.name !== void 0 ? { name: asset.name } : {},
2252
+ ...asset.isNative !== void 0 ? { isNative: asset.isNative } : {}
2253
+ };
2254
+ }
2255
+ function relayQuoteToJson(quote) {
2256
+ return relayQuoteJsonSchema.parse({
2257
+ ...quote.requestId !== void 0 ? { requestId: quote.requestId } : {},
2258
+ source: quote.source,
2259
+ destination: quote.destination,
2260
+ inputAmount: quote.inputAmount.toString(),
2261
+ outputAmount: quote.outputAmount.toString(),
2262
+ ...quote.rate !== void 0 ? { rate: quote.rate } : {},
2263
+ ...quote.timeEstimateSeconds !== void 0 ? { timeEstimateSeconds: quote.timeEstimateSeconds } : {},
2264
+ ...quote.fees !== void 0 ? { fees: sanitizeRelayValue(quote.fees) } : {},
2265
+ txs: quote.txs.map(preparedTxToJson),
2266
+ raw: sanitizeRelayQuoteRaw(quote.raw)
2267
+ });
2268
+ }
2269
+ function relayQuoteFromJson(json) {
2270
+ const parsed = relayQuoteJsonSchema.parse(json);
2271
+ return {
2272
+ ...parsed.requestId !== void 0 ? { requestId: parsed.requestId } : {},
2273
+ source: cashAssetFromJson(parsed.source),
2274
+ destination: cashAssetFromJson(parsed.destination),
2275
+ inputAmount: BigInt(parsed.inputAmount),
2276
+ outputAmount: BigInt(parsed.outputAmount),
2277
+ ...parsed.rate !== void 0 ? { rate: parsed.rate } : {},
2278
+ ...parsed.timeEstimateSeconds !== void 0 ? { timeEstimateSeconds: parsed.timeEstimateSeconds } : {},
2279
+ ...parsed.fees !== void 0 ? { fees: restoreRelayValue(parsed.fees) } : {},
2280
+ txs: parsed.txs.map(preparedTxFromJson),
2281
+ raw: restoreRelayQuoteRaw(parsed.raw)
2282
+ };
2283
+ }
2284
+ function sourceCapabilitiesToJson(capabilities) {
2285
+ return cashSourceCapabilitiesJsonSchema.parse(capabilities);
2286
+ }
2287
+ function sourceCapabilitiesFromJson(json) {
2288
+ const parsed = cashSourceCapabilitiesJsonSchema.parse(json);
2289
+ return {
2290
+ destination: cashAssetFromJson(parsed.destination),
2291
+ chains: parsed.chains.map((chain) => ({
2292
+ id: chain.id,
2293
+ name: chain.name,
2294
+ displayName: chain.displayName,
2295
+ disabled: chain.disabled,
2296
+ depositEnabled: chain.depositEnabled,
2297
+ blockProductionLagging: chain.blockProductionLagging,
2298
+ ...chain.vmType !== void 0 ? { vmType: chain.vmType } : {},
2299
+ tokens: chain.tokens.map(cashAssetFromJson)
2300
+ })),
2301
+ source: parsed.source,
2302
+ asOf: parsed.asOf
2303
+ };
2304
+ }
2305
+ function relayStatusToJson(status) {
2306
+ return relayStatusJsonSchema.parse({ ...status, raw: sanitizeRelayValue(status.raw) });
2307
+ }
2308
+ function relayStatusFromJson(json) {
2309
+ const parsed = relayStatusJsonSchema.parse(json);
2310
+ return {
2311
+ requestId: parsed.requestId,
2312
+ status: parsed.status,
2313
+ ...parsed.details !== void 0 ? { details: parsed.details } : {},
2314
+ inTxHashes: parsed.inTxHashes,
2315
+ txHashes: parsed.txHashes,
2316
+ ...parsed.updatedAt !== void 0 ? { updatedAt: parsed.updatedAt } : {},
2317
+ ...parsed.originChainId !== void 0 ? { originChainId: parsed.originChainId } : {},
2318
+ ...parsed.destinationChainId !== void 0 ? { destinationChainId: parsed.destinationChainId } : {},
2319
+ ...parsed.quoteCreatedAt !== void 0 ? { quoteCreatedAt: parsed.quoteCreatedAt } : {},
2320
+ raw: restoreRelayValue(parsed.raw)
2321
+ };
2322
+ }
2323
+ function relayExecutionResultToJson(result) {
2324
+ return relayExecutionResultJsonSchema.parse({
2325
+ ...result.requestId !== void 0 ? { requestId: result.requestId } : {},
2326
+ txHashes: result.txHashes,
2327
+ ...result.transactions !== void 0 ? { transactions: result.transactions } : {},
2328
+ quote: sanitizeRelayQuoteRaw(result.quote)
2329
+ });
2330
+ }
2331
+ function relayExecutionResultFromJson(json) {
2332
+ const parsed = relayExecutionResultJsonSchema.parse(json);
2333
+ return {
2334
+ ...parsed.requestId !== void 0 ? { requestId: parsed.requestId } : {},
2335
+ txHashes: parsed.txHashes,
2336
+ ...parsed.transactions !== void 0 ? { transactions: parsed.transactions } : {},
2337
+ quote: restoreRelayQuoteRaw(parsed.quote)
2338
+ };
2339
+ }
1763
2340
  function preparedTxToJson(tx) {
1764
2341
  return { to: tx.to, data: tx.data, value: tx.value.toString(), chainId: tx.chainId };
1765
2342
  }
@@ -1878,7 +2455,68 @@ function capabilitiesFromJson(json) {
1878
2455
  }
1879
2456
  };
1880
2457
  }
2458
+ function cashErrorToJson(error) {
2459
+ return cashErrorJsonSchema.parse({
2460
+ code: error.code,
2461
+ message: error.message,
2462
+ retryable: error.retryable,
2463
+ remediation: error.remediation,
2464
+ ...error.recovery ? { recovery: error.recovery } : {}
2465
+ });
2466
+ }
2467
+ function cashErrorFromJson(json) {
2468
+ const parsed = cashErrorJsonSchema.parse(json);
2469
+ let recovery;
2470
+ if (parsed.recovery) {
2471
+ if (parsed.recovery.kind === "inspect-base-transaction") {
2472
+ recovery = {
2473
+ kind: parsed.recovery.kind,
2474
+ transactionHash: parsed.recovery.transactionHash,
2475
+ operation: parsed.recovery.operation
2476
+ };
2477
+ } else if (parsed.recovery.kind === "inspect-base-operation-submission") {
2478
+ recovery = {
2479
+ kind: parsed.recovery.kind,
2480
+ operation: parsed.recovery.operation
2481
+ };
2482
+ } else if (parsed.recovery.kind === "inspect-relay-route") {
2483
+ recovery = {
2484
+ kind: parsed.recovery.kind,
2485
+ txHashes: parsed.recovery.txHashes,
2486
+ ...parsed.recovery.requestId !== void 0 ? { requestId: parsed.recovery.requestId } : {},
2487
+ ...parsed.recovery.transactions !== void 0 ? { transactions: parsed.recovery.transactions } : {}
2488
+ };
2489
+ } else {
2490
+ const common = {
2491
+ amount: parsed.recovery.amount,
2492
+ txHashes: parsed.recovery.txHashes,
2493
+ ...parsed.recovery.requestId !== void 0 ? { requestId: parsed.recovery.requestId } : {},
2494
+ ...parsed.recovery.transactions !== void 0 ? { transactions: parsed.recovery.transactions } : {}
2495
+ };
2496
+ if (parsed.recovery.kind === "retry-base-usdc-cashout") {
2497
+ recovery = { ...common, kind: parsed.recovery.kind };
2498
+ } else if (parsed.recovery.kind === "inspect-base-cashout-submission") {
2499
+ recovery = {
2500
+ ...common,
2501
+ kind: parsed.recovery.kind,
2502
+ depositor: parsed.recovery.depositor
2503
+ };
2504
+ } else {
2505
+ recovery = {
2506
+ ...common,
2507
+ kind: parsed.recovery.kind,
2508
+ depositTxHash: parsed.recovery.depositTxHash
2509
+ };
2510
+ }
2511
+ }
2512
+ }
2513
+ return new CashError({
2514
+ code: parsed.code,
2515
+ message: parsed.message,
2516
+ retryable: parsed.retryable,
2517
+ remediation: parsed.remediation,
2518
+ ...recovery ? { recovery } : {}
2519
+ });
2520
+ }
1881
2521
 
1882
- 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 };
1883
- //# sourceMappingURL=index.js.map
1884
- //# sourceMappingURL=index.js.map
2522
+ 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 };