@zkp2p/cash 0.1.3 → 0.1.5

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]) => {
@@ -591,20 +592,183 @@ function normalizeChain(chain) {
591
592
  function isSupportedEvmChain(chain) {
592
593
  return chain.vmType === void 0 || chain.vmType === "evm";
593
594
  }
595
+ function isExecutableSourceChain(chain) {
596
+ return isSupportedEvmChain(chain) && !chain.disabled && chain.depositEnabled && !chain.blockProductionLagging;
597
+ }
594
598
  function quoteRequestId(quote) {
595
599
  return quote.steps.map((step) => step.requestId).find((id) => id !== void 0);
596
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({
611
+ hash: tx.txHash,
612
+ chainId: tx.chainId,
613
+ ...tx.isBatchTx ? { isBatchTx: true } : {}
614
+ });
615
+ }
616
+ for (const tx of item.txHashes ?? []) {
617
+ record({
618
+ hash: tx.txHash,
619
+ chainId: tx.chainId,
620
+ ...tx.isBatchTx ? { isBatchTx: true } : {}
621
+ });
622
+ }
623
+ }
624
+ }
625
+ const dedupe = (txs) => [
626
+ ...new Map(txs.map((tx) => [`${tx.chainId}:${tx.hash.toLowerCase()}`, tx])).values()
627
+ ];
628
+ return { origin: dedupe(origin), destination: dedupe(destination) };
629
+ }
630
+ function relayTransactionHashes(transactions) {
631
+ return [
632
+ ...new Set([...transactions.origin, ...transactions.destination].map(({ hash }) => hash))
633
+ ];
634
+ }
597
635
  function quoteSourceChainId(quote) {
598
636
  const details = asRecord(quote.details);
599
637
  const currencyIn = asRecord(details.currencyIn);
600
638
  const sourceCurrency = asRecord(currencyIn.currency);
601
639
  return asNumber(sourceCurrency.chainId);
602
640
  }
641
+ function assertCanonicalRelayDestination(quote) {
642
+ const details = asRecord(quote.details);
643
+ const currencyOut = asRecord(details.currencyOut);
644
+ const destination = asRecord(currencyOut.currency);
645
+ if (asNumber(destination.chainId) !== BASE_CHAIN_ID || asString(destination.address)?.toLowerCase() !== BASE_USDC_ADDRESS.toLowerCase()) {
646
+ throw new Error("Relay quote destination is not canonical Base USDC");
647
+ }
648
+ }
649
+ async function assertWalletChainId(wallet, expectedChainId, operation) {
650
+ let actualChainId;
651
+ try {
652
+ actualChainId = await wallet.getChainId();
653
+ } catch (err) {
654
+ throw errors.signerChainUnavailable(operation, expectedChainId, err);
655
+ }
656
+ if (actualChainId !== expectedChainId) {
657
+ throw errors.signerChainMismatch(operation, expectedChainId, actualChainId);
658
+ }
659
+ }
660
+ async function assertRelayExecutionIdentity(quote, wallet, expectedRecipient) {
661
+ const signer = wallet.account?.address;
662
+ if (!signer) throw new Error("Relay execution requires a wallet account");
663
+ const sourceChainId = quoteSourceChainId(quote);
664
+ if (sourceChainId !== void 0) {
665
+ await assertWalletChainId(wallet, sourceChainId, "Relay execution");
666
+ }
667
+ const details = asRecord(quote.details);
668
+ const sender = asString(details.sender);
669
+ const recipient = asString(details.recipient);
670
+ if (!sender || sender.toLowerCase() !== signer.toLowerCase()) {
671
+ throw new Error("Relay quote sender does not match the execution signer");
672
+ }
673
+ const destinationOwner = expectedRecipient ?? signer;
674
+ if (!recipient || recipient.toLowerCase() !== destinationOwner.toLowerCase()) {
675
+ throw new Error("Relay quote recipient does not match the expected Base recipient");
676
+ }
677
+ }
678
+ function isRelaySecretKey(key) {
679
+ const normalized = key.toLowerCase();
680
+ return normalized === "headers" || normalized === "apikey";
681
+ }
682
+ function redactRelayValue(value, seen = /* @__PURE__ */ new WeakMap()) {
683
+ if (value === null || typeof value !== "object") return value;
684
+ if (value instanceof Date || value instanceof Error) return value;
685
+ const existing = seen.get(value);
686
+ if (existing !== void 0) return existing;
687
+ if (Array.isArray(value)) {
688
+ const output2 = [];
689
+ seen.set(value, output2);
690
+ for (const entry of value) output2.push(redactRelayValue(entry, seen));
691
+ return output2;
692
+ }
693
+ const output = {};
694
+ seen.set(value, output);
695
+ for (const [key, entry] of Object.entries(value)) {
696
+ if (!isRelaySecretKey(key)) output[key] = redactRelayValue(entry, seen);
697
+ }
698
+ return output;
699
+ }
700
+ var RELAY_WIRE_TYPE = "__zkp2pCashType";
701
+ function sanitizeRelayValue(value, seen = /* @__PURE__ */ new WeakSet()) {
702
+ if (typeof value === "bigint") {
703
+ return { [RELAY_WIRE_TYPE]: "bigint", value: value.toString() };
704
+ }
705
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
706
+ return value;
707
+ }
708
+ if (typeof value === "number") {
709
+ return Number.isFinite(value) ? value : { [RELAY_WIRE_TYPE]: "number", value: String(value) };
710
+ }
711
+ if (typeof value === "undefined") return { [RELAY_WIRE_TYPE]: "undefined" };
712
+ if (value instanceof Date) {
713
+ return { [RELAY_WIRE_TYPE]: "date", value: value.toISOString() };
714
+ }
715
+ if (value instanceof Error) {
716
+ return {
717
+ [RELAY_WIRE_TYPE]: "error",
718
+ name: value.name,
719
+ message: value.message
720
+ };
721
+ }
722
+ if (typeof value !== "object") return void 0;
723
+ if (seen.has(value)) throw new TypeError("Relay payload contains a circular reference");
724
+ seen.add(value);
725
+ if (Array.isArray(value)) {
726
+ const output2 = value.map((entry) => sanitizeRelayValue(entry, seen));
727
+ seen.delete(value);
728
+ return output2;
729
+ }
730
+ const output = {};
731
+ for (const [key, entry] of Object.entries(value)) {
732
+ if (isRelaySecretKey(key)) continue;
733
+ const sanitized = sanitizeRelayValue(entry, seen);
734
+ if (sanitized !== void 0) output[key] = sanitized;
735
+ }
736
+ seen.delete(value);
737
+ return output;
738
+ }
739
+ function restoreRelayValue(value) {
740
+ if (Array.isArray(value)) return value.map(restoreRelayValue);
741
+ if (value === null || typeof value !== "object") return value;
742
+ const row = value;
743
+ const wireType = row[RELAY_WIRE_TYPE];
744
+ const keyCount = Object.keys(row).length;
745
+ if (keyCount === 2 && wireType === "bigint" && typeof row.value === "string") {
746
+ return BigInt(row.value);
747
+ }
748
+ if (keyCount === 2 && wireType === "date" && typeof row.value === "string") {
749
+ return new Date(row.value);
750
+ }
751
+ if (keyCount === 2 && wireType === "number" && typeof row.value === "string") {
752
+ return Number(row.value);
753
+ }
754
+ if (keyCount === 1 && wireType === "undefined") return void 0;
755
+ if (keyCount === 3 && wireType === "error" && typeof row.message === "string") {
756
+ const error = new Error(row.message);
757
+ if (typeof row.name === "string") error.name = row.name;
758
+ return error;
759
+ }
760
+ return Object.fromEntries(
761
+ Object.entries(row).map(([key, entry]) => [key, restoreRelayValue(entry)])
762
+ );
763
+ }
764
+ function redactRelayQuoteRaw(quote) {
765
+ return redactRelayValue(quote);
766
+ }
603
767
  function sanitizeRelayQuoteRaw(quote) {
604
- if (!quote.request) return quote;
605
- const request = { ...quote.request };
606
- delete request.headers;
607
- return { ...quote, request };
768
+ return sanitizeRelayValue(quote);
769
+ }
770
+ function restoreRelayQuoteRaw(value) {
771
+ return restoreRelayValue(value);
608
772
  }
609
773
  async function resolveRelayChains(options, client, config = {}) {
610
774
  const preferInjectedClientChains = config.preferInjectedClientChains ?? true;
@@ -618,19 +782,38 @@ function relayQuoteFromExecute(input, quote) {
618
782
  const currencyOut = asRecord(details.currencyOut);
619
783
  const sourceCurrency = asRecord(currencyIn.currency);
620
784
  const destinationCurrency = asRecord(currencyOut.currency);
621
- const source = normalizeToken(input.source.chainId, sourceCurrency) ?? {
622
- chainId: input.source.chainId,
623
- address: input.source.currency,
624
- symbol: "TOKEN",
625
- decimals: 0
626
- };
627
- const destination = normalizeToken(BASE_CHAIN_ID, destinationCurrency) ?? BASE_USDC_ASSET;
785
+ const sourceChainId = asNumber(sourceCurrency.chainId);
786
+ const sourceAddress = asString(sourceCurrency.address);
787
+ const sender = asString(details.sender);
788
+ const recipient = asString(details.recipient);
789
+ const expectedRecipient = input.recipient ?? input.user;
790
+ const destinationChainId = asNumber(destinationCurrency.chainId);
791
+ const destinationAddress = asString(destinationCurrency.address);
792
+ if (sourceChainId !== input.source.chainId || sourceAddress?.toLowerCase() !== input.source.currency.toLowerCase()) {
793
+ throw new Error("Relay quote source does not match the requested asset");
794
+ }
795
+ if (!sender || sender.toLowerCase() !== input.user.toLowerCase()) {
796
+ throw new Error("Relay quote sender does not match the requested wallet");
797
+ }
798
+ if (!recipient || recipient.toLowerCase() !== expectedRecipient.toLowerCase()) {
799
+ throw new Error("Relay quote recipient does not match the requested Base recipient");
800
+ }
801
+ if (destinationChainId !== BASE_CHAIN_ID || destinationAddress?.toLowerCase() !== BASE_USDC_ADDRESS.toLowerCase()) {
802
+ throw new Error("Relay quote destination is not canonical Base USDC");
803
+ }
804
+ const source = normalizeToken(input.source.chainId, sourceCurrency);
805
+ if (!source) throw new Error("Relay quote source metadata is malformed");
806
+ const destination = normalizeToken(destinationChainId, destinationCurrency);
807
+ if (!destination) throw new Error("Relay quote destination metadata is malformed");
628
808
  const txs = quote.steps.flatMap(
629
809
  (step) => step.items.map((item) => normalizeTx(item.data, input.source.chainId)).filter((tx) => tx !== null)
630
810
  );
631
- const outputAmount = BigInt(
632
- String(currencyOut.minimumAmount ?? currencyOut.amount ?? input.amount.toString())
633
- );
811
+ const rawOutputAmount = currencyOut.minimumAmount ?? currencyOut.amount;
812
+ if (rawOutputAmount === void 0 || rawOutputAmount === null) {
813
+ throw new Error("Relay quote is missing an output amount");
814
+ }
815
+ const outputAmount = BigInt(String(rawOutputAmount));
816
+ if (outputAmount <= 0n) throw new Error("Relay quote output amount must be positive");
634
817
  const requestId = quoteRequestId(quote);
635
818
  const rate = asNumber(details.rate);
636
819
  const timeEstimateSeconds = asNumber(details.timeEstimate);
@@ -638,92 +821,135 @@ function relayQuoteFromExecute(input, quote) {
638
821
  ...requestId ? { requestId } : {},
639
822
  source,
640
823
  destination,
641
- inputAmount: BigInt(String(currencyIn.amount ?? input.amount.toString())),
824
+ inputAmount: BigInt(String(currencyIn.amount)),
642
825
  outputAmount,
643
826
  ...rate !== void 0 ? { rate } : {},
644
827
  ...timeEstimateSeconds !== void 0 ? { timeEstimateSeconds } : {},
645
828
  ...quote.fees !== void 0 ? { fees: quote.fees } : {},
646
829
  txs,
647
- raw: sanitizeRelayQuoteRaw(quote)
830
+ raw: redactRelayQuoteRaw(quote)
648
831
  };
649
832
  }
650
833
  async function readRelaySourceCapabilities(options = {}) {
651
- const client = relayClient(options);
652
- const chains = await resolveRelayChains(options, client);
653
- return {
654
- destination: BASE_USDC_ASSET,
655
- chains: chains.map(normalizeChain).filter((chain) => chain.tokens.length > 0 && isSupportedEvmChain(chain)).sort((a, b) => a.displayName.localeCompare(b.displayName)),
656
- source: "relay-sdk",
657
- asOf: Math.floor(Date.now() / 1e3)
658
- };
834
+ try {
835
+ const client = relayClient(options);
836
+ const chains = await resolveRelayChains(options, client);
837
+ return {
838
+ destination: BASE_USDC_ASSET,
839
+ chains: chains.map(normalizeChain).filter((chain) => chain.tokens.length > 0 && isExecutableSourceChain(chain)).sort((a, b) => a.displayName.localeCompare(b.displayName)),
840
+ source: "relay-sdk",
841
+ asOf: Math.floor(Date.now() / 1e3)
842
+ };
843
+ } catch (err) {
844
+ if (isCashError(err)) throw err;
845
+ throw errors.sourceCapabilitiesFailed(err);
846
+ }
659
847
  }
660
848
  async function quoteRelayToBaseUsdc(input, options = {}) {
661
- const client = relayClient(options);
662
- const quote = await client.actions.getQuote(
663
- {
664
- chainId: input.source.chainId,
665
- currency: input.source.currency,
666
- toChainId: BASE_CHAIN_ID,
667
- toCurrency: BASE_USDC_ADDRESS,
668
- user: input.user,
669
- recipient: input.recipient ?? input.user,
670
- amount: input.amount.toString(),
671
- tradeType: input.tradeType ?? "EXACT_INPUT"
672
- },
673
- false
674
- );
675
- return relayQuoteFromExecute(input, quote);
849
+ try {
850
+ if (input.amount <= 0n) throw new Error("Relay quote amount must be positive");
851
+ const client = relayClient(options);
852
+ const quote = await client.actions.getQuote(
853
+ {
854
+ chainId: input.source.chainId,
855
+ currency: input.source.currency,
856
+ toChainId: BASE_CHAIN_ID,
857
+ toCurrency: BASE_USDC_ADDRESS,
858
+ user: input.user,
859
+ recipient: input.recipient ?? input.user,
860
+ amount: input.amount.toString(),
861
+ tradeType: input.tradeType ?? "EXACT_INPUT"
862
+ },
863
+ false
864
+ );
865
+ return relayQuoteFromExecute(input, quote);
866
+ } catch (err) {
867
+ if (isCashError(err)) throw err;
868
+ throw errors.sourceQuoteFailed(err);
869
+ }
676
870
  }
677
871
  async function executeRelayQuote(quote, wallet, options = {}) {
678
- const client = relayClient(options.relay);
679
- const sourceChainId = quoteSourceChainId(quote);
680
- if (sourceChainId !== void 0 && !(client.chains ?? []).some((chain) => chain.id === sourceChainId)) {
681
- await resolveRelayChains(options.relay ?? {}, client, { preferInjectedClientChains: false });
682
- }
683
- const { data } = await client.actions.execute({
684
- quote,
685
- wallet,
686
- ...options.onProgress ? { onProgress: options.onProgress } : {},
687
- ...options.disableCapabilitiesCheck !== void 0 ? { disableCapabilitiesCheck: options.disableCapabilitiesCheck } : {}
688
- });
689
- const requestId = quoteRequestId(data);
690
- return {
691
- ...requestId ? { requestId } : {},
692
- txHashes: data.steps.flatMap(
693
- (step) => step.items.flatMap((item) => (item.txHashes ?? []).map((tx) => tx.txHash))
694
- ),
695
- quote: data
696
- };
872
+ let observedRequestId;
873
+ let observedTransactions = { origin: [], destination: [] };
874
+ try {
875
+ const rawQuote = "raw" in quote ? quote.raw : quote;
876
+ observedRequestId = quoteRequestId(rawQuote);
877
+ assertCanonicalRelayDestination(rawQuote);
878
+ await assertRelayExecutionIdentity(rawQuote, wallet, options.recipient);
879
+ const client = relayClient(options.relay);
880
+ const sourceChainId = quoteSourceChainId(rawQuote);
881
+ if (sourceChainId !== void 0 && !(client.chains ?? []).some((chain) => chain.id === sourceChainId)) {
882
+ await resolveRelayChains(options.relay ?? {}, client, { preferInjectedClientChains: false });
883
+ }
884
+ const onProgress = (data2) => {
885
+ const progressSteps = Array.isArray(data2.steps) ? data2.steps : [];
886
+ observedRequestId = progressSteps.map((step) => step.requestId).find((id) => id !== void 0) ?? observedRequestId;
887
+ observedTransactions = collectRelayTransactions(progressSteps, sourceChainId);
888
+ if (options.onProgress) {
889
+ try {
890
+ options.onProgress(data2);
891
+ } catch {
892
+ }
893
+ }
894
+ };
895
+ const { data } = await client.actions.execute({
896
+ quote: rawQuote,
897
+ wallet,
898
+ onProgress,
899
+ ...options.disableCapabilitiesCheck !== void 0 ? { disableCapabilitiesCheck: options.disableCapabilitiesCheck } : {}
900
+ });
901
+ const requestId = quoteRequestId(data) ?? observedRequestId;
902
+ const transactions = collectRelayTransactions(data.steps, sourceChainId);
903
+ return {
904
+ ...requestId ? { requestId } : {},
905
+ txHashes: relayTransactionHashes(transactions),
906
+ transactions,
907
+ quote: redactRelayQuoteRaw(data)
908
+ };
909
+ } catch (err) {
910
+ if (isCashError(err)) throw err;
911
+ const txHashes = relayTransactionHashes(observedTransactions);
912
+ throw errors.sourceExecutionFailed(err, {
913
+ ...observedRequestId ? { requestId: observedRequestId } : {},
914
+ txHashes,
915
+ ...txHashes.length > 0 ? { transactions: observedTransactions } : {}
916
+ });
917
+ }
697
918
  }
698
919
  async function readRelayStatus(requestId, options = {}) {
699
- const client = relayClient(options);
700
- const response = await client.utils.request({
701
- url: `${client.baseApiUrl}/intents/status/v3`,
702
- method: "get",
703
- params: { requestId }
704
- });
705
- const root = asRecord(response.data);
706
- const status = asString(root.status);
707
- if (status !== "refund" && status !== "waiting" && status !== "depositing" && status !== "failure" && status !== "pending" && status !== "submitted" && status !== "success") {
708
- throw new Error(`Relay returned unknown status: ${String(root.status)}`);
709
- }
710
- const details = asString(root.details);
711
- const updatedAt = asNumber(root.updatedAt);
712
- const originChainId = asNumber(root.originChainId);
713
- const destinationChainId = asNumber(root.destinationChainId);
714
- const quoteCreatedAt = asNumber(root.quoteCreatedAt);
715
- return {
716
- requestId,
717
- status,
718
- ...details ? { details } : {},
719
- inTxHashes: Array.isArray(root.inTxHashes) ? root.inTxHashes.map(String) : [],
720
- txHashes: Array.isArray(root.txHashes) ? root.txHashes.map(String) : [],
721
- ...updatedAt !== void 0 ? { updatedAt } : {},
722
- ...originChainId !== void 0 ? { originChainId } : {},
723
- ...destinationChainId !== void 0 ? { destinationChainId } : {},
724
- ...quoteCreatedAt !== void 0 ? { quoteCreatedAt } : {},
725
- raw: response.data
726
- };
920
+ try {
921
+ const client = relayClient(options);
922
+ const response = await client.utils.request({
923
+ url: `${client.baseApiUrl}/intents/status/v3`,
924
+ method: "get",
925
+ params: { requestId }
926
+ });
927
+ const root = asRecord(response.data);
928
+ const status = asString(root.status);
929
+ if (status !== "refund" && status !== "waiting" && status !== "depositing" && status !== "failure" && status !== "pending" && status !== "submitted" && status !== "success") {
930
+ throw new Error(`Relay returned unknown status: ${String(root.status)}`);
931
+ }
932
+ const details = asString(root.details);
933
+ const updatedAt = asNumber(root.updatedAt);
934
+ const originChainId = asNumber(root.originChainId);
935
+ const destinationChainId = asNumber(root.destinationChainId);
936
+ const quoteCreatedAt = asNumber(root.quoteCreatedAt);
937
+ return {
938
+ requestId,
939
+ status,
940
+ ...details ? { details } : {},
941
+ inTxHashes: Array.isArray(root.inTxHashes) ? root.inTxHashes.map(String) : [],
942
+ txHashes: Array.isArray(root.txHashes) ? root.txHashes.map(String) : [],
943
+ ...updatedAt !== void 0 ? { updatedAt } : {},
944
+ ...originChainId !== void 0 ? { originChainId } : {},
945
+ ...destinationChainId !== void 0 ? { destinationChainId } : {},
946
+ ...quoteCreatedAt !== void 0 ? { quoteCreatedAt } : {},
947
+ raw: response.data
948
+ };
949
+ } catch (err) {
950
+ if (isCashError(err)) throw err;
951
+ throw errors.sourceStatusFailed(requestId, err);
952
+ }
727
953
  }
728
954
 
729
955
  // src/client/estimate.ts
@@ -770,11 +996,16 @@ async function readEstimate(publicClient, input, context = {}) {
770
996
  if (!feedConfig || feedConfig.feed.toLowerCase() === ZERO_ADDRESS) {
771
997
  rate = 1;
772
998
  } else {
773
- const result = await publicClient.readContract({
774
- address: feedConfig.feed,
775
- abi: CHAINLINK_LATEST_ROUND_ABI,
776
- functionName: "latestRoundData"
777
- });
999
+ let result;
1000
+ try {
1001
+ result = await publicClient.readContract({
1002
+ address: feedConfig.feed,
1003
+ abi: CHAINLINK_LATEST_ROUND_ABI,
1004
+ functionName: "latestRoundData"
1005
+ });
1006
+ } catch (err) {
1007
+ throw errors.oracleReadFailed(currency, err);
1008
+ }
778
1009
  const answer = Number(result[1]);
779
1010
  const price = answer / 10 ** feedConfig.decimals;
780
1011
  if (!Number.isFinite(price) || price <= 0) {
@@ -820,6 +1051,7 @@ async function readEstimate(publicClient, input, context = {}) {
820
1051
  var DEFAULT_RPC_URL = "https://mainnet.base.org";
821
1052
  var CASH_ATTRIBUTION_CODE = "peer-cash";
822
1053
  var DEFAULT_CURATOR_URLS = {
1054
+ preproduction: "https://api-preprod.zkp2p.xyz",
823
1055
  staging: "https://api-staging.zkp2p.xyz"
824
1056
  };
825
1057
  var ERC20_APPROVE_ABI = parseAbi([
@@ -854,12 +1086,29 @@ async function submitAndConfirm(client, verb, send) {
854
1086
  try {
855
1087
  hash = await send();
856
1088
  } catch (err) {
857
- throw mapChainError(verb, err);
1089
+ const mapped = mapChainError(verb, err);
1090
+ if (isKnownPreBroadcastFailure(err, mapped)) throw mapped;
1091
+ throw errors.transactionSubmissionUnknown(verb, err, {
1092
+ kind: "inspect-base-operation-submission",
1093
+ operation: verb
1094
+ });
1095
+ }
1096
+ let receipt;
1097
+ try {
1098
+ receipt = await client.publicClient.waitForTransactionReceipt({ hash });
1099
+ } catch (err) {
1100
+ throw errors.transactionStatusUnknown(hash, err, verb);
858
1101
  }
859
- const receipt = await client.publicClient.waitForTransactionReceipt({ hash });
860
1102
  if (receipt.status === "reverted") throw errors.transactionFailed(hash);
861
1103
  return hash;
862
1104
  }
1105
+ function isKnownPreBroadcastFailure(err, mapped) {
1106
+ if (mapped.code === "INSUFFICIENT_TOKEN_BALANCE" || mapped.code === "ALLOWANCE_NOT_VISIBLE" || mapped.code === "ESCROW_PAUSED") {
1107
+ return true;
1108
+ }
1109
+ const message = err instanceof Error ? err.message : String(err);
1110
+ return /user rejected|user denied|rejected request|action_rejected/i.test(message);
1111
+ }
863
1112
  function depositOrderOptions(deposit) {
864
1113
  const remaining = toBigIntOrUndefined(deposit.remainingDeposits);
865
1114
  const outstanding = toBigIntOrUndefined(deposit.outstandingIntentAmount);
@@ -899,9 +1148,10 @@ function createCashClient(options) {
899
1148
  }
900
1149
  const readClient = buildSdkClient(createWalletClient({ chain: base, transport }));
901
1150
  const signingClients = /* @__PURE__ */ new WeakMap();
902
- function signingClient(verb, opts) {
1151
+ async function signingClient(verb, opts) {
903
1152
  const signer = opts?.signer;
904
1153
  if (!signer?.account) throw errors.signerRequired(verb);
1154
+ await assertWalletChainId(signer, BASE_CHAIN_ID, verb);
905
1155
  let client = signingClients.get(signer);
906
1156
  if (!client) {
907
1157
  client = buildSdkClient(signer);
@@ -911,13 +1161,15 @@ function createCashClient(options) {
911
1161
  }
912
1162
  function validatePayout(input) {
913
1163
  const { receive } = input;
914
- const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
915
- if (!catalog[receive.platform]) throw errors.unsupportedPlatform(receive.platform);
1164
+ const platform = buildCapabilities(environment).platforms.find(
1165
+ (capability) => capability.platform === receive.platform
1166
+ );
1167
+ if (!platform) throw errors.unsupportedPlatform(receive.platform);
916
1168
  if (!isMarketRateSupported(receive.currency)) {
917
1169
  throw errors.oracleUnsupportedCurrency(receive.currency);
918
1170
  }
919
- if (platformRequiresIdentityAttestation(receive.platform) && !receive.payee.identityAttestation) {
920
- throw errors.payeeVerificationRequired(receive.platform);
1171
+ if (!platform.currencies.includes(receive.currency)) {
1172
+ throw errors.unsupportedPlatformCurrency(receive.platform, receive.currency);
921
1173
  }
922
1174
  return {
923
1175
  payouts: [
@@ -926,15 +1178,22 @@ function createCashClient(options) {
926
1178
  currency: receive.currency,
927
1179
  payeeData: receive.payee
928
1180
  }
929
- ],
930
- ...input.intentAmountRange ? { intentAmountRange: input.intentAmountRange } : {}
1181
+ ]
931
1182
  };
932
1183
  }
933
- function validateInput(input) {
934
- if (input.amount < MIN_CASHOUT_AMOUNT) {
935
- throw errors.amountBelowMinimum(input.amount, MIN_CASHOUT_AMOUNT);
1184
+ function validateDepositInput(amount, input, payoutInput = validatePayout(input)) {
1185
+ if (amount < MIN_CASHOUT_AMOUNT) {
1186
+ throw errors.amountBelowMinimum(amount, MIN_CASHOUT_AMOUNT);
1187
+ }
1188
+ const range = input.intentAmountRange;
1189
+ if (range && (range.min <= 0n || range.max < range.min || range.max > amount)) {
1190
+ throw errors.invalidIntentAmountRange(amount, range.min, range.max);
936
1191
  }
937
- return { amount: input.amount, ...validatePayout(input) };
1192
+ return {
1193
+ amount,
1194
+ ...payoutInput,
1195
+ ...range ? { intentAmountRange: range } : {}
1196
+ };
938
1197
  }
939
1198
  async function buildDepositParams(client, depositInput) {
940
1199
  try {
@@ -949,32 +1208,54 @@ function createCashClient(options) {
949
1208
  throw errors.payeeRegistrationFailed(err);
950
1209
  }
951
1210
  }
1211
+ function parseDepositId(depositId) {
1212
+ try {
1213
+ const parsed = parseCompositeDepositId(depositId);
1214
+ return {
1215
+ ...parsed,
1216
+ compositeId: createCompositeDepositId(parsed.escrowAddress, parsed.onchainDepositId)
1217
+ };
1218
+ } catch (err) {
1219
+ throw errors.invalidDepositId(depositId, err);
1220
+ }
1221
+ }
952
1222
  async function fetchOrder(depositId) {
953
- const deposits = await readClient.indexer.getDepositsByIdsWithRelations([depositId], {
954
- includeIntents: true,
955
- intentStatuses: CASH_ORDER_STATUSES
956
- });
1223
+ const { compositeId } = parseDepositId(depositId);
1224
+ let deposits;
1225
+ try {
1226
+ deposits = await readClient.indexer.getDepositsByIdsWithRelations([compositeId], {
1227
+ includeIntents: true,
1228
+ intentStatuses: CASH_ORDER_STATUSES
1229
+ });
1230
+ } catch (err) {
1231
+ throw errors.indexerUnavailable("order", err);
1232
+ }
957
1233
  const deposit = deposits[0];
958
1234
  if (!deposit) {
959
- const intents = await readClient.indexer.getIntentsForDeposits(
960
- [depositId],
961
- CASH_ORDER_STATUSES
962
- );
963
- if (intents.length === 0) throw errors.orderNotFound(depositId);
964
- return deriveCashOrder(depositId, intents);
1235
+ let intents;
1236
+ try {
1237
+ intents = await readClient.indexer.getIntentsForDeposits(
1238
+ [compositeId],
1239
+ CASH_ORDER_STATUSES
1240
+ );
1241
+ } catch (err) {
1242
+ throw errors.indexerUnavailable("order intents", err);
1243
+ }
1244
+ if (intents.length === 0) throw errors.orderNotFound(compositeId);
1245
+ return deriveCashOrder(compositeId, intents);
965
1246
  }
966
1247
  const payouts = derivePayouts(
967
1248
  deposit.paymentMethods ?? [],
968
1249
  deposit.currencies ?? [],
969
1250
  getPaymentMethodsCatalog(BASE_CHAIN_ID, environment)
970
1251
  );
971
- return deriveCashOrder(depositId, deposit.intents ?? [], {
1252
+ return deriveCashOrder(compositeId, deposit.intents ?? [], {
972
1253
  ...depositOrderOptions(deposit),
973
1254
  ...payouts.length > 0 ? { payouts } : {}
974
1255
  });
975
1256
  }
976
1257
  function escrowContext(depositId) {
977
- const { escrowAddress, onchainDepositId } = parseCompositeDepositId(depositId);
1258
+ const { escrowAddress, onchainDepositId } = parseDepositId(depositId);
978
1259
  return {
979
1260
  onchainDepositId,
980
1261
  escrowArg: escrowAddress ? { escrowAddress } : {}
@@ -989,7 +1270,7 @@ function createCashClient(options) {
989
1270
  const signaled = order.fills.filter((f) => f.status === "SIGNALED");
990
1271
  const liveIntent = signaled.some((f) => isFillLive(f, nowSeconds));
991
1272
  const expiredIntent = signaled.length > 0 && !liveIntent;
992
- if (order.pendingAmount > 0n && liveIntent) {
1273
+ if (liveIntent || order.pendingAmount > 0n && signaled.length === 0) {
993
1274
  throw errors.activeIntentBlocksWithdrawal(depositId);
994
1275
  }
995
1276
  if (availableAmount(order) <= 0n && order.pendingAmount === 0n) {
@@ -1030,22 +1311,104 @@ function createCashClient(options) {
1030
1311
  txOverrides: attribution
1031
1312
  });
1032
1313
  } catch (err) {
1033
- throw mapChainError("approve", err);
1314
+ throw mapChainError("approve", err, { requiredAmount: amount });
1034
1315
  }
1035
1316
  if (allowance.hadAllowance || !allowance.hash) return;
1036
- const receipt = await client.publicClient.waitForTransactionReceipt({ hash: allowance.hash });
1317
+ let receipt;
1318
+ try {
1319
+ receipt = await client.publicClient.waitForTransactionReceipt({ hash: allowance.hash });
1320
+ } catch (err) {
1321
+ throw errors.transactionStatusUnknown(allowance.hash, err, "approve");
1322
+ }
1037
1323
  if (receipt.status === "reverted") throw errors.transactionFailed(allowance.hash);
1324
+ let lastReadError;
1038
1325
  for (let attempt = 0; attempt < 15; attempt++) {
1039
- const visible = await client.publicClient.readContract({
1040
- address: token,
1041
- abi: ERC20_ALLOWANCE_ABI,
1042
- functionName: "allowance",
1043
- args: [owner, escrow]
1044
- });
1045
- if (visible >= amount) return;
1326
+ try {
1327
+ const visible = await client.publicClient.readContract({
1328
+ address: token,
1329
+ abi: ERC20_ALLOWANCE_ABI,
1330
+ functionName: "allowance",
1331
+ args: [owner, escrow]
1332
+ });
1333
+ if (visible >= amount) return;
1334
+ } catch (err) {
1335
+ lastReadError = err;
1336
+ }
1046
1337
  await sleep(1e3);
1047
1338
  }
1048
- throw errors.allowanceNotVisible(amount);
1339
+ throw errors.allowanceNotVisible(amount, lastReadError);
1340
+ }
1341
+ async function waitForBaseSignerAfterRelay(client, cashoutSigner, sourceSigner, owner, sourceChainId, executed) {
1342
+ if (sourceChainId !== BASE_CHAIN_ID) return;
1343
+ const baseTransactions = (executed.transactions?.origin ?? []).filter(
1344
+ (transaction) => transaction.chainId === BASE_CHAIN_ID
1345
+ );
1346
+ const batchIds = baseTransactions.filter((transaction) => transaction.isBatchTx === true).map((transaction) => transaction.hash);
1347
+ let batchTransactionHashes = [];
1348
+ if (batchIds.length > 0) {
1349
+ let batchError;
1350
+ let batchesComplete = false;
1351
+ for (let attempt = 0; attempt < 20; attempt++) {
1352
+ try {
1353
+ const statuses = await Promise.all(
1354
+ batchIds.map((id) => sourceSigner.getCallsStatus({ id }))
1355
+ );
1356
+ if (statuses.some((status) => status.status === "failure")) {
1357
+ throw new Error("Relay wallet call bundle failed");
1358
+ }
1359
+ if (statuses.every((status) => status.status === "success")) {
1360
+ batchTransactionHashes = statuses.flatMap(
1361
+ (status) => (status.receipts ?? []).map((receipt) => receipt.transactionHash)
1362
+ );
1363
+ batchesComplete = true;
1364
+ break;
1365
+ }
1366
+ } catch (err) {
1367
+ batchError = err;
1368
+ }
1369
+ await sleep(250);
1370
+ }
1371
+ if (!batchesComplete) {
1372
+ throw batchError ?? new Error("Relay wallet call bundle did not complete");
1373
+ }
1374
+ }
1375
+ const hashes = [
1376
+ ...baseTransactions.filter((transaction) => transaction.isBatchTx !== true).map((transaction) => transaction.hash),
1377
+ ...batchTransactionHashes
1378
+ ];
1379
+ if (hashes.length === 0) return;
1380
+ let transactions;
1381
+ let lastLookupError;
1382
+ for (let attempt = 0; attempt < 20; attempt++) {
1383
+ try {
1384
+ transactions = await Promise.all(
1385
+ hashes.map((hash) => client.publicClient.getTransaction({ hash }))
1386
+ );
1387
+ break;
1388
+ } catch (err) {
1389
+ lastLookupError = err;
1390
+ await sleep(250);
1391
+ }
1392
+ }
1393
+ if (!transactions) throw lastLookupError;
1394
+ const ownerNonces = transactions.filter((transaction) => transaction.from.toLowerCase() === owner.toLowerCase()).map((transaction) => transaction.nonce);
1395
+ if (ownerNonces.length === 0) return;
1396
+ const afterRelay = Math.max(...ownerNonces) + 1;
1397
+ let lastNonceError;
1398
+ for (let attempt = 0; attempt < 20; attempt++) {
1399
+ try {
1400
+ const pendingHex = await cashoutSigner.transport.request({
1401
+ method: "eth_getTransactionCount",
1402
+ params: [owner, "pending"]
1403
+ });
1404
+ if (Number(BigInt(pendingHex)) >= afterRelay) return;
1405
+ } catch (err) {
1406
+ lastNonceError = err;
1407
+ }
1408
+ await sleep(250);
1409
+ }
1410
+ if (lastNonceError) throw lastNonceError;
1411
+ throw new Error(`Signer provider did not observe Relay nonce ${afterRelay - 1}`);
1049
1412
  }
1050
1413
  return {
1051
1414
  capabilities,
@@ -1056,8 +1419,10 @@ function createCashClient(options) {
1056
1419
  return quoteRelayToBaseUsdc(input, options.relay);
1057
1420
  },
1058
1421
  async executeSourceQuote(quote, opts) {
1422
+ if (!opts.signer.account) throw errors.signerRequired("executeSourceQuote");
1059
1423
  return executeRelayQuote(quote, opts.signer, {
1060
1424
  ...options.relay ? { relay: options.relay } : {},
1425
+ ...opts.recipient ? { recipient: opts.recipient } : {},
1061
1426
  ...opts.onProgress ? { onProgress: opts.onProgress } : {},
1062
1427
  ...opts.disableCapabilitiesCheck !== void 0 ? { disableCapabilitiesCheck: opts.disableCapabilitiesCheck } : {}
1063
1428
  });
@@ -1073,7 +1438,7 @@ function createCashClient(options) {
1073
1438
  });
1074
1439
  },
1075
1440
  async cashout(input, opts) {
1076
- const client = signingClient("cashout", opts);
1441
+ const client = await signingClient("cashout", opts);
1077
1442
  const owner = opts.signer.account.address;
1078
1443
  const payoutInput = validatePayout(input);
1079
1444
  let sourceResult;
@@ -1081,6 +1446,7 @@ function createCashClient(options) {
1081
1446
  if (input.source) {
1082
1447
  const sourceSigner = opts.sourceSigner ?? (input.source.chainId === BASE_CHAIN_ID ? opts.signer : void 0);
1083
1448
  if (!sourceSigner?.account) throw errors.signerRequired("source cashout");
1449
+ await assertWalletChainId(sourceSigner, input.source.chainId, "source cashout");
1084
1450
  if (input.source.recipient !== void 0 && input.source.recipient.toLowerCase() !== owner.toLowerCase()) {
1085
1451
  throw errors.sourceRecipientMismatch(input.source.recipient, owner);
1086
1452
  }
@@ -1098,20 +1464,38 @@ function createCashClient(options) {
1098
1464
  throw errors.amountBelowMinimum(relayQuote.outputAmount, MIN_CASHOUT_AMOUNT);
1099
1465
  }
1100
1466
  cashoutAmount = relayQuote.outputAmount;
1101
- const depositInput2 = { amount: cashoutAmount, ...payoutInput };
1467
+ const depositInput2 = validateDepositInput(cashoutAmount, input, payoutInput);
1102
1468
  const params2 = await buildDepositParams(client, depositInput2);
1103
1469
  const escrow2 = client.escrowV2Address ?? client.escrowAddress;
1104
1470
  await settleAllowance(client, params2.token, owner, escrow2, depositInput2.amount);
1105
1471
  const executed = await executeRelayQuote(relayQuote.raw, sourceSigner, {
1106
1472
  ...options.relay ? { relay: options.relay } : {},
1473
+ recipient: owner,
1107
1474
  ...opts.onSourceProgress ? { onProgress: opts.onSourceProgress } : {},
1108
1475
  ...opts.disableSourceCapabilitiesCheck !== void 0 ? { disableCapabilitiesCheck: opts.disableSourceCapabilitiesCheck } : {}
1109
1476
  });
1110
- sourceResult = {
1477
+ const routedSource = {
1111
1478
  amount: cashoutAmount,
1112
1479
  ...executed.requestId ? { requestId: executed.requestId } : {},
1113
- txHashes: executed.txHashes
1480
+ txHashes: executed.txHashes,
1481
+ ...executed.transactions ? { transactions: executed.transactions } : {}
1114
1482
  };
1483
+ sourceResult = routedSource;
1484
+ try {
1485
+ await waitForBaseSignerAfterRelay(
1486
+ client,
1487
+ opts.signer,
1488
+ sourceSigner,
1489
+ owner,
1490
+ input.source.chainId,
1491
+ executed
1492
+ );
1493
+ } catch (err) {
1494
+ throw errors.sourceRouteCompletedCashoutFailed(
1495
+ routedSource,
1496
+ mapChainError("resolve same-chain Relay nonce", err)
1497
+ );
1498
+ }
1115
1499
  const attributedParams2 = { ...params2, txOverrides: attribution };
1116
1500
  const send2 = async () => {
1117
1501
  try {
@@ -1128,10 +1512,26 @@ function createCashClient(options) {
1128
1512
  try {
1129
1513
  hash2 = await send2();
1130
1514
  } catch (err) {
1131
- throw mapChainError("createDeposit", err);
1515
+ const mapped = mapChainError("createDeposit", err, {
1516
+ requiredAmount: depositInput2.amount
1517
+ });
1518
+ if (isKnownPreBroadcastFailure(err, mapped)) {
1519
+ throw errors.sourceRouteCompletedCashoutFailed(routedSource, mapped);
1520
+ }
1521
+ throw errors.sourceCashoutSubmissionUnknown(routedSource, owner, mapped);
1522
+ }
1523
+ let receipt2;
1524
+ try {
1525
+ receipt2 = await client.publicClient.waitForTransactionReceipt({ hash: hash2 });
1526
+ } catch (err) {
1527
+ throw errors.sourceCashoutStatusUnknown(routedSource, hash2, err);
1528
+ }
1529
+ if (receipt2.status === "reverted") {
1530
+ throw errors.sourceRouteCompletedCashoutFailed(
1531
+ routedSource,
1532
+ errors.transactionFailed(hash2)
1533
+ );
1132
1534
  }
1133
- const receipt2 = await client.publicClient.waitForTransactionReceipt({ hash: hash2 });
1134
- if (receipt2.status === "reverted") throw errors.transactionFailed(hash2);
1135
1535
  const abi2 = client.escrowV2Abi ?? client.escrowAbi;
1136
1536
  const resolved2 = resolveCashDepositId({ logs: receipt2.logs, abi: abi2 });
1137
1537
  if (!resolved2) throw errors.depositResolutionFailed(hash2);
@@ -1145,10 +1545,10 @@ function createCashClient(options) {
1145
1545
  escrowAddress: resolved2.escrowAddress,
1146
1546
  onchainDepositId: resolved2.onchainDepositId,
1147
1547
  order: order2,
1148
- source: sourceResult
1548
+ source: routedSource
1149
1549
  };
1150
1550
  }
1151
- const depositInput = validateInput(input);
1551
+ const depositInput = validateDepositInput(input.amount, input, payoutInput);
1152
1552
  const params = await buildDepositParams(client, depositInput);
1153
1553
  const escrow = client.escrowV2Address ?? client.escrowAddress;
1154
1554
  await settleAllowance(client, params.token, owner, escrow, depositInput.amount);
@@ -1168,9 +1568,23 @@ function createCashClient(options) {
1168
1568
  try {
1169
1569
  hash = await send();
1170
1570
  } catch (err) {
1171
- throw mapChainError("createDeposit", err);
1571
+ const mapped = mapChainError("createDeposit", err, {
1572
+ requiredAmount: depositInput.amount
1573
+ });
1574
+ if (isKnownPreBroadcastFailure(err, mapped)) throw mapped;
1575
+ throw errors.transactionSubmissionUnknown("cashout", err, {
1576
+ kind: "inspect-base-cashout-submission",
1577
+ amount: depositInput.amount.toString(),
1578
+ depositor: owner,
1579
+ txHashes: []
1580
+ });
1581
+ }
1582
+ let receipt;
1583
+ try {
1584
+ receipt = await client.publicClient.waitForTransactionReceipt({ hash });
1585
+ } catch (err) {
1586
+ throw errors.transactionStatusUnknown(hash, err, "cashout");
1172
1587
  }
1173
- const receipt = await client.publicClient.waitForTransactionReceipt({ hash });
1174
1588
  if (receipt.status === "reverted") throw errors.transactionFailed(hash);
1175
1589
  const abi = client.escrowV2Abi ?? client.escrowAbi;
1176
1590
  const resolved = resolveCashDepositId({ logs: receipt.logs, abi });
@@ -1190,7 +1604,7 @@ function createCashClient(options) {
1190
1604
  },
1191
1605
  async prepare(input) {
1192
1606
  if (input.source) throw errors.sourceRouteUnsupportedInPrepare();
1193
- const depositInput = validateInput(input);
1607
+ const depositInput = validateDepositInput(input.amount, input);
1194
1608
  const params = await buildDepositParams(readClient, depositInput);
1195
1609
  const { prepared } = await readClient.prepareCreateDeposit({
1196
1610
  ...params,
@@ -1229,13 +1643,46 @@ function createCashClient(options) {
1229
1643
  return fetchOrder(depositId);
1230
1644
  },
1231
1645
  async buyer(address) {
1232
- const intents = await readClient.indexer.getOwnerIntents(address, CASH_ORDER_STATUSES);
1646
+ let intents;
1647
+ try {
1648
+ intents = await readClient.indexer.getOwnerIntents(address, CASH_ORDER_STATUSES);
1649
+ } catch (err) {
1650
+ throw errors.indexerUnavailable("buyer profile", err);
1651
+ }
1233
1652
  return deriveBuyerProfile(address, intents);
1234
1653
  },
1235
1654
  async orders(owner, opts = {}) {
1236
1655
  const { inFlight = false, limit = 100 } = opts;
1237
- const deposits = await readClient.indexer.getDeposits({ depositor: owner }, { limit });
1238
- 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));
1656
+ let deposits;
1657
+ try {
1658
+ deposits = await readClient.indexer.getDepositsWithRelations(
1659
+ { depositor: owner },
1660
+ { limit }
1661
+ );
1662
+ } catch (err) {
1663
+ throw errors.indexerUnavailable("orders", err);
1664
+ }
1665
+ const catalog = getPaymentMethodsCatalog(BASE_CHAIN_ID, environment);
1666
+ const derived = deposits.flatMap((deposit) => {
1667
+ if (deposit.token.toLowerCase() !== BASE_USDC_ADDRESS.toLowerCase()) return [];
1668
+ const payouts = derivePayouts(
1669
+ deposit.paymentMethods ?? [],
1670
+ deposit.currencies ?? [],
1671
+ catalog
1672
+ );
1673
+ if (payouts.length !== 1 || !payouts.every((payout) => payout.pricing.marketRate && payout.pricing.spreadBps === 0)) {
1674
+ return [];
1675
+ }
1676
+ return [
1677
+ deriveCashOrder(deposit.id, [], {
1678
+ ...depositOrderOptions(deposit),
1679
+ payouts,
1680
+ // List rows carry no intent detail - a positive outstanding
1681
+ // amount is treated conservatively as a live lock.
1682
+ fillsIncluded: false
1683
+ })
1684
+ ];
1685
+ }).filter((o) => o.totalAmount >= MIN_CASHOUT_AMOUNT).sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
1239
1686
  return inFlight ? derived.filter((o) => o.isInFlight) : derived;
1240
1687
  },
1241
1688
  async *watch(depositId, opts = {}) {
@@ -1265,7 +1712,7 @@ function createCashClient(options) {
1265
1712
  }
1266
1713
  },
1267
1714
  async withdraw(depositId, opts) {
1268
- const client = signingClient("withdraw", opts);
1715
+ const client = await signingClient("withdraw", opts);
1269
1716
  if (opts.amount !== void 0) {
1270
1717
  const { onchainDepositId: onchainDepositId2, escrowArg: escrowArg2 } = await partialWithdrawContext(
1271
1718
  depositId,
@@ -1363,7 +1810,7 @@ function createCashClient(options) {
1363
1810
  return { txs, steps };
1364
1811
  },
1365
1812
  async topUp(depositId, amount, opts) {
1366
- const client = signingClient("topUp", opts);
1813
+ const client = await signingClient("topUp", opts);
1367
1814
  const { onchainDepositId, escrowArg } = await topUpContext(depositId, amount);
1368
1815
  const owner = opts.signer.account.address;
1369
1816
  const escrow = escrowArg.escrowAddress ?? client.escrowV2Address ?? client.escrowAddress;
@@ -1418,6 +1865,40 @@ function createCashClient(options) {
1418
1865
  };
1419
1866
  }
1420
1867
  var bigintString = z.string().regex(/^-?\d+$/, "expected a decimal bigint string");
1868
+ var nonNegativeBigintString = z.string().regex(/^\d+$/, "expected a non-negative decimal bigint string");
1869
+ var relayTransactionJsonSchema = z.object({
1870
+ hash: z.string(),
1871
+ chainId: z.number(),
1872
+ isBatchTx: z.boolean().optional()
1873
+ });
1874
+ var relayTransactionsJsonSchema = z.object({
1875
+ origin: z.array(relayTransactionJsonSchema),
1876
+ destination: z.array(relayTransactionJsonSchema)
1877
+ }).strict();
1878
+ var cashAssetJsonSchema = z.object({
1879
+ chainId: z.number(),
1880
+ address: z.string(),
1881
+ symbol: z.string(),
1882
+ decimals: z.number(),
1883
+ name: z.string().optional(),
1884
+ isNative: z.boolean().optional()
1885
+ });
1886
+ var cashChainJsonSchema = z.object({
1887
+ id: z.number(),
1888
+ name: z.string(),
1889
+ displayName: z.string(),
1890
+ disabled: z.boolean(),
1891
+ depositEnabled: z.boolean(),
1892
+ blockProductionLagging: z.boolean(),
1893
+ vmType: z.string().optional(),
1894
+ tokens: z.array(cashAssetJsonSchema)
1895
+ });
1896
+ var cashSourceCapabilitiesJsonSchema = z.object({
1897
+ destination: cashAssetJsonSchema,
1898
+ chains: z.array(cashChainJsonSchema),
1899
+ source: z.literal("relay-sdk"),
1900
+ asOf: z.number()
1901
+ });
1421
1902
  var cashOrderStateSchema = z.enum([
1422
1903
  "awaiting-buyer",
1423
1904
  "matched",
@@ -1430,18 +1911,18 @@ var intentStatusSchema = z.enum(["SIGNALED", "FULFILLED", "PRUNED", "MANUALLY_RE
1430
1911
  var cashFillJsonSchema = z.object({
1431
1912
  intentHash: z.string(),
1432
1913
  status: intentStatusSchema,
1433
- amount: bigintString,
1914
+ amount: nonNegativeBigintString,
1434
1915
  buyer: z.string(),
1435
1916
  currency: z.string().optional(),
1436
1917
  currencyHash: z.string().optional(),
1437
1918
  rate: z.number().optional(),
1438
- conversionRate: bigintString.optional(),
1919
+ conversionRate: nonNegativeBigintString.optional(),
1439
1920
  fiatOwed: z.number().optional(),
1440
1921
  fiatPaid: z.number().optional(),
1441
1922
  paidCurrency: z.string().optional(),
1442
1923
  paymentId: z.string().optional(),
1443
1924
  paidAt: z.number().optional(),
1444
- releasedAmount: bigintString.optional(),
1925
+ releasedAmount: nonNegativeBigintString.optional(),
1445
1926
  fillLatencySeconds: z.number().optional(),
1446
1927
  isExpired: z.boolean().optional(),
1447
1928
  signaledAt: z.number().optional(),
@@ -1480,10 +1961,10 @@ var cashOrderJsonSchema = z.object({
1480
1961
  depositId: z.string(),
1481
1962
  state: cashOrderStateSchema,
1482
1963
  fills: z.array(cashFillJsonSchema),
1483
- totalAmount: bigintString,
1484
- filledAmount: bigintString,
1485
- pendingAmount: bigintString,
1486
- returnedAmount: bigintString,
1964
+ totalAmount: nonNegativeBigintString,
1965
+ filledAmount: nonNegativeBigintString,
1966
+ pendingAmount: nonNegativeBigintString,
1967
+ returnedAmount: nonNegativeBigintString,
1487
1968
  nextActions: z.array(cashNextActionSchema),
1488
1969
  primaryIntentHash: z.string().optional(),
1489
1970
  matchedAt: z.number().optional(),
@@ -1498,7 +1979,7 @@ var cashOrderJsonSchema = z.object({
1498
1979
  var cashEstimateJsonSchema = z.object({
1499
1980
  kind: z.literal("oracle-estimate"),
1500
1981
  currency: z.string(),
1501
- amount: bigintString,
1982
+ amount: nonNegativeBigintString,
1502
1983
  rate: z.number(),
1503
1984
  receiveAmount: z.number(),
1504
1985
  asOf: z.number(),
@@ -1514,7 +1995,7 @@ var cashEstimateJsonSchema = z.object({
1514
1995
  name: z.string().optional(),
1515
1996
  isNative: z.boolean().optional()
1516
1997
  }),
1517
- inputAmount: bigintString,
1998
+ inputAmount: nonNegativeBigintString,
1518
1999
  relayQuote: z.object({
1519
2000
  requestId: z.string().optional(),
1520
2001
  source: z.object({
@@ -1533,8 +2014,8 @@ var cashEstimateJsonSchema = z.object({
1533
2014
  name: z.string().optional(),
1534
2015
  isNative: z.boolean().optional()
1535
2016
  }),
1536
- inputAmount: bigintString,
1537
- outputAmount: bigintString,
2017
+ inputAmount: nonNegativeBigintString,
2018
+ outputAmount: nonNegativeBigintString,
1538
2019
  rate: z.number().optional(),
1539
2020
  timeEstimateSeconds: z.number().optional(),
1540
2021
  fees: z.unknown().optional(),
@@ -1542,7 +2023,7 @@ var cashEstimateJsonSchema = z.object({
1542
2023
  z.object({
1543
2024
  to: z.string(),
1544
2025
  data: z.string(),
1545
- value: bigintString,
2026
+ value: nonNegativeBigintString,
1546
2027
  chainId: z.number()
1547
2028
  })
1548
2029
  ),
@@ -1557,9 +2038,39 @@ var cashEstimateJsonSchema = z.object({
1557
2038
  var preparedTransactionJsonSchema = z.object({
1558
2039
  to: z.string(),
1559
2040
  data: z.string(),
1560
- value: bigintString,
2041
+ value: nonNegativeBigintString,
1561
2042
  chainId: z.number()
1562
2043
  });
2044
+ var relayQuoteJsonSchema = z.object({
2045
+ requestId: z.string().optional(),
2046
+ source: cashAssetJsonSchema,
2047
+ destination: cashAssetJsonSchema,
2048
+ inputAmount: nonNegativeBigintString,
2049
+ outputAmount: nonNegativeBigintString,
2050
+ rate: z.number().optional(),
2051
+ timeEstimateSeconds: z.number().optional(),
2052
+ fees: z.unknown().optional(),
2053
+ txs: z.array(preparedTransactionJsonSchema),
2054
+ raw: z.unknown()
2055
+ });
2056
+ var relayStatusJsonSchema = z.object({
2057
+ requestId: z.string(),
2058
+ status: z.enum(["refund", "waiting", "depositing", "failure", "pending", "submitted", "success"]),
2059
+ details: z.string().optional(),
2060
+ inTxHashes: z.array(z.string()),
2061
+ txHashes: z.array(z.string()),
2062
+ updatedAt: z.number().optional(),
2063
+ originChainId: z.number().optional(),
2064
+ destinationChainId: z.number().optional(),
2065
+ quoteCreatedAt: z.number().optional(),
2066
+ raw: z.unknown()
2067
+ });
2068
+ var relayExecutionResultJsonSchema = z.object({
2069
+ requestId: z.string().optional(),
2070
+ txHashes: z.array(z.string()),
2071
+ transactions: relayTransactionsJsonSchema.optional(),
2072
+ quote: z.unknown()
2073
+ });
1563
2074
  var cashPreparedStepJsonSchema = z.object({
1564
2075
  kind: z.enum([
1565
2076
  "approve",
@@ -1575,12 +2086,13 @@ var cashoutResultJsonSchema = z.object({
1575
2086
  depositId: z.string(),
1576
2087
  txHash: z.string(),
1577
2088
  escrowAddress: z.string(),
1578
- onchainDepositId: bigintString,
2089
+ onchainDepositId: nonNegativeBigintString,
1579
2090
  order: cashOrderJsonSchema,
1580
2091
  source: z.object({
1581
- amount: bigintString,
2092
+ amount: nonNegativeBigintString,
1582
2093
  requestId: z.string().optional(),
1583
- txHashes: z.array(z.string())
2094
+ txHashes: z.array(z.string()),
2095
+ transactions: relayTransactionsJsonSchema.optional()
1584
2096
  }).optional()
1585
2097
  });
1586
2098
  var prepareResultJsonSchema = z.object({
@@ -1610,39 +2122,7 @@ var cashCapabilitiesJsonSchema = z.object({
1610
2122
  chainId: z.number(),
1611
2123
  token: z.object({ address: z.string(), symbol: z.literal("USDC"), decimals: z.number() })
1612
2124
  }),
1613
- relay: z.object({
1614
- destination: z.object({
1615
- chainId: z.number(),
1616
- address: z.string(),
1617
- symbol: z.string(),
1618
- decimals: z.number(),
1619
- name: z.string().optional(),
1620
- isNative: z.boolean().optional()
1621
- }),
1622
- chains: z.array(
1623
- z.object({
1624
- id: z.number(),
1625
- name: z.string(),
1626
- displayName: z.string(),
1627
- disabled: z.boolean(),
1628
- depositEnabled: z.boolean(),
1629
- blockProductionLagging: z.boolean(),
1630
- vmType: z.string().optional(),
1631
- tokens: z.array(
1632
- z.object({
1633
- chainId: z.number(),
1634
- address: z.string(),
1635
- symbol: z.string(),
1636
- decimals: z.number(),
1637
- name: z.string().optional(),
1638
- isNative: z.boolean().optional()
1639
- })
1640
- )
1641
- })
1642
- ),
1643
- source: z.literal("relay-sdk"),
1644
- asOf: z.number()
1645
- }).optional()
2125
+ relay: cashSourceCapabilitiesJsonSchema.optional()
1646
2126
  }),
1647
2127
  platforms: z.array(
1648
2128
  z.object({
@@ -1653,15 +2133,98 @@ var cashCapabilitiesJsonSchema = z.object({
1653
2133
  })
1654
2134
  ),
1655
2135
  currencies: z.array(z.string()),
1656
- amount: z.object({ min: bigintString, recommendedMin: bigintString, max: z.null() }),
2136
+ amount: z.object({
2137
+ min: nonNegativeBigintString,
2138
+ recommendedMin: nonNegativeBigintString,
2139
+ max: z.null()
2140
+ }),
1657
2141
  pricing: z.object({ kind: z.literal("oracle-market-rate"), spreadBps: z.literal(0) })
1658
2142
  });
2143
+ function defineCashErrorCodes(codes) {
2144
+ return codes;
2145
+ }
2146
+ var CASH_ERROR_CODES = defineCashErrorCodes([
2147
+ "ORACLE_UNSUPPORTED_CURRENCY",
2148
+ "ORACLE_READ_FAILED",
2149
+ "UNSUPPORTED_PLATFORM",
2150
+ "UNSUPPORTED_PLATFORM_CURRENCY",
2151
+ "AMOUNT_BELOW_MINIMUM",
2152
+ "INVALID_INTENT_AMOUNT_RANGE",
2153
+ "ACTIVE_INTENT_BLOCKS_WITHDRAWAL",
2154
+ "NOTHING_TO_WITHDRAW",
2155
+ "INSUFFICIENT_AVAILABLE_FUNDS",
2156
+ "INSUFFICIENT_TOKEN_BALANCE",
2157
+ "ORDER_NOT_ACTIVE",
2158
+ "INVALID_DEPOSIT_ID",
2159
+ "ESCROW_PAUSED",
2160
+ "INDEXER_LAG",
2161
+ "INDEXER_UNAVAILABLE",
2162
+ "ORDER_NOT_FOUND",
2163
+ "PAYEE_REGISTRATION_FAILED",
2164
+ "PAYEE_VERIFICATION_REQUIRED",
2165
+ "SOURCE_ROUTE_UNSUPPORTED_IN_PREPARE",
2166
+ "SOURCE_RECIPIENT_MISMATCH",
2167
+ "SOURCE_CAPABILITIES_FAILED",
2168
+ "SOURCE_QUOTE_FAILED",
2169
+ "SOURCE_EXECUTION_FAILED",
2170
+ "SOURCE_STATUS_FAILED",
2171
+ "SOURCE_ROUTE_COMPLETED_CASHOUT_FAILED",
2172
+ "SOURCE_CASHOUT_SUBMISSION_UNKNOWN",
2173
+ "SOURCE_CASHOUT_STATUS_UNKNOWN",
2174
+ "DEPOSIT_RESOLUTION_FAILED",
2175
+ "ALLOWANCE_NOT_VISIBLE",
2176
+ "SIGNER_REQUIRED",
2177
+ "SIGNER_CHAIN_MISMATCH",
2178
+ "SIGNER_CHAIN_UNAVAILABLE",
2179
+ "WATCH_TIMEOUT",
2180
+ "TRANSACTION_FAILED",
2181
+ "TRANSACTION_SUBMISSION_UNKNOWN",
2182
+ "TRANSACTION_STATUS_UNKNOWN"
2183
+ ]);
2184
+ var cashSourceRecoveryJsonShape = {
2185
+ amount: nonNegativeBigintString,
2186
+ requestId: z.string().optional(),
2187
+ txHashes: z.array(z.string()),
2188
+ transactions: relayTransactionsJsonSchema.optional()
2189
+ };
2190
+ var cashErrorRecoveryJsonSchema = z.discriminatedUnion("kind", [
2191
+ z.object({
2192
+ ...cashSourceRecoveryJsonShape,
2193
+ kind: z.literal("retry-base-usdc-cashout")
2194
+ }).strict(),
2195
+ z.object({
2196
+ ...cashSourceRecoveryJsonShape,
2197
+ kind: z.literal("inspect-base-cashout-transaction"),
2198
+ depositTxHash: z.string()
2199
+ }).strict(),
2200
+ z.object({
2201
+ ...cashSourceRecoveryJsonShape,
2202
+ kind: z.literal("inspect-base-cashout-submission"),
2203
+ depositor: z.string()
2204
+ }).strict(),
2205
+ z.object({
2206
+ kind: z.literal("inspect-relay-route"),
2207
+ requestId: z.string().optional(),
2208
+ txHashes: z.array(z.string()),
2209
+ transactions: relayTransactionsJsonSchema.optional()
2210
+ }).strict(),
2211
+ z.object({
2212
+ kind: z.literal("inspect-base-operation-submission"),
2213
+ operation: z.string()
2214
+ }).strict(),
2215
+ z.object({
2216
+ kind: z.literal("inspect-base-transaction"),
2217
+ transactionHash: z.string(),
2218
+ operation: z.string()
2219
+ }).strict()
2220
+ ]);
1659
2221
  var cashErrorJsonSchema = z.object({
1660
- code: z.string(),
2222
+ code: z.enum(CASH_ERROR_CODES),
1661
2223
  message: z.string(),
1662
2224
  retryable: z.boolean(),
1663
- remediation: z.string()
1664
- });
2225
+ remediation: z.string(),
2226
+ recovery: cashErrorRecoveryJsonSchema.optional()
2227
+ }).strict();
1665
2228
 
1666
2229
  // src/codecs/json.ts
1667
2230
  function omitUndefined(obj) {
@@ -1692,35 +2255,38 @@ function fillToJson(fill) {
1692
2255
  });
1693
2256
  }
1694
2257
  function fillFromJson(json) {
2258
+ const parsed = cashFillJsonSchema.parse(json);
1695
2259
  return omitUndefined({
1696
- ...json,
1697
- amount: BigInt(json.amount),
1698
- conversionRate: json.conversionRate !== void 0 ? BigInt(json.conversionRate) : void 0,
1699
- releasedAmount: json.releasedAmount !== void 0 ? BigInt(json.releasedAmount) : void 0
2260
+ ...parsed,
2261
+ amount: BigInt(parsed.amount),
2262
+ conversionRate: parsed.conversionRate !== void 0 ? BigInt(parsed.conversionRate) : void 0,
2263
+ releasedAmount: parsed.releasedAmount !== void 0 ? BigInt(parsed.releasedAmount) : void 0
1700
2264
  });
1701
2265
  }
1702
2266
  function orderToJson(order) {
1703
- return omitUndefined({
1704
- depositId: order.depositId,
1705
- state: order.state,
1706
- fills: order.fills.map(fillToJson),
1707
- totalAmount: order.totalAmount.toString(),
1708
- filledAmount: order.filledAmount.toString(),
1709
- pendingAmount: order.pendingAmount.toString(),
1710
- returnedAmount: order.returnedAmount.toString(),
1711
- nextActions: order.nextActions,
1712
- primaryIntentHash: order.primaryIntentHash,
1713
- matchedAt: order.matchedAt,
1714
- deliveredAt: order.deliveredAt,
1715
- updatedAt: order.updatedAt,
1716
- intentCount: order.intentCount,
1717
- payouts: order.payouts?.map(
1718
- (p) => omitUndefined({ ...p, pricing: omitUndefined({ ...p.pricing }) })
1719
- ),
1720
- successRateBps: order.successRateBps,
1721
- isInFlight: order.isInFlight,
1722
- withdrawn: order.withdrawn
1723
- });
2267
+ return cashOrderJsonSchema.parse(
2268
+ omitUndefined({
2269
+ depositId: order.depositId,
2270
+ state: order.state,
2271
+ fills: order.fills.map(fillToJson),
2272
+ totalAmount: order.totalAmount.toString(),
2273
+ filledAmount: order.filledAmount.toString(),
2274
+ pendingAmount: order.pendingAmount.toString(),
2275
+ returnedAmount: order.returnedAmount.toString(),
2276
+ nextActions: order.nextActions,
2277
+ primaryIntentHash: order.primaryIntentHash,
2278
+ matchedAt: order.matchedAt,
2279
+ deliveredAt: order.deliveredAt,
2280
+ updatedAt: order.updatedAt,
2281
+ intentCount: order.intentCount,
2282
+ payouts: order.payouts?.map(
2283
+ (p) => omitUndefined({ ...p, pricing: omitUndefined({ ...p.pricing }) })
2284
+ ),
2285
+ successRateBps: order.successRateBps,
2286
+ isInFlight: order.isInFlight,
2287
+ withdrawn: order.withdrawn
2288
+ })
2289
+ );
1724
2290
  }
1725
2291
  function orderFromJson(json) {
1726
2292
  const parsed = cashOrderJsonSchema.parse(json);
@@ -1746,6 +2312,7 @@ function estimateToJson(estimate) {
1746
2312
  inputAmount: estimate.source.relayQuote.inputAmount.toString(),
1747
2313
  outputAmount: estimate.source.relayQuote.outputAmount.toString(),
1748
2314
  txs: estimate.source.relayQuote.txs.map(preparedTxToJson),
2315
+ ...estimate.source.relayQuote.fees !== void 0 ? { fees: sanitizeRelayValue(estimate.source.relayQuote.fees) } : {},
1749
2316
  raw: sanitizeRelayQuoteRaw(estimate.source.relayQuote.raw)
1750
2317
  }
1751
2318
  } : void 0
@@ -1764,11 +2331,108 @@ function estimateFromJson(json) {
1764
2331
  ...parsed.source.relayQuote,
1765
2332
  inputAmount: BigInt(parsed.source.relayQuote.inputAmount),
1766
2333
  outputAmount: BigInt(parsed.source.relayQuote.outputAmount),
1767
- txs: parsed.source.relayQuote.txs.map(preparedTxFromJson)
2334
+ txs: parsed.source.relayQuote.txs.map(preparedTxFromJson),
2335
+ ...parsed.source.relayQuote.fees !== void 0 ? { fees: restoreRelayValue(parsed.source.relayQuote.fees) } : {},
2336
+ raw: restoreRelayQuoteRaw(parsed.source.relayQuote.raw)
1768
2337
  }
1769
2338
  } : void 0
1770
2339
  });
1771
2340
  }
2341
+ function cashAssetFromJson(asset) {
2342
+ return {
2343
+ chainId: asset.chainId,
2344
+ address: asset.address,
2345
+ symbol: asset.symbol,
2346
+ decimals: asset.decimals,
2347
+ ...asset.name !== void 0 ? { name: asset.name } : {},
2348
+ ...asset.isNative !== void 0 ? { isNative: asset.isNative } : {}
2349
+ };
2350
+ }
2351
+ function relayQuoteToJson(quote) {
2352
+ return relayQuoteJsonSchema.parse({
2353
+ ...quote.requestId !== void 0 ? { requestId: quote.requestId } : {},
2354
+ source: quote.source,
2355
+ destination: quote.destination,
2356
+ inputAmount: quote.inputAmount.toString(),
2357
+ outputAmount: quote.outputAmount.toString(),
2358
+ ...quote.rate !== void 0 ? { rate: quote.rate } : {},
2359
+ ...quote.timeEstimateSeconds !== void 0 ? { timeEstimateSeconds: quote.timeEstimateSeconds } : {},
2360
+ ...quote.fees !== void 0 ? { fees: sanitizeRelayValue(quote.fees) } : {},
2361
+ txs: quote.txs.map(preparedTxToJson),
2362
+ raw: sanitizeRelayQuoteRaw(quote.raw)
2363
+ });
2364
+ }
2365
+ function relayQuoteFromJson(json) {
2366
+ const parsed = relayQuoteJsonSchema.parse(json);
2367
+ return {
2368
+ ...parsed.requestId !== void 0 ? { requestId: parsed.requestId } : {},
2369
+ source: cashAssetFromJson(parsed.source),
2370
+ destination: cashAssetFromJson(parsed.destination),
2371
+ inputAmount: BigInt(parsed.inputAmount),
2372
+ outputAmount: BigInt(parsed.outputAmount),
2373
+ ...parsed.rate !== void 0 ? { rate: parsed.rate } : {},
2374
+ ...parsed.timeEstimateSeconds !== void 0 ? { timeEstimateSeconds: parsed.timeEstimateSeconds } : {},
2375
+ ...parsed.fees !== void 0 ? { fees: restoreRelayValue(parsed.fees) } : {},
2376
+ txs: parsed.txs.map(preparedTxFromJson),
2377
+ raw: restoreRelayQuoteRaw(parsed.raw)
2378
+ };
2379
+ }
2380
+ function sourceCapabilitiesToJson(capabilities) {
2381
+ return cashSourceCapabilitiesJsonSchema.parse(capabilities);
2382
+ }
2383
+ function sourceCapabilitiesFromJson(json) {
2384
+ const parsed = cashSourceCapabilitiesJsonSchema.parse(json);
2385
+ return {
2386
+ destination: cashAssetFromJson(parsed.destination),
2387
+ chains: parsed.chains.map((chain) => ({
2388
+ id: chain.id,
2389
+ name: chain.name,
2390
+ displayName: chain.displayName,
2391
+ disabled: chain.disabled,
2392
+ depositEnabled: chain.depositEnabled,
2393
+ blockProductionLagging: chain.blockProductionLagging,
2394
+ ...chain.vmType !== void 0 ? { vmType: chain.vmType } : {},
2395
+ tokens: chain.tokens.map(cashAssetFromJson)
2396
+ })),
2397
+ source: parsed.source,
2398
+ asOf: parsed.asOf
2399
+ };
2400
+ }
2401
+ function relayStatusToJson(status) {
2402
+ return relayStatusJsonSchema.parse({ ...status, raw: sanitizeRelayValue(status.raw) });
2403
+ }
2404
+ function relayStatusFromJson(json) {
2405
+ const parsed = relayStatusJsonSchema.parse(json);
2406
+ return {
2407
+ requestId: parsed.requestId,
2408
+ status: parsed.status,
2409
+ ...parsed.details !== void 0 ? { details: parsed.details } : {},
2410
+ inTxHashes: parsed.inTxHashes,
2411
+ txHashes: parsed.txHashes,
2412
+ ...parsed.updatedAt !== void 0 ? { updatedAt: parsed.updatedAt } : {},
2413
+ ...parsed.originChainId !== void 0 ? { originChainId: parsed.originChainId } : {},
2414
+ ...parsed.destinationChainId !== void 0 ? { destinationChainId: parsed.destinationChainId } : {},
2415
+ ...parsed.quoteCreatedAt !== void 0 ? { quoteCreatedAt: parsed.quoteCreatedAt } : {},
2416
+ raw: restoreRelayValue(parsed.raw)
2417
+ };
2418
+ }
2419
+ function relayExecutionResultToJson(result) {
2420
+ return relayExecutionResultJsonSchema.parse({
2421
+ ...result.requestId !== void 0 ? { requestId: result.requestId } : {},
2422
+ txHashes: result.txHashes,
2423
+ ...result.transactions !== void 0 ? { transactions: result.transactions } : {},
2424
+ quote: sanitizeRelayQuoteRaw(result.quote)
2425
+ });
2426
+ }
2427
+ function relayExecutionResultFromJson(json) {
2428
+ const parsed = relayExecutionResultJsonSchema.parse(json);
2429
+ return {
2430
+ ...parsed.requestId !== void 0 ? { requestId: parsed.requestId } : {},
2431
+ txHashes: parsed.txHashes,
2432
+ ...parsed.transactions !== void 0 ? { transactions: parsed.transactions } : {},
2433
+ quote: restoreRelayQuoteRaw(parsed.quote)
2434
+ };
2435
+ }
1772
2436
  function preparedTxToJson(tx) {
1773
2437
  return { to: tx.to, data: tx.data, value: tx.value.toString(), chainId: tx.chainId };
1774
2438
  }
@@ -1887,7 +2551,68 @@ function capabilitiesFromJson(json) {
1887
2551
  }
1888
2552
  };
1889
2553
  }
2554
+ function cashErrorToJson(error) {
2555
+ return cashErrorJsonSchema.parse({
2556
+ code: error.code,
2557
+ message: error.message,
2558
+ retryable: error.retryable,
2559
+ remediation: error.remediation,
2560
+ ...error.recovery ? { recovery: error.recovery } : {}
2561
+ });
2562
+ }
2563
+ function cashErrorFromJson(json) {
2564
+ const parsed = cashErrorJsonSchema.parse(json);
2565
+ let recovery;
2566
+ if (parsed.recovery) {
2567
+ if (parsed.recovery.kind === "inspect-base-transaction") {
2568
+ recovery = {
2569
+ kind: parsed.recovery.kind,
2570
+ transactionHash: parsed.recovery.transactionHash,
2571
+ operation: parsed.recovery.operation
2572
+ };
2573
+ } else if (parsed.recovery.kind === "inspect-base-operation-submission") {
2574
+ recovery = {
2575
+ kind: parsed.recovery.kind,
2576
+ operation: parsed.recovery.operation
2577
+ };
2578
+ } else if (parsed.recovery.kind === "inspect-relay-route") {
2579
+ recovery = {
2580
+ kind: parsed.recovery.kind,
2581
+ txHashes: parsed.recovery.txHashes,
2582
+ ...parsed.recovery.requestId !== void 0 ? { requestId: parsed.recovery.requestId } : {},
2583
+ ...parsed.recovery.transactions !== void 0 ? { transactions: parsed.recovery.transactions } : {}
2584
+ };
2585
+ } else {
2586
+ const common = {
2587
+ amount: parsed.recovery.amount,
2588
+ txHashes: parsed.recovery.txHashes,
2589
+ ...parsed.recovery.requestId !== void 0 ? { requestId: parsed.recovery.requestId } : {},
2590
+ ...parsed.recovery.transactions !== void 0 ? { transactions: parsed.recovery.transactions } : {}
2591
+ };
2592
+ if (parsed.recovery.kind === "retry-base-usdc-cashout") {
2593
+ recovery = { ...common, kind: parsed.recovery.kind };
2594
+ } else if (parsed.recovery.kind === "inspect-base-cashout-submission") {
2595
+ recovery = {
2596
+ ...common,
2597
+ kind: parsed.recovery.kind,
2598
+ depositor: parsed.recovery.depositor
2599
+ };
2600
+ } else {
2601
+ recovery = {
2602
+ ...common,
2603
+ kind: parsed.recovery.kind,
2604
+ depositTxHash: parsed.recovery.depositTxHash
2605
+ };
2606
+ }
2607
+ }
2608
+ }
2609
+ return new CashError({
2610
+ code: parsed.code,
2611
+ message: parsed.message,
2612
+ retryable: parsed.retryable,
2613
+ remediation: parsed.remediation,
2614
+ ...recovery ? { recovery } : {}
2615
+ });
2616
+ }
1890
2617
 
1891
- 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 };
1892
- //# sourceMappingURL=index.js.map
1893
- //# sourceMappingURL=index.js.map
2618
+ 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 };