@sodax/sdk 2.0.0-rc.17 → 2.0.0-rc.19

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.mjs CHANGED
@@ -17,6 +17,7 @@ import * as IconSdkRaw from 'icon-sdk-js';
17
17
  import { ChainGrpcWasmApi, TxGrpcApi, toBase64, fromBase64, MsgExecuteContract, ChainRestAuthApi, BaseAccount, createTransaction } from '@injectivelabs/sdk-ts';
18
18
  import { TxRaw } from 'cosmjs-types/cosmos/tx/v1beta1/tx.js';
19
19
  import { getNetworkEndpoints, Network } from '@injectivelabs/networks';
20
+ import * as v4 from 'valibot';
20
21
  import BigNumber5, { BigNumber } from 'bignumber.js';
21
22
  import { Token, Price } from '@pancakeswap/swap-sdk-core';
22
23
  import { sqrtRatioX96ToPrice, PositionMath, tickToPrice, TickMath, maxLiquidityForAmount0Precise, maxLiquidityForAmount1, maxLiquidityForAmounts } from '@pancakeswap/v3-sdk';
@@ -91,8 +92,8 @@ function serializeCause(cause, depth) {
91
92
  }
92
93
  function sanitizeContext(value, depth) {
93
94
  const out = {};
94
- for (const [key, v] of Object.entries(value)) {
95
- out[key] = sanitizeValue(v, depth + 1);
95
+ for (const [key, v6] of Object.entries(value)) {
96
+ out[key] = sanitizeValue(v6, depth + 1);
96
97
  }
97
98
  return out;
98
99
  }
@@ -103,13 +104,13 @@ function sanitizeValue(value, depth) {
103
104
  if (t === "bigint") return value.toString();
104
105
  if (t === "string" || t === "number" || t === "boolean") return value;
105
106
  if (t === "function" || t === "symbol") return safeString(value);
106
- if (Array.isArray(value)) return value.map((v) => sanitizeValue(v, depth + 1));
107
+ if (Array.isArray(value)) return value.map((v6) => sanitizeValue(v6, depth + 1));
107
108
  if (value instanceof Date) return value.toISOString();
108
109
  if (value instanceof Map) {
109
- return Array.from(value.entries()).map(([k, v]) => [sanitizeValue(k, depth + 1), sanitizeValue(v, depth + 1)]);
110
+ return Array.from(value.entries()).map(([k, v6]) => [sanitizeValue(k, depth + 1), sanitizeValue(v6, depth + 1)]);
110
111
  }
111
112
  if (value instanceof Set) {
112
- return Array.from(value.values()).map((v) => sanitizeValue(v, depth + 1));
113
+ return Array.from(value.values()).map((v6) => sanitizeValue(v6, depth + 1));
113
114
  }
114
115
  if (value instanceof Error) return { name: value.name, message: value.message };
115
116
  if (t === "object") {
@@ -121,9 +122,9 @@ function sanitizeValue(value, depth) {
121
122
  }
122
123
  return safeString(value);
123
124
  }
124
- function safeString(v) {
125
+ function safeString(v6) {
125
126
  try {
126
- return String(v);
127
+ return String(v6);
127
128
  } catch {
128
129
  return "[unserializable]";
129
130
  }
@@ -171,6 +172,7 @@ var SODAX_FEATURES = [
171
172
  "dex",
172
173
  "partner",
173
174
  "recovery",
175
+ "backend",
174
176
  "leverageYield"
175
177
  ];
176
178
 
@@ -2612,7 +2614,7 @@ var RelayChainIdMap = {
2612
2614
  [ChainKeys.STACKS_MAINNET]: 60n
2613
2615
  };
2614
2616
  var INTENT_CHAIN_IDS = Object.values(RelayChainIdMap);
2615
- var IntentRelayChainIdToChainKey = Object.fromEntries(Object.entries(RelayChainIdMap).map(([chainKey, chainId]) => [chainId, chainKey]));
2617
+ var IntentRelayChainIdToChainKey = new Map(Object.entries(RelayChainIdMap).map(([chainKey, chainId]) => [chainId, chainKey]));
2616
2618
  var CHAIN_LOGO_BASE_URL = "https://raw.githubusercontent.com/icon-project/sodax-sdks/main/packages/assets/chain";
2617
2619
  var chainLogo = (key) => `${CHAIN_LOGO_BASE_URL}/${key}.png`;
2618
2620
  var baseChainInfo = {
@@ -4218,7 +4220,7 @@ function isValidWalletProviderForChainKey(chainKey, walletProvider) {
4218
4220
  }
4219
4221
 
4220
4222
  // ../types/dist/index.js
4221
- var CONFIG_VERSION = 215;
4223
+ var CONFIG_VERSION = 217;
4222
4224
  function isEvmSpokeChainConfig(value) {
4223
4225
  return typeof value === "object" && value !== null && value.chain.type === "EVM" && value.chain.key !== HUB_CHAIN_KEY;
4224
4226
  }
@@ -4308,26 +4310,6 @@ function isRawDestinationParams(value) {
4308
4310
  if (typeof obj.dstAddress !== "string") return false;
4309
4311
  return typeof obj.dstChainKey === "string" && spokeChainKeysSet.has(obj.dstChainKey);
4310
4312
  }
4311
- function isSubmitSwapTxResponse(value) {
4312
- return typeof value === "object" && value !== null && typeof value.success === "boolean" && typeof value.message === "string";
4313
- }
4314
- function isSubmitSwapTxStatusResponse(value) {
4315
- if (typeof value !== "object" || value === null) return false;
4316
- const obj = value;
4317
- if (typeof obj.success !== "boolean") return false;
4318
- if (typeof obj.data !== "object" || obj.data === null) return false;
4319
- const data = obj.data;
4320
- if (typeof data.txHash !== "string") return false;
4321
- if (typeof data.srcChainKey !== "string") return false;
4322
- if (typeof data.status !== "string") return false;
4323
- if (typeof data.failedAttempts !== "number") return false;
4324
- if (data.result !== void 0) {
4325
- if (typeof data.result !== "object" || data.result === null) return false;
4326
- const result = data.result;
4327
- if (typeof result.dstIntentTxHash !== "string") return false;
4328
- }
4329
- return true;
4330
- }
4331
4313
  function isBitcoinWalletProviderType(wp) {
4332
4314
  return wp.chainType === "BITCOIN";
4333
4315
  }
@@ -4365,11 +4347,11 @@ async function retry(action, retryCount = DEFAULT_MAX_RETRY, delayMs = DEFAULT_R
4365
4347
  throw new Error(`Retry exceeded MAX_RETRY_DEFAULT=${DEFAULT_MAX_RETRY}`);
4366
4348
  }
4367
4349
  function getRandomBytes(length) {
4368
- const array = new Uint8Array(length);
4350
+ const array3 = new Uint8Array(length);
4369
4351
  for (let i = 0; i < length; i++) {
4370
- array[i] = Math.floor(Math.random() * 256);
4352
+ array3[i] = Math.floor(Math.random() * 256);
4371
4353
  }
4372
- return array;
4354
+ return array3;
4373
4355
  }
4374
4356
  function randomUint256() {
4375
4357
  const bytes = getRandomBytes(32);
@@ -10621,6 +10603,144 @@ var ProtocolIntentsAbi = [
10621
10603
  }
10622
10604
  ],
10623
10605
  stateMutability: "view"
10606
+ },
10607
+ {
10608
+ type: "function",
10609
+ name: "cancelIntent",
10610
+ inputs: [
10611
+ {
10612
+ name: "fromToken",
10613
+ type: "address",
10614
+ internalType: "address"
10615
+ },
10616
+ {
10617
+ name: "toToken",
10618
+ type: "address",
10619
+ internalType: "address"
10620
+ }
10621
+ ],
10622
+ outputs: [],
10623
+ stateMutability: "nonpayable"
10624
+ },
10625
+ {
10626
+ type: "function",
10627
+ name: "getUserIntent",
10628
+ inputs: [
10629
+ {
10630
+ name: "user",
10631
+ type: "address",
10632
+ internalType: "address"
10633
+ },
10634
+ {
10635
+ name: "fromToken",
10636
+ type: "address",
10637
+ internalType: "address"
10638
+ },
10639
+ {
10640
+ name: "toToken",
10641
+ type: "address",
10642
+ internalType: "address"
10643
+ }
10644
+ ],
10645
+ outputs: [
10646
+ {
10647
+ name: "intentHash",
10648
+ type: "bytes32",
10649
+ internalType: "bytes32"
10650
+ }
10651
+ ],
10652
+ stateMutability: "view"
10653
+ },
10654
+ {
10655
+ type: "function",
10656
+ name: "getIntentDetails",
10657
+ inputs: [
10658
+ {
10659
+ name: "intentHash",
10660
+ type: "bytes32",
10661
+ internalType: "bytes32"
10662
+ }
10663
+ ],
10664
+ outputs: [
10665
+ {
10666
+ name: "intent",
10667
+ type: "tuple",
10668
+ internalType: "struct Intents.Intent",
10669
+ components: [
10670
+ {
10671
+ name: "intentId",
10672
+ type: "uint256",
10673
+ internalType: "uint256"
10674
+ },
10675
+ {
10676
+ name: "creator",
10677
+ type: "address",
10678
+ internalType: "address"
10679
+ },
10680
+ {
10681
+ name: "inputToken",
10682
+ type: "address",
10683
+ internalType: "address"
10684
+ },
10685
+ {
10686
+ name: "outputToken",
10687
+ type: "address",
10688
+ internalType: "address"
10689
+ },
10690
+ {
10691
+ name: "inputAmount",
10692
+ type: "uint256",
10693
+ internalType: "uint256"
10694
+ },
10695
+ {
10696
+ name: "minOutputAmount",
10697
+ type: "uint256",
10698
+ internalType: "uint256"
10699
+ },
10700
+ {
10701
+ name: "deadline",
10702
+ type: "uint256",
10703
+ internalType: "uint256"
10704
+ },
10705
+ {
10706
+ name: "allowPartialFill",
10707
+ type: "bool",
10708
+ internalType: "bool"
10709
+ },
10710
+ {
10711
+ name: "srcChain",
10712
+ type: "uint256",
10713
+ internalType: "uint256"
10714
+ },
10715
+ {
10716
+ name: "dstChain",
10717
+ type: "uint256",
10718
+ internalType: "uint256"
10719
+ },
10720
+ {
10721
+ name: "srcAddress",
10722
+ type: "bytes",
10723
+ internalType: "bytes"
10724
+ },
10725
+ {
10726
+ name: "dstAddress",
10727
+ type: "bytes",
10728
+ internalType: "bytes"
10729
+ },
10730
+ {
10731
+ name: "solver",
10732
+ type: "address",
10733
+ internalType: "address"
10734
+ },
10735
+ {
10736
+ name: "data",
10737
+ type: "bytes",
10738
+ internalType: "bytes"
10739
+ }
10740
+ ]
10741
+ }
10742
+ ],
10743
+ stateMutability: "view"
10624
10744
  }
10625
10745
  ];
10626
10746
 
@@ -13478,29 +13598,6 @@ var universalRouterAbi = [
13478
13598
  }
13479
13599
  ];
13480
13600
 
13481
- // src/shared/utils/deepMerge.ts
13482
- function deepMerge(target, source) {
13483
- const result = { ...target };
13484
- for (const key of Object.keys(source)) {
13485
- const sourceVal = source[key];
13486
- const targetVal = target[key];
13487
- if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null && !Array.isArray(targetVal)) {
13488
- result[key] = deepMerge(
13489
- targetVal,
13490
- sourceVal
13491
- );
13492
- } else if (sourceVal !== void 0) {
13493
- result[key] = sourceVal;
13494
- }
13495
- }
13496
- return result;
13497
- }
13498
-
13499
- // src/shared/config/mergeSodaxConfig.ts
13500
- function mergeSodaxConfig(base2, override) {
13501
- return deepMerge(base2, override);
13502
- }
13503
-
13504
13601
  // src/shared/logger.ts
13505
13602
  var consoleLogger = {
13506
13603
  debug: (message, data) => data ? console.debug(message, data) : console.debug(message),
@@ -13528,16 +13625,85 @@ function resolveLogger(option) {
13528
13625
  return option;
13529
13626
  }
13530
13627
 
13628
+ // src/shared/analytics.ts
13629
+ var noopAnalytics = {
13630
+ isEnabled: () => false,
13631
+ emit: () => {
13632
+ },
13633
+ trackResult: (_feature, _action, run) => run()
13634
+ };
13635
+ var LEVEL_RANK = { basic: 0, detailed: 1 };
13636
+ function normalizeFeatures(features) {
13637
+ if (features === void 0) return null;
13638
+ const map = /* @__PURE__ */ new Map();
13639
+ if (Array.isArray(features)) {
13640
+ for (const feature of features) map.set(feature, true);
13641
+ } else {
13642
+ for (const [feature, scope] of Object.entries(features)) {
13643
+ if (scope === true) map.set(feature, true);
13644
+ else if (scope) map.set(feature, new Set(scope.actions));
13645
+ }
13646
+ }
13647
+ return map;
13648
+ }
13649
+ function resolveAnalytics(option) {
13650
+ if (!option) return noopAnalytics;
13651
+ const { tracker, level: configuredLevel = "basic", features } = option;
13652
+ const maxRank = LEVEL_RANK[configuredLevel];
13653
+ const allow = normalizeFeatures(features);
13654
+ const isEnabled = (feature, action, level = "basic") => {
13655
+ if (LEVEL_RANK[level] > maxRank) return false;
13656
+ if (allow === null) return true;
13657
+ const scope = allow.get(feature);
13658
+ if (scope === void 0) return false;
13659
+ if (scope === true) return true;
13660
+ if (action === void 0) return scope.size > 0;
13661
+ return scope.has(action);
13662
+ };
13663
+ const emit = (feature, action, phase, build, level = "basic") => {
13664
+ if (!isEnabled(feature, action, level)) return;
13665
+ try {
13666
+ tracker({ feature, action, phase, level, data: build?.() });
13667
+ } catch {
13668
+ }
13669
+ };
13670
+ const trackResult = async (feature, action, run, data) => {
13671
+ emit(feature, action, "start", data?.start);
13672
+ try {
13673
+ const result = await run();
13674
+ if (result.ok) {
13675
+ const build = data?.success;
13676
+ emit(feature, action, "success", build ? () => build(result.value) : void 0);
13677
+ } else {
13678
+ const build = data?.failure;
13679
+ emit(feature, action, "failure", build ? () => build(result.error) : void 0);
13680
+ }
13681
+ return result;
13682
+ } catch (error) {
13683
+ emit(feature, action, "failure", () => ({ error: error instanceof Error ? error.message : String(error) }));
13684
+ throw error;
13685
+ }
13686
+ };
13687
+ return { isEnabled, emit, trackResult };
13688
+ }
13689
+
13531
13690
  // src/shared/config/ConfigService.ts
13532
13691
  var ConfigService = class {
13533
13692
  sodax;
13534
- api;
13535
- userConfig;
13693
+ // TODO(config-v2): restore `api` / `userConfig` when initialize() dynamic fetch is re-enabled.
13694
+ // private readonly api: BackendApiService;
13695
+ // private readonly userConfig?: SodaxOptions;
13536
13696
  /**
13537
13697
  * SDK log sink. Resolved once at construction and kept independent of {@link sodax} so that
13538
13698
  * {@link initialize}'s dynamic-config swap never clobbers it. Read by services via `config.logger`.
13539
13699
  */
13540
13700
  logger;
13701
+ /**
13702
+ * Analytics emitter. Resolved once at construction and kept independent of {@link sodax} so that
13703
+ * {@link initialize}'s dynamic-config swap never clobbers it. Read by services via `config.analytics`;
13704
+ * disabled (no-op) unless the consumer passed an `analytics` config to `new Sodax(...)`.
13705
+ */
13706
+ analytics;
13541
13707
  /**
13542
13708
  * Global partner fee. Resolved once at construction and kept independent of {@link sodax} so that
13543
13709
  * {@link initialize}'s dynamic-config swap never clobbers it. The backend never supplies it — it is
@@ -13555,29 +13721,17 @@ var ConfigService = class {
13555
13721
  stakedATokenAddressesSet;
13556
13722
  chainToSupportedTokenAddressMap;
13557
13723
  hubAssetToXTokenMap;
13558
- constructor({ api, config, userConfig, logger, fee }) {
13559
- this.api = api;
13724
+ // `api` / `userConfig` are accepted but unused while initialize()'s dynamic fetch is disabled
13725
+ // (see TODO(config-v2) below); restore their assignments when re-enabling.
13726
+ constructor({ api, config, userConfig, logger, analytics, fee }) {
13560
13727
  this.sodax = config;
13561
- this.userConfig = userConfig;
13562
13728
  this.logger = logger ?? resolveLogger(void 0);
13729
+ this.analytics = analytics ?? noopAnalytics;
13563
13730
  this.fee = fee;
13564
13731
  this.loadSodaxConfigDataStructures(config);
13565
13732
  }
13566
13733
  async initialize() {
13567
13734
  try {
13568
- const result = await this.api.getAllConfig();
13569
- if (!result.ok) return result;
13570
- const response = result.value;
13571
- if (!response.version || response.version < CONFIG_VERSION) {
13572
- this.logger.warn(
13573
- `Dynamic config version is less than the current version, resorting to the default one. Current version: ${CONFIG_VERSION}, response version: ${response.version}`
13574
- );
13575
- } else {
13576
- const next = this.userConfig ? mergeSodaxConfig(response.config, this.userConfig) : response.config;
13577
- this.loadSodaxConfigDataStructures(next);
13578
- this.sodax = next;
13579
- this.initialized = true;
13580
- }
13581
13735
  return { ok: true, value: void 0 };
13582
13736
  } catch (error) {
13583
13737
  return { ok: false, error };
@@ -13610,6 +13764,16 @@ var ConfigService = class {
13610
13764
  getOriginalAssetAddress(chainId, hubAsset) {
13611
13765
  return this.hubAssetToXTokenMap.get(hubAsset.toLowerCase())?.address;
13612
13766
  }
13767
+ /**
13768
+ * Resolves the {@link XToken} descriptor (hub asset, vault, decimals) for a hub-asset address.
13769
+ *
13770
+ * Useful when a caller holds a hub asset directly on Sonic that has no spoke-token entry under
13771
+ * the hub chain — e.g. a partner BTC fee held as the BTC hub asset, which only exists as a spoke
13772
+ * token on Bitcoin. Returns `undefined` when the address is not a known hub asset.
13773
+ */
13774
+ getXTokenFromHubAsset(hubAsset) {
13775
+ return this.hubAssetToXTokenMap.get(hubAsset.toLowerCase());
13776
+ }
13613
13777
  getSpokeTokenFromOriginalAssetAddress(chainId, originalAssetAddress) {
13614
13778
  return this.supportedTokensPerChain.get(chainId)?.find((token) => token.address.toLowerCase() === originalAssetAddress.toLowerCase());
13615
13779
  }
@@ -13837,9 +14001,32 @@ function parseTokenArrayFromJson(input) {
13837
14001
  }
13838
14002
  return tokens;
13839
14003
  }
14004
+
14005
+ // src/shared/utils/deepMerge.ts
14006
+ function deepMerge(target, source) {
14007
+ const result = { ...target };
14008
+ for (const key of Object.keys(source)) {
14009
+ const sourceVal = source[key];
14010
+ const targetVal = target[key];
14011
+ if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null && !Array.isArray(targetVal)) {
14012
+ result[key] = deepMerge(
14013
+ targetVal,
14014
+ sourceVal
14015
+ );
14016
+ } else if (sourceVal !== void 0) {
14017
+ result[key] = sourceVal;
14018
+ }
14019
+ }
14020
+ return result;
14021
+ }
14022
+
14023
+ // src/shared/config/mergeSodaxConfig.ts
14024
+ function mergeSodaxConfig(base2, override) {
14025
+ return deepMerge(base2, override);
14026
+ }
13840
14027
  function encodeContractCalls(calls) {
13841
14028
  return encodeAbiParameters(parseAbiParameters("(address,uint256,bytes)[]"), [
13842
- calls.map((v) => [v.address, v.value, v.data])
14029
+ calls.map((v6) => [v6.address, v6.value, v6.data])
13843
14030
  ]);
13844
14031
  }
13845
14032
  async function waitForTransactionReceipt(hash, provider) {
@@ -15520,6 +15707,32 @@ var BitcoinSpokeService = class {
15520
15707
  throw error;
15521
15708
  }
15522
15709
  }
15710
+ /**
15711
+ * Sign and submit a TRADING-wallet raw transaction — the Bound-built *unsigned* PSBT returned by
15712
+ * `deposit({ raw: true })` / the Swaps API (`createIntent().tx.data`). Signs it with the wallet
15713
+ * provider's key, then sends it to Bound Exchange to co-sign with the second 2-of-2 key and
15714
+ * broadcast. Returns the broadcast tx id.
15715
+ *
15716
+ * This is the client-side completion of the TRADING deposit flow: the backend builds the PSBT
15717
+ * (it can't broadcast — the user's signature is missing), the client signs here, and Bound
15718
+ * co-signs + broadcasts. `relayData` ({ address, payload }) is the relay identity returned by
15719
+ * `createIntent()`; forwarding it lets Bound auto-resubmit a stuck relay. It is **not** recoverable
15720
+ * from `rawTx` (whose `to` is the asset manager, not the hub wallet), so callers must supply it.
15721
+ *
15722
+ * @throws if the chain is not in `TRADING` wallet mode (raw txs only exist in TRADING mode).
15723
+ */
15724
+ async signAndSubmitRawTransaction(params) {
15725
+ if (this.walletMode !== "TRADING") {
15726
+ throw new Error("signAndSubmitRawTransaction requires TRADING wallet mode.");
15727
+ }
15728
+ const { rawTx, walletProvider, relayData, accessToken = this.radfi.accessToken } = params;
15729
+ if (!rawTx?.data || !rawTx?.from) {
15730
+ throw new Error("signAndSubmitRawTransaction: rawTx.data (PSBT) and rawTx.from are required.");
15731
+ }
15732
+ const signedTx = await walletProvider.signTransaction(rawTx.data, false);
15733
+ const signedBase64Tx = normalizePsbtToBase64(signedTx);
15734
+ return this.radfi.requestRadfiSignature({ userAddress: rawTx.from, signedBase64Tx, relayData }, accessToken);
15735
+ }
15523
15736
  /**
15524
15737
  * Build deposit PSBT with embedded cross-chain data
15525
15738
  */
@@ -15789,7 +16002,7 @@ function hexToBytes2(hex) {
15789
16002
  const al = hl / 2;
15790
16003
  if (hl % 2)
15791
16004
  throw new Error("hex string expected, got unpadded hex of length " + hl);
15792
- const array = new Uint8Array(al);
16005
+ const array3 = new Uint8Array(al);
15793
16006
  for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
15794
16007
  const n1 = asciiToBase16(hex.charCodeAt(hi));
15795
16008
  const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
@@ -15797,9 +16010,9 @@ function hexToBytes2(hex) {
15797
16010
  const char = hex[hi] + hex[hi + 1];
15798
16011
  throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
15799
16012
  }
15800
- array[ai] = n1 * 16 + n2;
16013
+ array3[ai] = n1 * 16 + n2;
15801
16014
  }
15802
- return array;
16015
+ return array3;
15803
16016
  }
15804
16017
  function concatBytes(...arrays) {
15805
16018
  let sum = 0;
@@ -16429,22 +16642,22 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
16429
16642
  const byte0 = Uint8Array.of(0);
16430
16643
  const byte1 = Uint8Array.of(1);
16431
16644
  const _maxDrbgIters = 1e3;
16432
- let v = u8n(hashLen);
16645
+ let v6 = u8n(hashLen);
16433
16646
  let k = u8n(hashLen);
16434
16647
  let i = 0;
16435
16648
  const reset = () => {
16436
- v.fill(1);
16649
+ v6.fill(1);
16437
16650
  k.fill(0);
16438
16651
  i = 0;
16439
16652
  };
16440
- const h = (...msgs) => hmacFn(k, concatBytes(v, ...msgs));
16653
+ const h = (...msgs) => hmacFn(k, concatBytes(v6, ...msgs));
16441
16654
  const reseed = (seed = NULL) => {
16442
16655
  k = h(byte0, seed);
16443
- v = h();
16656
+ v6 = h();
16444
16657
  if (seed.length === 0)
16445
16658
  return;
16446
16659
  k = h(byte1, seed);
16447
- v = h();
16660
+ v6 = h();
16448
16661
  };
16449
16662
  const gen = () => {
16450
16663
  if (i++ >= _maxDrbgIters)
@@ -16452,10 +16665,10 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
16452
16665
  let len = 0;
16453
16666
  const out = [];
16454
16667
  while (len < qByteLen) {
16455
- v = h();
16456
- const sl = v.slice();
16668
+ v6 = h();
16669
+ const sl = v6.slice();
16457
16670
  out.push(sl);
16458
- len += v.length;
16671
+ len += v6.length;
16459
16672
  }
16460
16673
  return concatBytes(...out);
16461
16674
  };
@@ -16470,18 +16683,18 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
16470
16683
  };
16471
16684
  return genUntil;
16472
16685
  }
16473
- function validateObject(object, fields = {}, optFields = {}) {
16474
- if (!object || typeof object !== "object")
16686
+ function validateObject(object4, fields = {}, optFields = {}) {
16687
+ if (!object4 || typeof object4 !== "object")
16475
16688
  throw new Error("expected valid options object");
16476
16689
  function checkField(fieldName, expectedType, isOpt) {
16477
- const val = object[fieldName];
16690
+ const val = object4[fieldName];
16478
16691
  if (isOpt && val === void 0)
16479
16692
  return;
16480
16693
  const current = typeof val;
16481
16694
  if (current !== expectedType || val === null)
16482
16695
  throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
16483
16696
  }
16484
- const iter = (f, isOpt) => Object.entries(f).forEach(([k, v]) => checkField(k, v, isOpt));
16697
+ const iter = (f, isOpt) => Object.entries(f).forEach(([k, v6]) => checkField(k, v6, isOpt));
16485
16698
  iter(fields, false);
16486
16699
  iter(optFields, true);
16487
16700
  }
@@ -16520,12 +16733,12 @@ function pow2(x, power, modulo) {
16520
16733
  }
16521
16734
  return res;
16522
16735
  }
16523
- function invert(number, modulo) {
16524
- if (number === _0n2)
16736
+ function invert(number4, modulo) {
16737
+ if (number4 === _0n2)
16525
16738
  throw new Error("invert: expected non-zero number");
16526
16739
  if (modulo <= _0n2)
16527
16740
  throw new Error("invert: expected positive modulus, got " + modulo);
16528
- let a = mod(number, modulo);
16741
+ let a = mod(number4, modulo);
16529
16742
  let b = modulo;
16530
16743
  let x = _0n2, u = _1n2;
16531
16744
  while (a !== _0n2) {
@@ -16552,9 +16765,9 @@ function sqrt3mod4(Fp, n) {
16552
16765
  function sqrt5mod8(Fp, n) {
16553
16766
  const p5div8 = (Fp.ORDER - _5n) / _8n;
16554
16767
  const n2 = Fp.mul(n, _2n);
16555
- const v = Fp.pow(n2, p5div8);
16556
- const nv = Fp.mul(n, v);
16557
- const i = Fp.mul(Fp.mul(nv, _2n), v);
16768
+ const v6 = Fp.pow(n2, p5div8);
16769
+ const nv = Fp.mul(n, v6);
16770
+ const i = Fp.mul(Fp.mul(nv, _2n), v6);
16558
16771
  const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
16559
16772
  assertIsSquare(Fp, root, n);
16560
16773
  return root;
@@ -17029,27 +17242,27 @@ var wNAF = class {
17029
17242
  assert0(n);
17030
17243
  return acc;
17031
17244
  }
17032
- getPrecomputes(W, point, transform) {
17245
+ getPrecomputes(W, point, transform2) {
17033
17246
  let comp = pointPrecomputes.get(point);
17034
17247
  if (!comp) {
17035
17248
  comp = this.precomputeWindow(point, W);
17036
17249
  if (W !== 1) {
17037
- if (typeof transform === "function")
17038
- comp = transform(comp);
17250
+ if (typeof transform2 === "function")
17251
+ comp = transform2(comp);
17039
17252
  pointPrecomputes.set(point, comp);
17040
17253
  }
17041
17254
  }
17042
17255
  return comp;
17043
17256
  }
17044
- cached(point, scalar, transform) {
17257
+ cached(point, scalar, transform2) {
17045
17258
  const W = getW(point);
17046
- return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
17259
+ return this.wNAF(W, this.getPrecomputes(W, point, transform2), scalar);
17047
17260
  }
17048
- unsafe(point, scalar, transform, prev) {
17261
+ unsafe(point, scalar, transform2, prev) {
17049
17262
  const W = getW(point);
17050
17263
  if (W === 1)
17051
17264
  return this._unsafeLadder(point, scalar, prev);
17052
- return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev);
17265
+ return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform2), scalar, prev);
17053
17266
  }
17054
17267
  // We calculate precomputes for elliptic curve point multiplication
17055
17268
  // using windowed method. This specifies window size and
@@ -17136,9 +17349,9 @@ function edwards(params, extraOpts = {}) {
17136
17349
  validateObject(extraOpts, {}, { uvRatio: "function" });
17137
17350
  const MASK = _2n2 << BigInt(Fn.BYTES * 8) - _1n4;
17138
17351
  const modP = (n) => Fp.create(n);
17139
- const uvRatio2 = extraOpts.uvRatio || ((u, v) => {
17352
+ const uvRatio2 = extraOpts.uvRatio || ((u, v6) => {
17140
17353
  try {
17141
- return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
17354
+ return { isValid: true, value: Fp.sqrt(Fp.div(u, v6)) };
17142
17355
  } catch (e) {
17143
17356
  return { isValid: false, value: _0n4 };
17144
17357
  }
@@ -17234,8 +17447,8 @@ function edwards(params, extraOpts = {}) {
17234
17447
  aInRange("point.y", y, _0n4, max);
17235
17448
  const y2 = modP(y * y);
17236
17449
  const u = modP(y2 - _1n4);
17237
- const v = modP(d * y2 - a);
17238
- let { isValid, value: x } = uvRatio2(u, v);
17450
+ const v6 = modP(d * y2 - a);
17451
+ let { isValid, value: x } = uvRatio2(u, v6);
17239
17452
  if (!isValid)
17240
17453
  throw new Error("bad point: invalid y coordinate");
17241
17454
  const isXOdd = (x & _1n4) === _1n4;
@@ -17575,13 +17788,13 @@ function adjustScalarBytes(bytes) {
17575
17788
  return bytes;
17576
17789
  }
17577
17790
  var ED25519_SQRT_M1 = /* @__PURE__ */ BigInt("19681161376707505956807079304988542015446066515923890162744021073123829784752");
17578
- function uvRatio(u, v) {
17791
+ function uvRatio(u, v6) {
17579
17792
  const P = ed25519_CURVE_p;
17580
- const v3 = mod(v * v * v, P);
17581
- const v7 = mod(v3 * v3 * v, P);
17793
+ const v32 = mod(v6 * v6 * v6, P);
17794
+ const v7 = mod(v32 * v32 * v6, P);
17582
17795
  const pow = ed25519_pow_2_252_3(u * v7).pow_p_5_8;
17583
- let x = mod(u * v3 * pow, P);
17584
- const vx2 = mod(v * x * x, P);
17796
+ let x = mod(u * v32 * pow, P);
17797
+ const vx2 = mod(v6 * x * x, P);
17585
17798
  const root1 = x;
17586
17799
  const root2 = mod(x * ED25519_SQRT_M1, P);
17587
17800
  const useRoot1 = vx2 === u;
@@ -17912,7 +18125,7 @@ function findSeatPrice(validators, maxNumberOfSeats, minimumStakeRatio, protocol
17912
18125
  return findSeatPriceForProtocolAfter49(validators, maxNumberOfSeats, minimumStakeRatio);
17913
18126
  }
17914
18127
  function findSeatPriceForProtocolBefore49(validators, numSeats) {
17915
- const stakes = validators.map((v) => BigInt(v.stake)).sort(sortBigIntAsc);
18128
+ const stakes = validators.map((v6) => BigInt(v6.stake)).sort(sortBigIntAsc);
17916
18129
  const num = BigInt(numSeats);
17917
18130
  const stakesSum = stakes.reduce((a, b) => a + b);
17918
18131
  if (stakesSum < num) {
@@ -17941,7 +18154,7 @@ function findSeatPriceForProtocolAfter49(validators, maxNumberOfSeats, minimumSt
17941
18154
  if (minimumStakeRatio.length != 2) {
17942
18155
  throw Error("minimumStakeRatio should have 2 elements");
17943
18156
  }
17944
- const stakes = validators.map((v) => BigInt(v.stake)).sort(sortBigIntAsc);
18157
+ const stakes = validators.map((v6) => BigInt(v6.stake)).sort(sortBigIntAsc);
17945
18158
  const stakesSum = stakes.reduce((a, b) => a + b);
17946
18159
  if (validators.length < maxNumberOfSeats) {
17947
18160
  return stakesSum * BigInt(minimumStakeRatio[0]) / BigInt(minimumStakeRatio[1]);
@@ -18113,10 +18326,10 @@ var DER = {
18113
18326
  if (length < 128)
18114
18327
  throw new E("tlv.decode(long): not minimal encoding");
18115
18328
  }
18116
- const v = data.subarray(pos, pos + length);
18117
- if (v.length !== length)
18329
+ const v6 = data.subarray(pos, pos + length);
18330
+ if (v6.length !== length)
18118
18331
  throw new E("tlv.decode: wrong value length");
18119
- return { v, l: data.subarray(pos + length) };
18332
+ return { v: v6, l: data.subarray(pos + length) };
18120
18333
  }
18121
18334
  },
18122
18335
  // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
@@ -18684,9 +18897,9 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
18684
18897
  extraEntropy: false
18685
18898
  };
18686
18899
  const hasLargeCofactor = CURVE_ORDER * _2n4 < Fp.ORDER;
18687
- function isBiggerThanHalfOrder(number) {
18900
+ function isBiggerThanHalfOrder(number4) {
18688
18901
  const HALF = CURVE_ORDER >> _1n6;
18689
- return number > HALF;
18902
+ return number4 > HALF;
18690
18903
  }
18691
18904
  function validateRS(title, num) {
18692
18905
  if (!Fn.isValidNot0(num))
@@ -18863,14 +19076,14 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
18863
19076
  return false;
18864
19077
  const { r, s } = sig;
18865
19078
  const h = bits2int_modN(message);
18866
- const is = Fn.inv(s);
18867
- const u1 = Fn.create(h * is);
18868
- const u2 = Fn.create(r * is);
19079
+ const is2 = Fn.inv(s);
19080
+ const u1 = Fn.create(h * is2);
19081
+ const u2 = Fn.create(r * is2);
18869
19082
  const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
18870
19083
  if (R.is0())
18871
19084
  return false;
18872
- const v = Fn.create(R.x);
18873
- return v === r;
19085
+ const v6 = Fn.create(R.x);
19086
+ return v6 === r;
18874
19087
  } catch (e) {
18875
19088
  return false;
18876
19089
  }
@@ -26216,13 +26429,18 @@ var SwapService = class {
26216
26429
  solver;
26217
26430
  partnerFee;
26218
26431
  relayerApiEndpoint;
26219
- constructor({ config, hubProvider, spoke }) {
26432
+ // backend swaps-API client + opt-in 2-step submit-tx flag
26433
+ backendApi;
26434
+ useBackendSubmitTx;
26435
+ constructor({ config, hubProvider, spoke, backendApi, useBackendSubmitTx }) {
26220
26436
  this.solver = config.solver;
26221
26437
  this.partnerFee = config.swapPartnerFee;
26222
26438
  this.relayerApiEndpoint = config.relay.relayerApiEndpoint;
26223
26439
  this.config = config;
26224
26440
  this.hubProvider = hubProvider;
26225
26441
  this.spoke = spoke;
26442
+ this.backendApi = backendApi;
26443
+ this.useBackendSubmitTx = useBackendSubmitTx ?? false;
26226
26444
  }
26227
26445
  /**
26228
26446
  * Estimates the gas cost for a raw (unsigned) transaction on a spoke chain.
@@ -26366,14 +26584,22 @@ var SwapService = class {
26366
26584
  /**
26367
26585
  * Executes a full end-to-end cross-chain swap.
26368
26586
  *
26369
- * Orchestrates the complete swap lifecycle:
26370
- * 1. Calls `createIntent` to submit the intent transaction on the source spoke chain.
26371
- * 2. Verifies the spoke transaction landed on-chain.
26372
- * 3. For non-hub source chains: submits the spoke tx to the relayer and waits for the
26373
- * relay packet to land on the hub (Sonic). Skipped when `srcChainKey` is the hub.
26374
- * 4. Calls `postExecution` to notify the solver, triggering it to fill the intent.
26375
- *
26376
- * @param _params - Swap action params including intent parameters, wallet provider, and optional timeout.
26587
+ * Orchestrates the complete swap lifecycle. `createIntent` first submits the intent transaction on
26588
+ * the source spoke chain; completion then runs via one of two paths, both bounded by a single
26589
+ * shared `timeout` budget:
26590
+ *
26591
+ * - **Client-side (default), {@link fallbackSwapSteps}:** verifies the spoke tx landed on-chain,
26592
+ * relays it to the hub (Sonic) and waits for the packet skipped when `srcChainKey` is the hub,
26593
+ * where the spoke tx already is the hub tx — then calls `postExecution` to notify the solver,
26594
+ * triggering it to fill the intent.
26595
+ * - **Backend 2-step (opt-in via `swapsOptions.useBackendSubmitTx`), {@link submitTx}:** hands the
26596
+ * broadcast tx to the swaps API, which verifies, relays and post-executes server-side, then polls
26597
+ * for completion. On ANY non-success it transparently falls back to the client-side path above —
26598
+ * safe because re-relaying / re-posting an already-processed swap is idempotent (no double-fill).
26599
+ *
26600
+ * @param _params - Swap action params including intent parameters, wallet provider, and an optional
26601
+ * `timeout` — the single shared budget for the whole completion flow (relay/poll plus any
26602
+ * fallback), so total wall-clock never exceeds it.
26377
26603
  * @returns A `Result<SwapResponse, SwapError>`. On success:
26378
26604
  * - `solverExecutionResponse` — solver acknowledgement (`{ answer: 'OK', intent_hash }`).
26379
26605
  * - `intent` — the on-chain intent object that was created.
@@ -26397,63 +26623,179 @@ var SwapService = class {
26397
26623
  const { params } = _params;
26398
26624
  const srcChainKey = params.srcChainKey;
26399
26625
  const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey };
26400
- try {
26401
- const timeout = _params.timeout;
26402
- const createIntentResult = await this.createIntent(_params);
26403
- if (!createIntentResult.ok) {
26404
- return { ok: false, error: createIntentResult.error };
26626
+ return this.config.analytics.trackResult(
26627
+ "swap",
26628
+ "swap",
26629
+ async () => {
26630
+ try {
26631
+ const createIntentResult = await this.createIntent(_params);
26632
+ if (!createIntentResult.ok) {
26633
+ return { ok: false, error: createIntentResult.error };
26634
+ }
26635
+ const created = createIntentResult.value;
26636
+ const deadline = Date.now() + (_params.timeout ?? DEFAULT_RELAY_TX_TIMEOUT);
26637
+ if (this.useBackendSubmitTx) {
26638
+ const submitted = await this.submitTx(_params, created, deadline);
26639
+ if (submitted.ok) return submitted;
26640
+ this.config.logger.warn(
26641
+ "[swap] backend submit-tx did not complete; falling back to the client-side relay",
26642
+ {
26643
+ error: submitted.error
26644
+ }
26645
+ );
26646
+ }
26647
+ return this.fallbackSwapSteps(_params, created, deadline);
26648
+ } catch (error) {
26649
+ if (isSwapError(error)) return { ok: false, error };
26650
+ return {
26651
+ ok: false,
26652
+ error: unknownFailed("swap", error, { ...baseCtx, action: "swap" })
26653
+ };
26654
+ }
26655
+ },
26656
+ {
26657
+ start: () => ({
26658
+ srcChainKey,
26659
+ dstChainKey: params.dstChainKey,
26660
+ srcAddress: params.srcAddress,
26661
+ dstAddress: params.dstAddress
26662
+ }),
26663
+ success: (value) => ({
26664
+ srcChainKey,
26665
+ dstChainKey: params.dstChainKey,
26666
+ srcTxHash: value.intentDeliveryInfo.srcTxHash,
26667
+ dstTxHash: value.intentDeliveryInfo.dstTxHash
26668
+ }),
26669
+ failure: (error) => ({ code: error.code })
26405
26670
  }
26406
- const { tx: spokeTxHash, intent, relayData } = createIntentResult.value;
26407
- const verifyTxHashResult = await this.spoke.verifyTxHash({
26408
- txHash: spokeTxHash,
26409
- chainKey: srcChainKey
26671
+ );
26672
+ }
26673
+ /**
26674
+ * Client-side swap completion (the default path): relay the broadcast intent tx to the hub — or
26675
+ * use it directly when the source IS the hub — then notify the solver via post-execution and
26676
+ * build the {@link SwapResponse}. Extracted verbatim from `swap()` so the opt-in backend 2-step
26677
+ * path ({@link submitTx}) can fall back to it on any non-success.
26678
+ */
26679
+ async fallbackSwapSteps(_params, created, deadline) {
26680
+ const { params } = _params;
26681
+ const srcChainKey = params.srcChainKey;
26682
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey };
26683
+ const { tx: spokeTxHash, intent, relayData } = created;
26684
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
26685
+ txHash: created.tx,
26686
+ chainKey: srcChainKey
26687
+ });
26688
+ if (!verifyTxHashResult.ok) {
26689
+ return { ok: false, error: verifyFailed("swap", verifyTxHashResult.error, { ...baseCtx, action: "swap" }) };
26690
+ }
26691
+ let dstIntentTxHash;
26692
+ if (isHubChainKeyType(srcChainKey)) {
26693
+ dstIntentTxHash = spokeTxHash;
26694
+ } else {
26695
+ const packet = await relayTxAndWaitPacket({
26696
+ srcTxHash: spokeTxHash,
26697
+ data: relayData,
26698
+ chainKey: srcChainKey,
26699
+ relayerApiEndpoint: this.relayerApiEndpoint,
26700
+ // Remaining shared budget: ≈ full `timeout` on the flag-off path (called immediately), or
26701
+ // the reserve `submitTx` left on the backend path. Floor keeps a stalled-backend fallback viable.
26702
+ timeout: Math.max(deadline - Date.now(), 5e3)
26410
26703
  });
26411
- if (!verifyTxHashResult.ok) {
26412
- return { ok: false, error: verifyFailed("swap", verifyTxHashResult.error, { ...baseCtx, action: "swap" }) };
26704
+ if (!packet.ok) {
26705
+ return { ok: false, error: mapRelayFailure(packet.error, { feature: "swap", action: "swap", ...baseCtx }) };
26413
26706
  }
26414
- let dstIntentTxHash;
26415
- if (isHubChainKeyType(srcChainKey)) {
26416
- dstIntentTxHash = spokeTxHash;
26417
- } else {
26418
- const packet = await relayTxAndWaitPacket({
26707
+ dstIntentTxHash = packet.value.dst_tx_hash;
26708
+ }
26709
+ const postExecResult = await this.postExecution({
26710
+ intent_tx_hash: dstIntentTxHash
26711
+ });
26712
+ if (!postExecResult.ok) {
26713
+ return { ok: false, error: postExecResult.error };
26714
+ }
26715
+ return {
26716
+ ok: true,
26717
+ value: {
26718
+ solverExecutionResponse: postExecResult.value,
26719
+ intent,
26720
+ intentDeliveryInfo: {
26721
+ srcChainKey,
26419
26722
  srcTxHash: spokeTxHash,
26420
- data: relayData,
26421
- chainKey: srcChainKey,
26422
- relayerApiEndpoint: this.relayerApiEndpoint,
26423
- timeout
26424
- });
26425
- if (!packet.ok) {
26426
- return { ok: false, error: mapRelayFailure(packet.error, { feature: "swap", action: "swap", ...baseCtx }) };
26723
+ srcAddress: params.srcAddress,
26724
+ dstChainKey: params.dstChainKey,
26725
+ dstTxHash: dstIntentTxHash,
26726
+ dstAddress: params.dstAddress
26427
26727
  }
26428
- dstIntentTxHash = packet.value.dst_tx_hash;
26429
26728
  }
26430
- const postExecResult = await this.postExecution({
26431
- intent_tx_hash: dstIntentTxHash
26729
+ };
26730
+ }
26731
+ /**
26732
+ * Backend 2-step swap path (opt-in via `swapsOptions.useBackendSubmitTx`): hand the broadcast
26733
+ * intent tx to the swaps API (`POST /swaps/submit-tx`); the backend relays + post-executes
26734
+ * server-side. Polls `getSubmitTxStatus` until `executed`, then reconstructs the same
26735
+ * {@link SwapResponse} the client-side path returns (`result.dstIntentTxHash` → delivery info,
26736
+ * `result.intent_hash` → solver response).
26737
+ *
26738
+ * Never throws — returns `{ ok: false }` on any non-success (submit `!ok`, terminal `failed` /
26739
+ * abandoned, or poll timeout) so `swap()` falls back to {@link fallbackSwapSteps}.
26740
+ *
26741
+ * Falling back is safe: re-relaying / re-posting an already-processed swap is idempotent — the
26742
+ * relay dedups and returns the existing `executed` packet, and the solver re-affirms the intent
26743
+ * (no double-fill). Verified live by `e2e-tests/e2e-relay.test.ts`. Polling stops at
26744
+ * `deadline - reserve` so the fallback keeps a guaranteed slice of the shared `swap` budget.
26745
+ */
26746
+ async submitTx(_params, created, deadline) {
26747
+ const { params } = _params;
26748
+ const srcChainKey = params.srcChainKey;
26749
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey };
26750
+ const { tx: spokeTxHash, intent, relayData } = created;
26751
+ const submitTxFailed = (cause) => ({
26752
+ ok: false,
26753
+ error: executionFailed("swap", cause, { ...baseCtx, action: "swap" })
26754
+ });
26755
+ try {
26756
+ const submitted = await this.backendApi.swaps.submitTx({
26757
+ txHash: spokeTxHash,
26758
+ srcChainKey,
26759
+ walletAddress: params.srcAddress,
26760
+ intent,
26761
+ relayData: relayData.payload
26432
26762
  });
26433
- if (!postExecResult.ok) {
26434
- return { ok: false, error: postExecResult.error };
26435
- }
26436
- return {
26437
- ok: true,
26438
- value: {
26439
- solverExecutionResponse: postExecResult.value,
26440
- intent,
26441
- intentDeliveryInfo: {
26442
- srcChainKey,
26443
- srcTxHash: spokeTxHash,
26444
- srcAddress: params.srcAddress,
26445
- dstChainKey: params.dstChainKey,
26446
- dstTxHash: dstIntentTxHash,
26447
- dstAddress: params.dstAddress
26763
+ if (!submitted.ok) return submitTxFailed(submitted.error);
26764
+ const reserveMs = Math.min(Math.ceil((deadline - Date.now()) / 3), 2e4);
26765
+ const pollDeadline = deadline - reserveMs;
26766
+ const pollIntervalMs = 1e3;
26767
+ while (Date.now() < pollDeadline) {
26768
+ const statusResult = await this.backendApi.swaps.getSubmitTxStatus({ txHash: spokeTxHash, srcChainKey });
26769
+ if (statusResult.ok) {
26770
+ const { status, result, failureReason, abandonedAt } = statusResult.value.data;
26771
+ if (status === "executed" && result?.dstIntentTxHash && result.intent_hash) {
26772
+ return {
26773
+ ok: true,
26774
+ value: {
26775
+ // Backend serializes the hex intent_hash as a plain string; brand it at the boundary.
26776
+ solverExecutionResponse: { answer: "OK", intent_hash: result.intent_hash },
26777
+ intent,
26778
+ intentDeliveryInfo: {
26779
+ srcChainKey,
26780
+ srcTxHash: spokeTxHash,
26781
+ srcAddress: params.srcAddress,
26782
+ dstChainKey: params.dstChainKey,
26783
+ dstTxHash: result.dstIntentTxHash,
26784
+ dstAddress: params.dstAddress
26785
+ }
26786
+ }
26787
+ };
26788
+ }
26789
+ if (status === "failed" || abandonedAt) {
26790
+ const reason = failureReason ? `: ${failureReason}` : "";
26791
+ return submitTxFailed(new Error(`backend submit-tx ${status}${reason}`));
26448
26792
  }
26449
26793
  }
26450
- };
26794
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
26795
+ }
26796
+ return submitTxFailed(new Error("backend submit-tx polling timed out before reaching executed"));
26451
26797
  } catch (error) {
26452
- if (isSwapError(error)) return { ok: false, error };
26453
- return {
26454
- ok: false,
26455
- error: unknownFailed("swap", error, { ...baseCtx, action: "swap" })
26456
- };
26798
+ return { ok: false, error: unknownFailed("swap", error, { ...baseCtx, action: "swap" }) };
26457
26799
  }
26458
26800
  }
26459
26801
  /**
@@ -28500,72 +28842,94 @@ var MigrationService = class {
28500
28842
  * }
28501
28843
  */
28502
28844
  async migratebnUSD(_params) {
28503
- const { params, timeout } = _params;
28504
- const baseCtx = {
28505
- srcChainKey: params.srcChainKey,
28506
- dstChainKey: params.dstChainKey,
28507
- action: "migratebnUSD"
28508
- };
28509
- try {
28510
- const intentResult = await this.createMigratebnUSDIntent(_params);
28511
- if (!intentResult.ok) return { ok: false, error: intentResult.error };
28512
- const { tx: spokeTxHash, relayData: extraData } = intentResult.value;
28513
- const verifyTxHashResult = await this.spoke.verifyTxHash({
28514
- txHash: spokeTxHash,
28515
- chainKey: params.srcChainKey
28516
- });
28517
- if (!verifyTxHashResult.ok) {
28518
- return {
28519
- ok: false,
28520
- error: verifyFailed("migration", verifyTxHashResult.error, baseCtx)
28521
- };
28522
- }
28523
- const packetResult = await relayTxAndWaitPacket({
28524
- srcTxHash: spokeTxHash,
28525
- data: extraData,
28526
- chainKey: params.srcChainKey,
28527
- relayerApiEndpoint: this.relayerApiEndpoint,
28528
- timeout
28529
- });
28530
- if (!packetResult.ok) {
28531
- return {
28532
- ok: false,
28533
- error: mapRelayFailure(packetResult.error, {
28534
- feature: "migration",
28535
- action: baseCtx.action,
28536
- srcChainKey: baseCtx.srcChainKey,
28537
- dstChainKey: baseCtx.dstChainKey
28538
- })
28845
+ return this.config.analytics.trackResult(
28846
+ "migration",
28847
+ "migratebnUSD",
28848
+ async () => {
28849
+ const { params, timeout } = _params;
28850
+ const baseCtx = {
28851
+ srcChainKey: params.srcChainKey,
28852
+ dstChainKey: params.dstChainKey,
28853
+ action: "migratebnUSD"
28539
28854
  };
28540
- }
28541
- if (!(params.srcChainKey === ChainKeys.SONIC_MAINNET || params.dstChainKey === ChainKeys.SONIC_MAINNET)) {
28542
- const execResult = await waitUntilIntentExecuted({
28543
- intentRelayChainId: getIntentRelayChainId(ChainKeys.SONIC_MAINNET).toString(),
28544
- srcTxHash: packetResult.value.dst_tx_hash,
28545
- timeout,
28546
- apiUrl: this.relayerApiEndpoint
28547
- });
28548
- if (!execResult.ok) {
28855
+ try {
28856
+ const intentResult = await this.createMigratebnUSDIntent(_params);
28857
+ if (!intentResult.ok) return { ok: false, error: intentResult.error };
28858
+ const { tx: spokeTxHash, relayData: extraData } = intentResult.value;
28859
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
28860
+ txHash: spokeTxHash,
28861
+ chainKey: params.srcChainKey
28862
+ });
28863
+ if (!verifyTxHashResult.ok) {
28864
+ return {
28865
+ ok: false,
28866
+ error: verifyFailed("migration", verifyTxHashResult.error, baseCtx)
28867
+ };
28868
+ }
28869
+ const packetResult = await relayTxAndWaitPacket({
28870
+ srcTxHash: spokeTxHash,
28871
+ data: extraData,
28872
+ chainKey: params.srcChainKey,
28873
+ relayerApiEndpoint: this.relayerApiEndpoint,
28874
+ timeout
28875
+ });
28876
+ if (!packetResult.ok) {
28877
+ return {
28878
+ ok: false,
28879
+ error: mapRelayFailure(packetResult.error, {
28880
+ feature: "migration",
28881
+ action: baseCtx.action,
28882
+ srcChainKey: baseCtx.srcChainKey,
28883
+ dstChainKey: baseCtx.dstChainKey
28884
+ })
28885
+ };
28886
+ }
28887
+ if (!(params.srcChainKey === ChainKeys.SONIC_MAINNET || params.dstChainKey === ChainKeys.SONIC_MAINNET)) {
28888
+ const execResult = await waitUntilIntentExecuted({
28889
+ intentRelayChainId: getIntentRelayChainId(ChainKeys.SONIC_MAINNET).toString(),
28890
+ srcTxHash: packetResult.value.dst_tx_hash,
28891
+ timeout,
28892
+ apiUrl: this.relayerApiEndpoint
28893
+ });
28894
+ if (!execResult.ok) {
28895
+ return {
28896
+ ok: false,
28897
+ error: mapRelayFailure(execResult.error, {
28898
+ feature: "migration",
28899
+ action: baseCtx.action,
28900
+ srcChainKey: baseCtx.srcChainKey,
28901
+ dstChainKey: baseCtx.dstChainKey,
28902
+ phase: "destinationExecution"
28903
+ })
28904
+ };
28905
+ }
28906
+ }
28907
+ return { ok: true, value: { srcChainTxHash: spokeTxHash, dstChainTxHash: packetResult.value.dst_tx_hash } };
28908
+ } catch (error) {
28909
+ if (isMigrateOrchestrationError(error)) return { ok: false, error };
28549
28910
  return {
28550
28911
  ok: false,
28551
- error: mapRelayFailure(execResult.error, {
28552
- feature: "migration",
28553
- action: baseCtx.action,
28554
- srcChainKey: baseCtx.srcChainKey,
28555
- dstChainKey: baseCtx.dstChainKey,
28556
- phase: "destinationExecution"
28557
- })
28912
+ error: executionFailed("migration", error, baseCtx)
28558
28913
  };
28559
28914
  }
28915
+ },
28916
+ {
28917
+ start: () => ({
28918
+ srcChainKey: _params.params.srcChainKey,
28919
+ dstChainKey: _params.params.dstChainKey,
28920
+ srcAddress: _params.params.srcAddress,
28921
+ dstAddress: _params.params.dstAddress,
28922
+ srcbnUSD: _params.params.srcbnUSD,
28923
+ dstbnUSD: _params.params.dstbnUSD,
28924
+ amount: _params.params.amount
28925
+ }),
28926
+ success: (value) => ({
28927
+ srcChainTxHash: value.srcChainTxHash,
28928
+ dstChainTxHash: value.dstChainTxHash
28929
+ }),
28930
+ failure: (error) => ({ code: error.code })
28560
28931
  }
28561
- return { ok: true, value: { srcChainTxHash: spokeTxHash, dstChainTxHash: packetResult.value.dst_tx_hash } };
28562
- } catch (error) {
28563
- if (isMigrateOrchestrationError(error)) return { ok: false, error };
28564
- return {
28565
- ok: false,
28566
- error: executionFailed("migration", error, baseCtx)
28567
- };
28568
- }
28932
+ );
28569
28933
  }
28570
28934
  /**
28571
28935
  * Migrates ICX or wICX tokens from ICON to SODA on the hub chain (Sonic), including relay.
@@ -28580,37 +28944,57 @@ var MigrationService = class {
28580
28944
  * check fails, the deposit reverts, or the relay times out.
28581
28945
  */
28582
28946
  async migrateIcxToSoda(_params) {
28583
- const { timeout } = _params;
28584
- const baseCtx = { srcChainKey: _params.params.srcChainKey, action: "migrateIcxToSoda" };
28585
- try {
28586
- const txResult = await this.createMigrateIcxToSodaIntent(_params);
28587
- if (!txResult.ok) return { ok: false, error: txResult.error };
28588
- const { tx, relayData } = txResult.value;
28589
- const packetResult = await relayTxAndWaitPacket({
28590
- srcTxHash: tx,
28591
- data: relayData,
28592
- chainKey: _params.params.srcChainKey,
28593
- relayerApiEndpoint: this.relayerApiEndpoint,
28594
- timeout
28595
- });
28596
- if (!packetResult.ok) {
28597
- return {
28598
- ok: false,
28599
- error: mapRelayFailure(packetResult.error, {
28600
- feature: "migration",
28601
- action: baseCtx.action,
28602
- srcChainKey: baseCtx.srcChainKey
28603
- })
28604
- };
28947
+ return this.config.analytics.trackResult(
28948
+ "migration",
28949
+ "migrateIcxToSoda",
28950
+ async () => {
28951
+ const { timeout } = _params;
28952
+ const baseCtx = { srcChainKey: _params.params.srcChainKey, action: "migrateIcxToSoda" };
28953
+ try {
28954
+ const txResult = await this.createMigrateIcxToSodaIntent(_params);
28955
+ if (!txResult.ok) return { ok: false, error: txResult.error };
28956
+ const { tx, relayData } = txResult.value;
28957
+ const packetResult = await relayTxAndWaitPacket({
28958
+ srcTxHash: tx,
28959
+ data: relayData,
28960
+ chainKey: _params.params.srcChainKey,
28961
+ relayerApiEndpoint: this.relayerApiEndpoint,
28962
+ timeout
28963
+ });
28964
+ if (!packetResult.ok) {
28965
+ return {
28966
+ ok: false,
28967
+ error: mapRelayFailure(packetResult.error, {
28968
+ feature: "migration",
28969
+ action: baseCtx.action,
28970
+ srcChainKey: baseCtx.srcChainKey
28971
+ })
28972
+ };
28973
+ }
28974
+ return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
28975
+ } catch (error) {
28976
+ if (isMigrateOrchestrationError(error)) return { ok: false, error };
28977
+ return {
28978
+ ok: false,
28979
+ error: executionFailed("migration", error, baseCtx)
28980
+ };
28981
+ }
28982
+ },
28983
+ {
28984
+ start: () => ({
28985
+ srcChainKey: _params.params.srcChainKey,
28986
+ srcAddress: _params.params.srcAddress,
28987
+ dstAddress: _params.params.dstAddress,
28988
+ address: _params.params.address,
28989
+ amount: _params.params.amount
28990
+ }),
28991
+ success: (value) => ({
28992
+ srcChainTxHash: value.srcChainTxHash,
28993
+ dstChainTxHash: value.dstChainTxHash
28994
+ }),
28995
+ failure: (error) => ({ code: error.code })
28605
28996
  }
28606
- return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
28607
- } catch (error) {
28608
- if (isMigrateOrchestrationError(error)) return { ok: false, error };
28609
- return {
28610
- ok: false,
28611
- error: executionFailed("migration", error, baseCtx)
28612
- };
28613
- }
28997
+ );
28614
28998
  }
28615
28999
  /**
28616
29000
  * Reverts a previous ICX→SODA migration by swapping SODA back to wICX on the hub and
@@ -28628,37 +29012,56 @@ var MigrationService = class {
28628
29012
  * Sonic deposit transaction and `dstChainTxHash` is the hub-side packet receipt.
28629
29013
  */
28630
29014
  async revertMigrateSodaToIcx(_params) {
28631
- const { timeout } = _params;
28632
- const baseCtx = { srcChainKey: ChainKeys.SONIC_MAINNET, action: "revertMigrateSodaToIcx" };
28633
- try {
28634
- const txResult = await this.createRevertSodaToIcxMigrationIntent(_params);
28635
- if (!txResult.ok) return { ok: false, error: txResult.error };
28636
- const { tx, relayData } = txResult.value;
28637
- const packetResult = await relayTxAndWaitPacket({
28638
- srcTxHash: tx,
28639
- data: relayData,
28640
- chainKey: ChainKeys.SONIC_MAINNET,
28641
- relayerApiEndpoint: this.relayerApiEndpoint,
28642
- timeout
28643
- });
28644
- if (!packetResult.ok) {
28645
- return {
28646
- ok: false,
28647
- error: mapRelayFailure(packetResult.error, {
28648
- feature: "migration",
28649
- action: baseCtx.action,
28650
- srcChainKey: baseCtx.srcChainKey
28651
- })
28652
- };
29015
+ return this.config.analytics.trackResult(
29016
+ "migration",
29017
+ "revertMigrateSodaToIcx",
29018
+ async () => {
29019
+ const { timeout } = _params;
29020
+ const baseCtx = { srcChainKey: ChainKeys.SONIC_MAINNET, action: "revertMigrateSodaToIcx" };
29021
+ try {
29022
+ const txResult = await this.createRevertSodaToIcxMigrationIntent(_params);
29023
+ if (!txResult.ok) return { ok: false, error: txResult.error };
29024
+ const { tx, relayData } = txResult.value;
29025
+ const packetResult = await relayTxAndWaitPacket({
29026
+ srcTxHash: tx,
29027
+ data: relayData,
29028
+ chainKey: ChainKeys.SONIC_MAINNET,
29029
+ relayerApiEndpoint: this.relayerApiEndpoint,
29030
+ timeout
29031
+ });
29032
+ if (!packetResult.ok) {
29033
+ return {
29034
+ ok: false,
29035
+ error: mapRelayFailure(packetResult.error, {
29036
+ feature: "migration",
29037
+ action: baseCtx.action,
29038
+ srcChainKey: baseCtx.srcChainKey
29039
+ })
29040
+ };
29041
+ }
29042
+ return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
29043
+ } catch (error) {
29044
+ if (isRevertMigrationOrchestrationError(error)) return { ok: false, error };
29045
+ return {
29046
+ ok: false,
29047
+ error: executionFailed("migration", error, baseCtx)
29048
+ };
29049
+ }
29050
+ },
29051
+ {
29052
+ start: () => ({
29053
+ srcChainKey: _params.params.srcChainKey,
29054
+ srcAddress: _params.params.srcAddress,
29055
+ dstAddress: _params.params.dstAddress,
29056
+ amount: _params.params.amount
29057
+ }),
29058
+ success: (value) => ({
29059
+ srcChainTxHash: value.srcChainTxHash,
29060
+ dstChainTxHash: value.dstChainTxHash
29061
+ }),
29062
+ failure: (error) => ({ code: error.code })
28653
29063
  }
28654
- return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
28655
- } catch (error) {
28656
- if (isRevertMigrationOrchestrationError(error)) return { ok: false, error };
28657
- return {
28658
- ok: false,
28659
- error: executionFailed("migration", error, baseCtx)
28660
- };
28661
- }
29064
+ );
28662
29065
  }
28663
29066
  /**
28664
29067
  * Migrates BALN tokens from ICON to SODA on the hub chain (Sonic), including relay.
@@ -28675,37 +29078,58 @@ var MigrationService = class {
28675
29078
  * ICON deposit transaction and `dstChainTxHash` is the hub-side packet receipt.
28676
29079
  */
28677
29080
  async migrateBaln(_params) {
28678
- const { timeout } = _params;
28679
- const baseCtx = { srcChainKey: ChainKeys.ICON_MAINNET, action: "migrateBaln" };
28680
- try {
28681
- const txResult = await this.createMigrateBalnIntent(_params);
28682
- if (!txResult.ok) return { ok: false, error: txResult.error };
28683
- const { tx, relayData } = txResult.value;
28684
- const packetResult = await relayTxAndWaitPacket({
28685
- srcTxHash: tx,
28686
- data: relayData,
28687
- chainKey: ChainKeys.ICON_MAINNET,
28688
- relayerApiEndpoint: this.relayerApiEndpoint,
28689
- timeout
28690
- });
28691
- if (!packetResult.ok) {
28692
- return {
28693
- ok: false,
28694
- error: mapRelayFailure(packetResult.error, {
28695
- feature: "migration",
28696
- action: baseCtx.action,
28697
- srcChainKey: baseCtx.srcChainKey
28698
- })
28699
- };
29081
+ return this.config.analytics.trackResult(
29082
+ "migration",
29083
+ "migrateBaln",
29084
+ async () => {
29085
+ const { timeout } = _params;
29086
+ const baseCtx = { srcChainKey: ChainKeys.ICON_MAINNET, action: "migrateBaln" };
29087
+ try {
29088
+ const txResult = await this.createMigrateBalnIntent(_params);
29089
+ if (!txResult.ok) return { ok: false, error: txResult.error };
29090
+ const { tx, relayData } = txResult.value;
29091
+ const packetResult = await relayTxAndWaitPacket({
29092
+ srcTxHash: tx,
29093
+ data: relayData,
29094
+ chainKey: ChainKeys.ICON_MAINNET,
29095
+ relayerApiEndpoint: this.relayerApiEndpoint,
29096
+ timeout
29097
+ });
29098
+ if (!packetResult.ok) {
29099
+ return {
29100
+ ok: false,
29101
+ error: mapRelayFailure(packetResult.error, {
29102
+ feature: "migration",
29103
+ action: baseCtx.action,
29104
+ srcChainKey: baseCtx.srcChainKey
29105
+ })
29106
+ };
29107
+ }
29108
+ return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
29109
+ } catch (error) {
29110
+ if (isMigrateOrchestrationError(error)) return { ok: false, error };
29111
+ return {
29112
+ ok: false,
29113
+ error: executionFailed("migration", error, baseCtx)
29114
+ };
29115
+ }
29116
+ },
29117
+ {
29118
+ start: () => ({
29119
+ srcChainKey: _params.params.srcChainKey,
29120
+ srcAddress: _params.params.srcAddress,
29121
+ dstAddress: _params.params.dstAddress,
29122
+ amount: _params.params.amount,
29123
+ lockupPeriod: _params.params.lockupPeriod,
29124
+ stake: _params.params.stake
29125
+ }),
29126
+ success: (value) => ({
29127
+ srcChainTxHash: value.srcChainTxHash,
29128
+ dstChainTxHash: value.dstChainTxHash
29129
+ }),
29130
+ failure: (error) => ({ code: error.code })
28700
29131
  }
28701
- return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
28702
- } catch (error) {
28703
- if (isMigrateOrchestrationError(error)) return { ok: false, error };
28704
- return {
28705
- ok: false,
28706
- error: executionFailed("migration", error, baseCtx)
28707
- };
28708
- }
29132
+ );
28709
29133
  }
28710
29134
  /**
28711
29135
  * Builds and submits the spoke-side deposit for a BALN→SODA migration without relaying.
@@ -29096,71 +29520,883 @@ var MigrationService = class {
29096
29520
  }
29097
29521
  };
29098
29522
 
29099
- // src/backendApi/BackendApiService.ts
29100
- var BackendApiService = class {
29523
+ // src/backendApi/api-utils.ts
29524
+ var toJsonBody = (value) => JSON.stringify(value, (_key, val) => typeof val === "bigint" ? val.toString() : val);
29525
+ async function makeRequest(params) {
29526
+ const { endpoint, config, overrideConfig = {}, logger, serviceLabel } = params;
29527
+ const baseURL = overrideConfig.baseURL || config.baseURL || "";
29528
+ const url = `${baseURL}${endpoint}`;
29529
+ const headers = { ...config.headers, ...overrideConfig.headers };
29530
+ const controller = new AbortController();
29531
+ const timeout = overrideConfig.timeout ?? config.timeout ?? DEFAULT_BACKEND_API_TIMEOUT;
29532
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
29533
+ try {
29534
+ const response = await fetch(url, {
29535
+ method: config.method,
29536
+ headers,
29537
+ body: config.body,
29538
+ signal: controller.signal
29539
+ });
29540
+ clearTimeout(timeoutId);
29541
+ if (!response.ok) {
29542
+ const errorText = await response.text();
29543
+ throw new Error("HTTP_REQUEST_FAILED", { cause: new Error(`HTTP ${response.status}: ${errorText}`) });
29544
+ }
29545
+ const data = await response.json();
29546
+ return data;
29547
+ } catch (error) {
29548
+ clearTimeout(timeoutId);
29549
+ if (error instanceof Error) {
29550
+ if (error.name === "AbortError") {
29551
+ throw new Error("REQUEST_TIMEOUT", { cause: new Error(`Request timeout after ${timeout}ms`) });
29552
+ }
29553
+ logger.error(`[${serviceLabel}] Request error`, error);
29554
+ throw error;
29555
+ }
29556
+ logger.error(`[${serviceLabel}] Unknown error`, error);
29557
+ throw new Error("UNKNOWN_REQUEST_ERROR", { cause: error });
29558
+ }
29559
+ }
29560
+ var SwapTokenSchema = v4.object({
29561
+ symbol: v4.string(),
29562
+ name: v4.string(),
29563
+ decimals: v4.number(),
29564
+ address: v4.string(),
29565
+ chainKey: v4.string(),
29566
+ hubAsset: v4.string(),
29567
+ vault: v4.string()
29568
+ });
29569
+ var IntentResponseSchema = v4.object({
29570
+ intentId: v4.string(),
29571
+ creator: v4.string(),
29572
+ inputToken: v4.string(),
29573
+ outputToken: v4.string(),
29574
+ inputAmount: v4.string(),
29575
+ minOutputAmount: v4.string(),
29576
+ deadline: v4.string(),
29577
+ allowPartialFill: v4.boolean(),
29578
+ srcChain: v4.string(),
29579
+ dstChain: v4.string(),
29580
+ srcAddress: v4.string(),
29581
+ dstAddress: v4.string(),
29582
+ solver: v4.string(),
29583
+ data: v4.string()
29584
+ });
29585
+ var RelayExtraDataResponseSchema = v4.object({
29586
+ address: v4.string(),
29587
+ payload: v4.string()
29588
+ });
29589
+ var makeCreateIntentResponseSchema = (txSchema) => v4.object({
29590
+ tx: txSchema,
29591
+ intent: IntentResponseSchema,
29592
+ relayData: RelayExtraDataResponseSchema
29593
+ });
29594
+ var GetSwapTokensResponseSchema = v4.record(v4.string(), v4.array(SwapTokenSchema));
29595
+ var GetSwapTokensByChainResponseSchema = v4.array(SwapTokenSchema);
29596
+ var makeQuoteResponseSchema = (txSchema) => v4.object({
29597
+ quotedAmount: v4.string(),
29598
+ txData: v4.optional(makeCreateIntentResponseSchema(txSchema))
29599
+ });
29600
+ var DeadlineResponseSchema = v4.object({
29601
+ deadline: v4.string()
29602
+ });
29603
+ var AllowanceCheckResponseSchema = v4.object({
29604
+ valid: v4.boolean()
29605
+ });
29606
+ var makeApproveResponseSchema = (txSchema) => v4.object({ tx: txSchema });
29607
+ var SubmitIntentResponseSchema = v4.object({
29608
+ result: v4.unknown()
29609
+ });
29610
+ var StatusResponseSchema = v4.object({
29611
+ status: v4.picklist([-1, 1, 2, 3, 4]),
29612
+ fillTxHash: v4.optional(v4.string())
29613
+ });
29614
+ var makeCancelIntentResponseSchema = (txSchema) => v4.object({ tx: txSchema });
29615
+ var IntentHashResponseSchema = v4.object({
29616
+ hash: v4.string()
29617
+ });
29618
+ var IntentPacketResponseSchema = v4.object({
29619
+ srcChainId: v4.number(),
29620
+ srcTxHash: v4.string(),
29621
+ srcAddress: v4.string(),
29622
+ status: v4.string(),
29623
+ dstChainId: v4.number(),
29624
+ connSn: v4.number(),
29625
+ dstAddress: v4.string(),
29626
+ dstTxHash: v4.string(),
29627
+ signatures: v4.array(v4.string()),
29628
+ payload: v4.string()
29629
+ });
29630
+ var IntentStateResponseSchema = v4.object({
29631
+ exists: v4.boolean(),
29632
+ remainingInput: v4.string(),
29633
+ receivedOutput: v4.string(),
29634
+ pendingPayment: v4.boolean()
29635
+ });
29636
+ var GasEstimateResponseSchema = v4.object({
29637
+ gas: v4.unknown()
29638
+ });
29639
+ var FeeResponseSchema = v4.object({
29640
+ fee: v4.string()
29641
+ });
29642
+ var SubmitTxResponseSchema = v4.object({
29643
+ success: v4.boolean(),
29644
+ data: v4.object({
29645
+ status: v4.picklist(["inserted", "duplicate"]),
29646
+ message: v4.string()
29647
+ })
29648
+ });
29649
+ var SubmitSwapTxStatusSchema = v4.picklist([
29650
+ "pending",
29651
+ "relaying",
29652
+ "relayed",
29653
+ "posting_execution",
29654
+ "executed",
29655
+ "failed"
29656
+ ]);
29657
+ var PacketDataSchema = v4.object({
29658
+ src_chain_id: v4.number(),
29659
+ src_tx_hash: v4.string(),
29660
+ src_address: v4.string(),
29661
+ status: v4.picklist(["pending", "validating", "executing", "executed"]),
29662
+ dst_chain_id: v4.number(),
29663
+ conn_sn: v4.number(),
29664
+ dst_address: v4.string(),
29665
+ dst_tx_hash: v4.string(),
29666
+ signatures: v4.array(v4.string()),
29667
+ payload: v4.string()
29668
+ });
29669
+ var SubmitTxStatusResultSchema = v4.object({
29670
+ dstIntentTxHash: v4.string(),
29671
+ packetData: v4.optional(PacketDataSchema),
29672
+ intent_hash: v4.optional(v4.string())
29673
+ });
29674
+ var SubmitTxStatusDataSchema = v4.object({
29675
+ txHash: v4.string(),
29676
+ srcChainKey: v4.string(),
29677
+ status: SubmitSwapTxStatusSchema,
29678
+ failedAtStep: v4.optional(SubmitSwapTxStatusSchema),
29679
+ failureReason: v4.optional(v4.string()),
29680
+ processingAttempts: v4.number(),
29681
+ abandonedAt: v4.optional(v4.string()),
29682
+ result: v4.optional(SubmitTxStatusResultSchema),
29683
+ userMessage: v4.optional(v4.string()),
29684
+ intentCancelled: v4.optional(v4.boolean())
29685
+ });
29686
+ var SubmitTxStatusResponseSchema = v4.object({
29687
+ success: v4.boolean(),
29688
+ data: SubmitTxStatusDataSchema
29689
+ });
29690
+ var AddressSchema = v4.custom((input) => typeof input === "string");
29691
+ var HexSchema = v4.custom((input) => typeof input === "string");
29692
+ var BigintFromString = v4.pipe(v4.string(), v4.toBigint());
29693
+ var BytesFromIndexRecord = v4.pipe(
29694
+ v4.record(v4.string(), v4.number()),
29695
+ v4.transform((indexed) => Uint8Array.from(Object.values(indexed)))
29696
+ );
29697
+ var EvmRawTxSchema = v4.object({
29698
+ from: AddressSchema,
29699
+ to: AddressSchema,
29700
+ value: BigintFromString,
29701
+ data: HexSchema
29702
+ });
29703
+ var SolanaRawTxSchema = v4.object({
29704
+ from: v4.string(),
29705
+ to: v4.string(),
29706
+ value: BigintFromString,
29707
+ data: v4.string()
29708
+ });
29709
+ var SuiRawTxSchema = v4.object({
29710
+ from: HexSchema,
29711
+ to: v4.string(),
29712
+ value: BigintFromString,
29713
+ data: v4.string()
29714
+ });
29715
+ var StellarRawTxSchema = v4.object({
29716
+ from: v4.string(),
29717
+ to: v4.string(),
29718
+ value: BigintFromString,
29719
+ data: v4.string()
29720
+ });
29721
+ var InjectiveRawTxSchema = v4.object({
29722
+ from: HexSchema,
29723
+ to: HexSchema,
29724
+ signedDoc: v4.object({
29725
+ bodyBytes: BytesFromIndexRecord,
29726
+ authInfoBytes: BytesFromIndexRecord,
29727
+ chainId: v4.string(),
29728
+ accountNumber: BigintFromString
29729
+ })
29730
+ });
29731
+ var NearRawTxSchema = v4.object({
29732
+ signerId: v4.string(),
29733
+ params: v4.object({
29734
+ contractId: v4.string(),
29735
+ method: v4.string(),
29736
+ args: v4.custom((input) => typeof input === "object" && input !== null),
29737
+ gas: v4.optional(BigintFromString),
29738
+ deposit: v4.optional(BigintFromString)
29739
+ })
29740
+ });
29741
+ var IconRawTxSchema = v4.record(
29742
+ v4.string(),
29743
+ v4.union([v4.string(), v4.custom((input) => typeof input === "object" && input !== null)])
29744
+ );
29745
+ var StacksRawTxSchema = v4.object({
29746
+ payload: v4.string(),
29747
+ estimatedLength: v4.optional(v4.number())
29748
+ });
29749
+ var AnyRawTxSchema = v4.custom((input) => typeof input === "object" && input !== null);
29750
+ function rawTxSchemaForChainKey(chainKey) {
29751
+ let chainType;
29752
+ try {
29753
+ chainType = getChainType(chainKey);
29754
+ } catch {
29755
+ chainType = void 0;
29756
+ }
29757
+ switch (chainType) {
29758
+ case "EVM":
29759
+ return EvmRawTxSchema;
29760
+ case "SOLANA":
29761
+ return SolanaRawTxSchema;
29762
+ case "SUI":
29763
+ return SuiRawTxSchema;
29764
+ case "STELLAR":
29765
+ return StellarRawTxSchema;
29766
+ case "INJECTIVE":
29767
+ return InjectiveRawTxSchema;
29768
+ case "ICON":
29769
+ return IconRawTxSchema;
29770
+ case "STACKS":
29771
+ return StacksRawTxSchema;
29772
+ case "NEAR":
29773
+ return NearRawTxSchema;
29774
+ default:
29775
+ return AnyRawTxSchema;
29776
+ }
29777
+ }
29778
+
29779
+ // src/backendApi/SwapsApiService.ts
29780
+ var SwapsApiService = class {
29781
+ // Fully-resolved swaps-API config supplied by the caller (BackendApiService resolves the
29782
+ // `ApiConfig` union via `resolveSwapsApiConfig`); this service does not resolve the union.
29783
+ config;
29784
+ headers;
29785
+ logger;
29101
29786
  constructor(config, logger = consoleLogger) {
29102
29787
  this.config = config;
29103
29788
  this.headers = { ...config.headers };
29104
29789
  this.logger = logger;
29105
29790
  }
29106
- headers;
29107
- logger;
29108
29791
  /**
29109
- * Execute a single HTTP request and return the parsed JSON body.
29110
- *
29111
- * Applies an `AbortController`-backed timeout (falls back to `this.config.timeout`
29112
- * when `config.timeout` is absent). Throws on non-2xx status codes or when the
29113
- * request exceeds the timeout, so callers should use {@link request} instead of
29114
- * calling this directly.
29115
- *
29116
- * @throws `Error('HTTP_REQUEST_FAILED')` on non-2xx responses.
29117
- * @throws `Error('REQUEST_TIMEOUT')` when the request exceeds the timeout.
29118
- * @throws `Error('UNKNOWN_REQUEST_ERROR')` for any other unexpected failure.
29792
+ * Issues a single HTTP request, validates the JSON body against `schema`, and
29793
+ * wraps the result in `Result<T>`. The service-level `baseURL`/`timeout`/
29794
+ * `headers` are merged with the optional per-call `overrideConfig` (which takes
29795
+ * precedence) inside {@link makeRequest}. Every public method delegates here.
29119
29796
  */
29120
- async makeRequest(endpoint, config) {
29121
- const url = config.baseURL ? `${config.baseURL}${endpoint}` : `${this.config.baseURL}${endpoint}`;
29122
- const headers = { ...this.headers, ...config.headers };
29123
- const controller = new AbortController();
29124
- const timeout = config.timeout ?? this.config.timeout;
29125
- const timeoutId = setTimeout(() => controller.abort(), timeout);
29797
+ async request(endpoint, config, schema, overrideConfig) {
29126
29798
  try {
29127
- const response = await fetch(url, {
29128
- method: config.method,
29129
- headers,
29130
- body: config.body,
29131
- signal: controller.signal
29799
+ const raw = await makeRequest({
29800
+ endpoint,
29801
+ config: { baseURL: this.config.baseURL, timeout: this.config.timeout, headers: this.headers, ...config },
29802
+ overrideConfig,
29803
+ logger: this.logger,
29804
+ serviceLabel: "SwapsApiService"
29132
29805
  });
29133
- clearTimeout(timeoutId);
29134
- if (!response.ok) {
29135
- const errorText = await response.text();
29136
- throw new Error("HTTP_REQUEST_FAILED", { cause: new Error(`HTTP ${response.status}: ${errorText}`) });
29806
+ const parsed = v4.safeParse(schema, raw);
29807
+ if (!parsed.success) {
29808
+ return {
29809
+ ok: false,
29810
+ error: new SodaxError("EXTERNAL_API_ERROR", `Invalid response shape from swaps API for ${endpoint}`, {
29811
+ feature: "backend",
29812
+ context: { api: "swaps", endpoint, reason: "invalid_response_shape", issues: v4.flatten(parsed.issues) }
29813
+ })
29814
+ };
29137
29815
  }
29138
- const data = await response.json();
29139
- return data;
29816
+ return { ok: true, value: parsed.output };
29140
29817
  } catch (error) {
29141
- clearTimeout(timeoutId);
29142
- if (error instanceof Error) {
29143
- if (error.name === "AbortError") {
29144
- throw new Error("REQUEST_TIMEOUT", { cause: new Error(`Request timeout after ${timeout}ms`) });
29145
- }
29146
- this.logger.error("[BackendApiService] Request error", error);
29147
- throw error;
29148
- }
29149
- this.logger.error("[BackendApiService] Unknown error", error);
29150
- throw new Error("UNKNOWN_REQUEST_ERROR", { cause: error });
29818
+ return {
29819
+ ok: false,
29820
+ error: new SodaxError(
29821
+ "EXTERNAL_API_ERROR",
29822
+ error instanceof Error ? error.message : `Request to ${endpoint} failed`,
29823
+ {
29824
+ feature: "backend",
29825
+ cause: error,
29826
+ context: { api: "swaps", endpoint }
29827
+ }
29828
+ )
29829
+ };
29830
+ }
29831
+ }
29832
+ // ──────────────────────────────────────────────────────────────────────
29833
+ // Tokens
29834
+ // ──────────────────────────────────────────────────────────────────────
29835
+ /**
29836
+ * Fetch all supported swap tokens grouped by SpokeChainKey.
29837
+ *
29838
+ * @returns `Result<GetSwapTokensResponseV2>` — map of chain key → token list.
29839
+ */
29840
+ async getTokens(config) {
29841
+ return this.request("/swaps/tokens", { method: "GET" }, GetSwapTokensResponseSchema, config);
29842
+ }
29843
+ /**
29844
+ * Fetch supported swap tokens for a single SpokeChainKey.
29845
+ *
29846
+ * @param chainKey - SODAX SpokeChainKey (e.g. `0xa4b1.arbitrum`, `solana`).
29847
+ * @returns `Result<GetSwapTokensByChainResponseV2>` — token list for the chain.
29848
+ */
29849
+ async getTokensByChain(chainKey, config) {
29850
+ return this.request(
29851
+ `/swaps/tokens/${chainKey}`,
29852
+ { method: "GET" },
29853
+ GetSwapTokensByChainResponseSchema,
29854
+ config
29855
+ );
29856
+ }
29857
+ // ──────────────────────────────────────────────────────────────────────
29858
+ // Quote · deadline
29859
+ // ──────────────────────────────────────────────────────────────────────
29860
+ /**
29861
+ * Get a solver quote for a cross-chain swap.
29862
+ *
29863
+ * Pass `query.includeTxData = true` to also build an unsigned create-intent
29864
+ * transaction (`txData`) using the quoted amount as `minOutputAmount`; in that
29865
+ * case `srcAddress`/`dstAddress` are required in the body.
29866
+ *
29867
+ * @returns `Result<QuoteResponseV2>` — `quotedAmount` (decimal string) and optional `txData`.
29868
+ */
29869
+ async getQuote(body, query, config) {
29870
+ const endpoint = query?.includeTxData ? "/swaps/quote?includeTxData=true" : "/swaps/quote";
29871
+ const txSchema = rawTxSchemaForChainKey(body.tokenSrcChainKey);
29872
+ return this.request(
29873
+ endpoint,
29874
+ { method: "POST", body: toJsonBody(body) },
29875
+ makeQuoteResponseSchema(txSchema),
29876
+ config
29877
+ );
29878
+ }
29879
+ /**
29880
+ * Compute a swap deadline (hub timestamp + `offsetSeconds`, default 300s).
29881
+ *
29882
+ * @returns `Result<DeadlineResponseV2>` — unix-seconds deadline (decimal string).
29883
+ */
29884
+ async getDeadline(query, config) {
29885
+ const queryParams = new URLSearchParams();
29886
+ if (query?.offsetSeconds !== void 0) queryParams.append("offsetSeconds", String(query.offsetSeconds));
29887
+ const queryString = queryParams.toString();
29888
+ const endpoint = queryString.length > 0 ? `/swaps/deadline?${queryString}` : "/swaps/deadline";
29889
+ return this.request(endpoint, { method: "GET" }, DeadlineResponseSchema, config);
29890
+ }
29891
+ // ──────────────────────────────────────────────────────────────────────
29892
+ // Allowance · approve · create intent
29893
+ // ──────────────────────────────────────────────────────────────────────
29894
+ /**
29895
+ * Check whether the source token allowance is already sufficient for the intent.
29896
+ *
29897
+ * @returns `Result<AllowanceCheckResponseV2>` — `{ valid }`.
29898
+ */
29899
+ async checkAllowance(body, config) {
29900
+ return this.request(
29901
+ "/swaps/allowance/check",
29902
+ { method: "POST", body: toJsonBody(body) },
29903
+ AllowanceCheckResponseSchema,
29904
+ config
29905
+ );
29906
+ }
29907
+ /**
29908
+ * Build an unsigned token-approval transaction for the source token.
29909
+ *
29910
+ * @returns `Result<ApproveResponseV2>` — `{ tx }` (chain-specific unsigned tx).
29911
+ */
29912
+ async approve(body, config) {
29913
+ const txSchema = rawTxSchemaForChainKey(body.srcChainKey);
29914
+ return this.request(
29915
+ "/swaps/approve",
29916
+ { method: "POST", body: toJsonBody(body) },
29917
+ makeApproveResponseSchema(txSchema),
29918
+ config
29919
+ );
29920
+ }
29921
+ /**
29922
+ * Build an unsigned create-intent transaction.
29923
+ *
29924
+ * @returns `Result<CreateIntentResponseV2>` — `{ tx, intent, relayData }`.
29925
+ */
29926
+ async createIntent(body, config) {
29927
+ const txSchema = rawTxSchemaForChainKey(body.srcChainKey);
29928
+ return this.request(
29929
+ "/swaps/intents",
29930
+ { method: "POST", body: toJsonBody(body) },
29931
+ makeCreateIntentResponseSchema(txSchema),
29932
+ config
29933
+ );
29934
+ }
29935
+ // ──────────────────────────────────────────────────────────────────────
29936
+ // Intent lifecycle: submit · status · cancel · hash · packet · extra-data
29937
+ // ──────────────────────────────────────────────────────────────────────
29938
+ /**
29939
+ * Submit the broadcast intent tx to the relay.
29940
+ *
29941
+ * @returns `Result<SubmitIntentResponseV2>` — `{ result }` (opaque relay response).
29942
+ */
29943
+ async submitIntent(body, config) {
29944
+ return this.request(
29945
+ "/swaps/intents/submit",
29946
+ { method: "POST", body: toJsonBody(body) },
29947
+ SubmitIntentResponseSchema,
29948
+ config
29949
+ );
29950
+ }
29951
+ /**
29952
+ * Poll the solver for intent execution status.
29953
+ *
29954
+ * @returns `Result<StatusResponseV2>` — `{ status, fillTxHash? }` (`fillTxHash` set when `status === 3`).
29955
+ */
29956
+ async getStatus(body, config) {
29957
+ return this.request(
29958
+ "/swaps/intents/status",
29959
+ { method: "POST", body: toJsonBody(body) },
29960
+ StatusResponseSchema,
29961
+ config
29962
+ );
29963
+ }
29964
+ /**
29965
+ * Build an unsigned cancel-intent transaction. The `intent` field carries
29966
+ * `bigint` numerics — {@link toJsonBody} serializes them to decimal strings.
29967
+ *
29968
+ * @returns `Result<CancelIntentResponseV2>` — `{ tx }`.
29969
+ */
29970
+ async cancelIntent(body, config) {
29971
+ const txSchema = rawTxSchemaForChainKey(body.srcChainKey);
29972
+ return this.request(
29973
+ "/swaps/intents/cancel",
29974
+ { method: "POST", body: toJsonBody(body) },
29975
+ makeCancelIntentResponseSchema(txSchema),
29976
+ config
29977
+ );
29978
+ }
29979
+ /**
29980
+ * Compute the keccak256 hash of an Intent struct. The `intent` field carries
29981
+ * `bigint` numerics — {@link toJsonBody} serializes them to decimal strings.
29982
+ *
29983
+ * @returns `Result<IntentHashResponseV2>` — `{ hash }`.
29984
+ */
29985
+ async getIntentHash(body, config) {
29986
+ return this.request(
29987
+ "/swaps/intents/hash",
29988
+ { method: "POST", body: toJsonBody(body) },
29989
+ IntentHashResponseSchema,
29990
+ config
29991
+ );
29992
+ }
29993
+ /**
29994
+ * Long-poll the relayer until the fill packet lands on the destination chain.
29995
+ *
29996
+ * @returns `Result<IntentPacketResponseV2>` — delivered packet data.
29997
+ */
29998
+ async getSolvedIntentPacket(body, config) {
29999
+ return this.request(
30000
+ "/swaps/intents/packet",
30001
+ { method: "POST", body: toJsonBody(body) },
30002
+ IntentPacketResponseSchema,
30003
+ config
30004
+ );
30005
+ }
30006
+ /**
30007
+ * Recover the relay extra data needed by `/swaps/intents/submit`. Provide
30008
+ * EITHER `txHash` OR `intent` (whose `bigint` numerics {@link toJsonBody} serializes).
30009
+ *
30010
+ * @returns `Result<IntentExtraDataResponseV2>` — `{ address, payload }`.
30011
+ */
30012
+ async getIntentSubmitTxExtraData(body, config) {
30013
+ return this.request(
30014
+ "/swaps/intents/extra-data",
30015
+ { method: "POST", body: toJsonBody(body) },
30016
+ RelayExtraDataResponseSchema,
30017
+ config
30018
+ );
30019
+ }
30020
+ /**
30021
+ * Get the on-chain fill state for an intent by its hub-chain tx hash.
30022
+ *
30023
+ * @returns `Result<IntentStateV2>` — `{ exists, remainingInput, receivedOutput, pendingPayment }`.
30024
+ */
30025
+ async getFilledIntent(txHash, config) {
30026
+ return this.request(`/swaps/intents/${txHash}/fill`, { method: "GET" }, IntentStateResponseSchema, config);
30027
+ }
30028
+ /**
30029
+ * Look up an Intent struct by its hub-chain creation tx hash.
30030
+ *
30031
+ * @returns `Result<GetIntentResponseV2>` — the decoded intent (bigint fields as decimal strings).
30032
+ */
30033
+ async getIntent(txHash, config) {
30034
+ return this.request(`/swaps/intents/${txHash}`, { method: "GET" }, IntentResponseSchema, config);
30035
+ }
30036
+ // ──────────────────────────────────────────────────────────────────────
30037
+ // Limit orders · gas · fees
30038
+ // ──────────────────────────────────────────────────────────────────────
30039
+ /**
30040
+ * Build an unsigned create-limit-order-intent transaction (same as create-intent
30041
+ * but `deadline` is optional).
30042
+ *
30043
+ * @returns `Result<CreateLimitOrderResponseV2>` — `{ tx, intent, relayData }`.
30044
+ */
30045
+ async createLimitOrderIntent(body, config) {
30046
+ const txSchema = rawTxSchemaForChainKey(body.srcChainKey);
30047
+ return this.request(
30048
+ "/swaps/limit-orders",
30049
+ { method: "POST", body: toJsonBody(body) },
30050
+ makeCreateIntentResponseSchema(txSchema),
30051
+ config
30052
+ );
30053
+ }
30054
+ /**
30055
+ * Estimate gas for a raw transaction on a spoke chain.
30056
+ *
30057
+ * @returns `Result<GasEstimateResponseV2>` — `{ gas }` (chain-specific shape).
30058
+ */
30059
+ async estimateGas(body, config) {
30060
+ return this.request(
30061
+ "/swaps/gas/estimate",
30062
+ { method: "POST", body: toJsonBody(body) },
30063
+ GasEstimateResponseSchema,
30064
+ config
30065
+ );
30066
+ }
30067
+ /**
30068
+ * Compute the partner fee for a given input amount.
30069
+ *
30070
+ * @returns `Result<FeeResponseV2>` — `{ fee }` (decimal string).
30071
+ */
30072
+ async getPartnerFee(query, config) {
30073
+ const queryParams = new URLSearchParams({ amount: query.amount });
30074
+ return this.request(
30075
+ `/swaps/fees/partner?${queryParams.toString()}`,
30076
+ { method: "GET" },
30077
+ FeeResponseSchema,
30078
+ config
30079
+ );
30080
+ }
30081
+ /**
30082
+ * Compute the protocol (solver) fee for a given input amount.
30083
+ *
30084
+ * @returns `Result<FeeResponseV2>` — `{ fee }` (decimal string).
30085
+ */
30086
+ async getSolverFee(query, config) {
30087
+ const queryParams = new URLSearchParams({ amount: query.amount });
30088
+ return this.request(
30089
+ `/swaps/fees/solver?${queryParams.toString()}`,
30090
+ { method: "GET" },
30091
+ FeeResponseSchema,
30092
+ config
30093
+ );
30094
+ }
30095
+ // ──────────────────────────────────────────────────────────────────────
30096
+ // Submit-tx state machine
30097
+ // ──────────────────────────────────────────────────────────────────────
30098
+ /**
30099
+ * Submit a swap transaction to be processed (relay, post-execution, etc.). The
30100
+ * `intent` field carries `bigint` numerics — {@link toJsonBody} serializes them.
30101
+ * Idempotent on `(txHash, srcChainKey)`.
30102
+ *
30103
+ * @returns `Result<SubmitTxResponseV2>` — `{ success, data: { status, message } }`.
30104
+ */
30105
+ async submitTx(body, config) {
30106
+ return this.request(
30107
+ "/swaps/submit-tx",
30108
+ { method: "POST", body: toJsonBody(body) },
30109
+ SubmitTxResponseSchema,
30110
+ config
30111
+ );
30112
+ }
30113
+ /**
30114
+ * Get the processing status of a submitted swap transaction by `(txHash, srcChainKey)`.
30115
+ *
30116
+ * @returns `Result<SubmitTxStatusResponseV2>` — `{ success, data }` (processing state).
30117
+ */
30118
+ async getSubmitTxStatus(query, config) {
30119
+ const queryParams = new URLSearchParams({ txHash: query.txHash, srcChainKey: query.srcChainKey });
30120
+ return this.request(
30121
+ `/swaps/submit-tx/status?${queryParams.toString()}`,
30122
+ { method: "GET" },
30123
+ SubmitTxStatusResponseSchema,
30124
+ config
30125
+ );
30126
+ }
30127
+ // ──────────────────────────────────────────────────────────────────────
30128
+ // Utilities (parity with BackendApiService)
30129
+ // ──────────────────────────────────────────────────────────────────────
30130
+ /**
30131
+ * Merge additional headers into the service's default header set. Existing
30132
+ * keys are overwritten; keys absent from `headers` are preserved.
30133
+ */
30134
+ setHeaders(headers) {
30135
+ Object.entries(headers).forEach(([key, value]) => {
30136
+ this.headers[key] = value;
30137
+ });
30138
+ }
30139
+ /** Return the base URL the service is currently pointing at. */
30140
+ getBaseURL() {
30141
+ return this.config.baseURL;
30142
+ }
30143
+ };
30144
+
30145
+ // src/backendApi/apiConfig.ts
30146
+ function isCustomApiConfig(config) {
30147
+ return "baseApiConfig" in config || "swapsApiConfig" in config;
30148
+ }
30149
+ function layerConfigs(...slices) {
30150
+ return slices.reduce(
30151
+ (acc, slice) => slice ? {
30152
+ baseURL: slice.baseURL ?? acc.baseURL,
30153
+ timeout: slice.timeout ?? acc.timeout,
30154
+ headers: { ...acc.headers, ...slice.headers }
30155
+ } : acc,
30156
+ {
30157
+ baseURL: DEFAULT_BACKEND_API_ENDPOINT,
30158
+ timeout: DEFAULT_BACKEND_API_TIMEOUT,
30159
+ headers: { ...DEFAULT_BACKEND_API_HEADERS }
29151
30160
  }
30161
+ );
30162
+ }
30163
+ function resolveBaseApiConfig(config) {
30164
+ if (isCustomApiConfig(config)) {
30165
+ return layerConfigs(config.baseApiConfig);
30166
+ }
30167
+ return layerConfigs(config);
30168
+ }
30169
+ function resolveSwapsApiConfig(config) {
30170
+ if (isCustomApiConfig(config)) {
30171
+ return layerConfigs(config.baseApiConfig, config.swapsApiConfig);
30172
+ }
30173
+ return layerConfigs(config);
30174
+ }
30175
+ var ChainKeySchema = v4.custom((input) => typeof input === "string");
30176
+ var SpokeChainKeySchema = v4.custom((input) => typeof input === "string");
30177
+ var AddressSchema2 = v4.custom((input) => typeof input === "string");
30178
+ var XTokenSchema = v4.object({
30179
+ symbol: v4.string(),
30180
+ name: v4.string(),
30181
+ decimals: v4.number(),
30182
+ address: v4.string(),
30183
+ chainKey: ChainKeySchema,
30184
+ hubAsset: AddressSchema2,
30185
+ vault: AddressSchema2,
30186
+ access: v4.optional(v4.picklist(["withdrawOnly", "depositOnly"]))
30187
+ });
30188
+ var IntentStructSchema = v4.object({
30189
+ intentId: v4.string(),
30190
+ creator: v4.string(),
30191
+ inputToken: v4.string(),
30192
+ outputToken: v4.string(),
30193
+ inputAmount: v4.string(),
30194
+ minOutputAmount: v4.string(),
30195
+ deadline: v4.string(),
30196
+ allowPartialFill: v4.boolean(),
30197
+ srcChain: v4.number(),
30198
+ dstChain: v4.number(),
30199
+ srcAddress: v4.string(),
30200
+ dstAddress: v4.string(),
30201
+ solver: v4.string(),
30202
+ data: v4.string()
30203
+ });
30204
+ var IntentResponseSchema2 = v4.object({
30205
+ intentHash: v4.string(),
30206
+ txHash: v4.string(),
30207
+ logIndex: v4.number(),
30208
+ chainId: v4.number(),
30209
+ blockNumber: v4.number(),
30210
+ open: v4.boolean(),
30211
+ intent: IntentStructSchema,
30212
+ events: v4.array(v4.unknown())
30213
+ });
30214
+ var UserIntentsResponseSchema = v4.object({
30215
+ total: v4.number(),
30216
+ offset: v4.number(),
30217
+ limit: v4.number(),
30218
+ items: v4.array(IntentResponseSchema2)
30219
+ });
30220
+ var OrderbookResponseSchema = v4.object({
30221
+ total: v4.number(),
30222
+ data: v4.array(
30223
+ v4.object({
30224
+ intentState: v4.object({
30225
+ exists: v4.boolean(),
30226
+ remainingInput: v4.string(),
30227
+ receivedOutput: v4.string(),
30228
+ pendingPayment: v4.boolean()
30229
+ }),
30230
+ intentData: v4.object({
30231
+ intentId: v4.string(),
30232
+ creator: v4.string(),
30233
+ inputToken: v4.string(),
30234
+ outputToken: v4.string(),
30235
+ inputAmount: v4.string(),
30236
+ minOutputAmount: v4.string(),
30237
+ deadline: v4.string(),
30238
+ allowPartialFill: v4.boolean(),
30239
+ srcChain: v4.number(),
30240
+ dstChain: v4.number(),
30241
+ srcAddress: v4.string(),
30242
+ dstAddress: v4.string(),
30243
+ solver: v4.string(),
30244
+ data: v4.string(),
30245
+ intentHash: v4.string(),
30246
+ txHash: v4.string(),
30247
+ blockNumber: v4.number()
30248
+ })
30249
+ })
30250
+ )
30251
+ });
30252
+ var MoneyMarketPositionSchema = v4.object({
30253
+ userAddress: v4.string(),
30254
+ positions: v4.array(
30255
+ v4.object({
30256
+ reserveAddress: v4.string(),
30257
+ aTokenAddress: v4.string(),
30258
+ variableDebtTokenAddress: v4.string(),
30259
+ aTokenBalance: v4.string(),
30260
+ variableDebtTokenBalance: v4.string(),
30261
+ blockNumber: v4.number()
30262
+ })
30263
+ )
30264
+ });
30265
+ var MoneyMarketAssetSchema = v4.object({
30266
+ reserveAddress: v4.string(),
30267
+ aTokenAddress: v4.string(),
30268
+ totalATokenBalance: v4.string(),
30269
+ variableDebtTokenAddress: v4.string(),
30270
+ totalVariableDebtTokenBalance: v4.string(),
30271
+ liquidityRate: v4.string(),
30272
+ symbol: v4.string(),
30273
+ totalSuppliers: v4.number(),
30274
+ totalBorrowers: v4.number(),
30275
+ variableBorrowRate: v4.string(),
30276
+ stableBorrowRate: v4.string(),
30277
+ liquidityIndex: v4.string(),
30278
+ variableBorrowIndex: v4.string(),
30279
+ blockNumber: v4.number()
30280
+ });
30281
+ var MoneyMarketAssetsSchema = v4.array(MoneyMarketAssetSchema);
30282
+ var MoneyMarketAssetBorrowersSchema = v4.object({
30283
+ borrowers: v4.array(v4.string()),
30284
+ total: v4.number(),
30285
+ offset: v4.number(),
30286
+ limit: v4.number()
30287
+ });
30288
+ var MoneyMarketAssetSuppliersSchema = v4.object({
30289
+ suppliers: v4.array(v4.string()),
30290
+ total: v4.number(),
30291
+ offset: v4.number(),
30292
+ limit: v4.number()
30293
+ });
30294
+ var MoneyMarketBorrowersSchema = v4.object({
30295
+ borrowers: v4.array(v4.string()),
30296
+ total: v4.number(),
30297
+ offset: v4.number(),
30298
+ limit: v4.number()
30299
+ });
30300
+ var GetChainsResponseSchema = v4.array(SpokeChainKeySchema);
30301
+ var TokensByChainMapSchema = v4.custom(
30302
+ (input) => typeof input === "object" && input !== null && Object.values(input).every((tokens) => Array.isArray(tokens) && tokens.every((t) => v4.is(XTokenSchema, t)))
30303
+ );
30304
+ var TokensListSchema = v4.array(XTokenSchema);
30305
+ var ReserveAssetsSchema = v4.array(AddressSchema2);
30306
+
30307
+ // src/backendApi/BackendApiService.ts
30308
+ var BackendApiService = class {
30309
+ // sub-services exposing domain-specific APIs
30310
+ swaps;
30311
+ // resolved base-API slice of the ApiConfig union (flat config, or its `baseApiConfig`)
30312
+ config;
30313
+ headers;
30314
+ logger;
30315
+ constructor(config, logger = consoleLogger) {
30316
+ this.config = resolveBaseApiConfig(config);
30317
+ this.headers = { ...this.config.headers };
30318
+ this.logger = logger;
30319
+ this.swaps = new SwapsApiService(resolveSwapsApiConfig(config), this.logger);
29152
30320
  }
29153
30321
  /**
29154
30322
  * Wraps {@link makeRequest} in a `Result<T>` so all errors are captured rather
29155
30323
  * than propagated as thrown exceptions. Every public endpoint method delegates
29156
30324
  * here instead of calling `makeRequest` directly.
29157
30325
  */
29158
- async request(endpoint, config) {
30326
+ /**
30327
+ * Fold any per-call override (carried on `config` alongside `method`/`body`) over the service
30328
+ * defaults: baseURL truthy-fallback (an empty-string override defers to the default), timeout
30329
+ * nullish-fallback, headers merged (override wins per key). Mirrors the original makeRequest merge.
30330
+ */
30331
+ resolveRequestConfig(config) {
30332
+ const { baseURL, timeout, headers, ...rest } = config;
30333
+ return {
30334
+ ...rest,
30335
+ baseURL: baseURL || this.config.baseURL,
30336
+ timeout: timeout ?? this.config.timeout,
30337
+ headers: { ...this.headers, ...headers }
30338
+ };
30339
+ }
30340
+ /**
30341
+ * Wrap a thrown transport failure (HTTP_REQUEST_FAILED / REQUEST_TIMEOUT / network error) as the
30342
+ * canonical backend `SodaxError` — identical shape to SwapsApiService; the underlying failure is
30343
+ * preserved on `error.cause`.
30344
+ */
30345
+ toExternalApiError(endpoint, error) {
30346
+ return new SodaxError(
30347
+ "EXTERNAL_API_ERROR",
30348
+ error instanceof Error ? error.message : `Backend API request to ${endpoint} failed`,
30349
+ { feature: "backend", cause: error, context: { api: "backend", endpoint } }
30350
+ );
30351
+ }
30352
+ /**
30353
+ * Issue a request and validate the JSON body against `schema`, wrapping the result in `Result<T>`.
30354
+ * Mirrors {@link SwapsApiService}: a 2xx body that fails the schema is surfaced as
30355
+ * `EXTERNAL_API_ERROR` (`context.reason: 'invalid_response_shape'`) rather than returned untyped.
30356
+ * Every data/token/money-market method delegates here; the config/relay reads use
30357
+ * {@link requestUnvalidated} instead.
30358
+ */
30359
+ async request(endpoint, config, schema) {
30360
+ try {
30361
+ const raw = await makeRequest({
30362
+ endpoint,
30363
+ config: this.resolveRequestConfig(config),
30364
+ logger: this.logger,
30365
+ serviceLabel: "BackendApiService"
30366
+ });
30367
+ const parsed = v4.safeParse(schema, raw);
30368
+ if (!parsed.success) {
30369
+ return {
30370
+ ok: false,
30371
+ error: new SodaxError("EXTERNAL_API_ERROR", `Invalid response shape from backend API for ${endpoint}`, {
30372
+ feature: "backend",
30373
+ context: { api: "backend", endpoint, reason: "invalid_response_shape", issues: v4.flatten(parsed.issues) }
30374
+ })
30375
+ };
30376
+ }
30377
+ return { ok: true, value: parsed.output };
30378
+ } catch (error) {
30379
+ return { ok: false, error: this.toExternalApiError(endpoint, error) };
30380
+ }
30381
+ }
30382
+ /**
30383
+ * Issue a request WITHOUT response-shape validation (passthrough typing). Reserved for the
30384
+ * config/relay reads whose shapes are too large/brittle to schema-validate (`SodaxConfig` via
30385
+ * `getAllConfig`, `SpokeChainConfigMap` via `getSpokeChainConfig`) or carry `bigint` values that
30386
+ * cannot survive JSON validation (`getRelayChainIdMap`). `ConfigService` version-gates and falls
30387
+ * back to packaged defaults, so it relies on no response-shape guarantee from these endpoints.
30388
+ */
30389
+ async requestUnvalidated(endpoint, config) {
29159
30390
  try {
29160
- const value = await this.makeRequest(endpoint, config);
30391
+ const value = await makeRequest({
30392
+ endpoint,
30393
+ config: this.resolveRequestConfig(config),
30394
+ logger: this.logger,
30395
+ serviceLabel: "BackendApiService"
30396
+ });
29161
30397
  return { ok: true, value };
29162
30398
  } catch (error) {
29163
- return { ok: false, error };
30399
+ return { ok: false, error: this.toExternalApiError(endpoint, error) };
29164
30400
  }
29165
30401
  }
29166
30402
  // Intent endpoints
@@ -29175,7 +30411,7 @@ var BackendApiService = class {
29175
30411
  * open/closed state, token amounts, and any fill events.
29176
30412
  */
29177
30413
  async getIntentByTxHash(txHash, config) {
29178
- return this.request(`/intent/tx/${txHash}`, { ...config, method: "GET" });
30414
+ return this.request(`/intent/tx/${txHash}`, { ...config, method: "GET" }, IntentResponseSchema2);
29179
30415
  }
29180
30416
  /**
29181
30417
  * Fetch a swap intent by its canonical intent hash.
@@ -29184,58 +30420,7 @@ var BackendApiService = class {
29184
30420
  * @returns `Result<IntentResponse>` — on success, the full intent details.
29185
30421
  */
29186
30422
  async getIntentByHash(intentHash, config) {
29187
- return this.request(`/intent/${intentHash}`, { ...config, method: "GET" });
29188
- }
29189
- // Swap submit-tx endpoints
29190
- /**
29191
- * Submit a signed spoke-chain swap transaction to the backend for processing.
29192
- *
29193
- * The backend relays the transaction to the hub chain, posts execution data
29194
- * to the solver, and advances the intent through its lifecycle. The response
29195
- * shape is validated at runtime via a type guard; an invalid shape is
29196
- * returned as `{ ok: false }`.
29197
- *
29198
- * @param params - The signed transaction hash, source chain key, sender wallet
29199
- * address, intent data, and relay data required to process the swap.
29200
- * @returns `Result<SubmitSwapTxResponse>` — on success, a confirmation object
29201
- * with `success: true` and a human-readable `message`.
29202
- */
29203
- async submitSwapTx(params, config) {
29204
- const result = await this.request("/swaps/submit-tx", {
29205
- ...config,
29206
- method: "POST",
29207
- body: JSON.stringify(params)
29208
- });
29209
- if (!result.ok) return result;
29210
- if (!isSubmitSwapTxResponse(result.value)) {
29211
- return { ok: false, error: new Error("Invalid submitSwapTx response: unexpected response shape") };
29212
- }
29213
- return { ok: true, value: result.value };
29214
- }
29215
- /**
29216
- * Poll the backend relay pipeline for the current status of a previously
29217
- * submitted swap transaction.
29218
- *
29219
- * Status progresses through: `pending` → `verifying` → `verified` →
29220
- * `relaying` → `relayed` → `posting_execution` → `executed` (or `failed`).
29221
- *
29222
- * @param params - Object containing the source-chain transaction hash and,
29223
- * optionally, the source chain key to disambiguate cross-chain hashes.
29224
- * @returns `Result<SubmitSwapTxStatusResponse>` — on success, includes the
29225
- * current `status`, any `failureReason`, and (once executed) the
29226
- * `dstIntentTxHash` on the hub chain.
29227
- */
29228
- async getSubmitSwapTxStatus(params, config) {
29229
- const queryParams = new URLSearchParams();
29230
- queryParams.append("txHash", params.txHash);
29231
- if (params.srcChainKey) queryParams.append("srcChainKey", params.srcChainKey);
29232
- const endpoint = `/swaps/submit-tx/status?${queryParams.toString()}`;
29233
- const result = await this.request(endpoint, { ...config, method: "GET" });
29234
- if (!result.ok) return result;
29235
- if (!isSubmitSwapTxStatusResponse(result.value)) {
29236
- return { ok: false, error: new Error("Invalid submitSwapTxStatus response: unexpected response shape") };
29237
- }
29238
- return { ok: true, value: result.value };
30423
+ return this.request(`/intent/${intentHash}`, { ...config, method: "GET" }, IntentResponseSchema2);
29239
30424
  }
29240
30425
  // Solver endpoints
29241
30426
  /**
@@ -29252,7 +30437,7 @@ var BackendApiService = class {
29252
30437
  queryParams.append("limit", params.limit);
29253
30438
  const queryString = queryParams.toString();
29254
30439
  const endpoint = `/solver/orderbook?${queryString}`;
29255
- return this.request(endpoint, { ...config, method: "GET" });
30440
+ return this.request(endpoint, { ...config, method: "GET" }, OrderbookResponseSchema);
29256
30441
  }
29257
30442
  /**
29258
30443
  * Fetch all swap intents created by a specific wallet address, with optional
@@ -29278,7 +30463,7 @@ var BackendApiService = class {
29278
30463
  if (offset) queryParams.append("offset", offset);
29279
30464
  const queryString = queryParams.toString();
29280
30465
  const endpoint = queryString.length > 0 ? `/intent/user/${userAddress}?${queryString}` : `/intent/user/${userAddress}`;
29281
- return this.request(endpoint, { ...config, method: "GET" });
30466
+ return this.request(endpoint, { ...config, method: "GET" }, UserIntentsResponseSchema);
29282
30467
  }
29283
30468
  // Money Market endpoints
29284
30469
  /**
@@ -29293,7 +30478,11 @@ var BackendApiService = class {
29293
30478
  * position across all active reserves.
29294
30479
  */
29295
30480
  async getMoneyMarketPosition(userAddress, config) {
29296
- return this.request(`/moneymarket/position/${userAddress}`, { ...config, method: "GET" });
30481
+ return this.request(
30482
+ `/moneymarket/position/${userAddress}`,
30483
+ { ...config, method: "GET" },
30484
+ MoneyMarketPositionSchema
30485
+ );
29297
30486
  }
29298
30487
  /**
29299
30488
  * Fetch the on-chain state for every active money market reserve asset.
@@ -29302,7 +30491,7 @@ var BackendApiService = class {
29302
30491
  * snapshots including interest rates, liquidity indices, and participant counts.
29303
30492
  */
29304
30493
  async getAllMoneyMarketAssets(config) {
29305
- return this.request("/moneymarket/asset/all", { ...config, method: "GET" });
30494
+ return this.request("/moneymarket/asset/all", { ...config, method: "GET" }, MoneyMarketAssetsSchema);
29306
30495
  }
29307
30496
  /**
29308
30497
  * Fetch the on-chain state for a single money market reserve asset.
@@ -29312,7 +30501,11 @@ var BackendApiService = class {
29312
30501
  * including interest rates, total balances, and liquidity indices.
29313
30502
  */
29314
30503
  async getMoneyMarketAsset(reserveAddress, config) {
29315
- return this.request(`/moneymarket/asset/${reserveAddress}`, { ...config, method: "GET" });
30504
+ return this.request(
30505
+ `/moneymarket/asset/${reserveAddress}`,
30506
+ { ...config, method: "GET" },
30507
+ MoneyMarketAssetSchema
30508
+ );
29316
30509
  }
29317
30510
  /**
29318
30511
  * Fetch a paginated list of wallets that currently have an outstanding borrow
@@ -29329,7 +30522,7 @@ var BackendApiService = class {
29329
30522
  queryParams.append("limit", params.limit);
29330
30523
  const queryString = queryParams.toString();
29331
30524
  const endpoint = `/moneymarket/asset/${reserveAddress}/borrowers?${queryString}`;
29332
- return this.request(endpoint, { ...config, method: "GET" });
30525
+ return this.request(endpoint, { ...config, method: "GET" }, MoneyMarketAssetBorrowersSchema);
29333
30526
  }
29334
30527
  /**
29335
30528
  * Fetch a paginated list of wallets that currently have an active supply
@@ -29346,7 +30539,7 @@ var BackendApiService = class {
29346
30539
  queryParams.append("limit", params.limit);
29347
30540
  const queryString = queryParams.toString();
29348
30541
  const endpoint = `/moneymarket/asset/${reserveAddress}/suppliers?${queryString}`;
29349
- return this.request(endpoint, { ...config, method: "GET" });
30542
+ return this.request(endpoint, { ...config, method: "GET" }, MoneyMarketAssetSuppliersSchema);
29350
30543
  }
29351
30544
  /**
29352
30545
  * Fetch a paginated list of all wallet addresses that hold an active borrow
@@ -29362,7 +30555,7 @@ var BackendApiService = class {
29362
30555
  queryParams.append("limit", params.limit);
29363
30556
  const queryString = queryParams.toString();
29364
30557
  const endpoint = `/moneymarket/borrowers?${queryString}`;
29365
- return this.request(endpoint, { ...config, method: "GET" });
30558
+ return this.request(endpoint, { ...config, method: "GET" }, MoneyMarketBorrowersSchema);
29366
30559
  }
29367
30560
  /**
29368
30561
  * Fetch the complete SODAX runtime configuration in a single request.
@@ -29375,7 +30568,7 @@ var BackendApiService = class {
29375
30568
  * `config` is the current `SodaxConfig` used by all SDK services.
29376
30569
  */
29377
30570
  async getAllConfig(config) {
29378
- return this.request("/config/all", { ...config, method: "GET" });
30571
+ return this.requestUnvalidated("/config/all", { ...config, method: "GET" });
29379
30572
  }
29380
30573
  /**
29381
30574
  * Fetch the list of spoke chain keys that are currently supported by the
@@ -29388,7 +30581,7 @@ var BackendApiService = class {
29388
30581
  * `SpokeChainKey` strings (e.g. `["ethereum", "arbitrum", "solana", …]`).
29389
30582
  */
29390
30583
  async getChains(config) {
29391
- return this.request("/config/spoke/chains", { ...config, method: "GET" });
30584
+ return this.request("/config/spoke/chains", { ...config, method: "GET" }, GetChainsResponseSchema);
29392
30585
  }
29393
30586
  /**
29394
30587
  * Fetch the full map of tokens available for swapping, keyed by spoke chain.
@@ -29400,7 +30593,7 @@ var BackendApiService = class {
29400
30593
  * supported spoke chain key to its list of swappable `XToken` definitions.
29401
30594
  */
29402
30595
  async getSwapTokens(config) {
29403
- return this.request("/config/swap/tokens", { ...config, method: "GET" });
30596
+ return this.request("/config/swap/tokens", { ...config, method: "GET" }, TokensByChainMapSchema);
29404
30597
  }
29405
30598
  /**
29406
30599
  * Fetch the list of tokens available for swapping on a specific spoke chain.
@@ -29412,10 +30605,7 @@ var BackendApiService = class {
29412
30605
  * array of `XToken` definitions supported for swapping on that chain.
29413
30606
  */
29414
30607
  async getSwapTokensByChainId(chainId, config) {
29415
- return this.request(`/config/swap/${chainId}/tokens`, {
29416
- ...config,
29417
- method: "GET"
29418
- });
30608
+ return this.request(`/config/swap/${chainId}/tokens`, { ...config, method: "GET" }, TokensListSchema);
29419
30609
  }
29420
30610
  /**
29421
30611
  * Fetch the full map of tokens available in the money market (lending/borrowing),
@@ -29427,10 +30617,7 @@ var BackendApiService = class {
29427
30617
  * each supported spoke chain key to its list of money-market `XToken` definitions.
29428
30618
  */
29429
30619
  async getMoneyMarketTokens(config) {
29430
- return this.request("/config/money-market/tokens", {
29431
- ...config,
29432
- method: "GET"
29433
- });
30620
+ return this.request("/config/money-market/tokens", { ...config, method: "GET" }, TokensByChainMapSchema);
29434
30621
  }
29435
30622
  /**
29436
30623
  * Fetch the list of hub-chain reserve asset addresses registered in the
@@ -29444,10 +30631,11 @@ var BackendApiService = class {
29444
30631
  * readonly array of reserve `Address` strings.
29445
30632
  */
29446
30633
  async getMoneyMarketReserveAssets(config) {
29447
- return this.request("/config/money-market/reserve-assets", {
29448
- ...config,
29449
- method: "GET"
29450
- });
30634
+ return this.request(
30635
+ "/config/money-market/reserve-assets",
30636
+ { ...config, method: "GET" },
30637
+ ReserveAssetsSchema
30638
+ );
29451
30639
  }
29452
30640
  /**
29453
30641
  * Fetch the list of tokens available for lending/borrowing on a specific
@@ -29460,10 +30648,11 @@ var BackendApiService = class {
29460
30648
  * readonly array of `XToken` definitions supported in the money market on that chain.
29461
30649
  */
29462
30650
  async getMoneyMarketTokensByChainId(chainId, config) {
29463
- return this.request(`/config/money-market/${chainId}/tokens`, {
29464
- ...config,
29465
- method: "GET"
29466
- });
30651
+ return this.request(
30652
+ `/config/money-market/${chainId}/tokens`,
30653
+ { ...config, method: "GET" },
30654
+ TokensListSchema
30655
+ );
29467
30656
  }
29468
30657
  /**
29469
30658
  * Fetch the mapping from spoke chain keys to the numeric chain IDs used by
@@ -29477,7 +30666,7 @@ var BackendApiService = class {
29477
30666
  * `IntentRelayChainIdMap` record mapping each spoke chain key to its relay chain ID.
29478
30667
  */
29479
30668
  async getRelayChainIdMap(config) {
29480
- return this.request("/config/relay/chain-id-map", {
30669
+ return this.requestUnvalidated("/config/relay/chain-id-map", {
29481
30670
  ...config,
29482
30671
  method: "GET"
29483
30672
  });
@@ -29494,7 +30683,7 @@ var BackendApiService = class {
29494
30683
  * `SpokeChainConfigMap` for all currently enabled spoke chains.
29495
30684
  */
29496
30685
  async getSpokeChainConfig(config) {
29497
- return this.request("/config/spoke/all-chains-configs", {
30686
+ return this.requestUnvalidated("/config/spoke/all-chains-configs", {
29498
30687
  ...config,
29499
30688
  method: "GET"
29500
30689
  });
@@ -29506,12 +30695,17 @@ var BackendApiService = class {
29506
30695
  * without constructing a new service instance. Existing header keys are
29507
30696
  * overwritten; keys absent from `headers` are preserved.
29508
30697
  *
30698
+ * The headers are also fanned out to the sub-services (`swaps`), which hold
30699
+ * their own header copies — so a token set here applies to every request made
30700
+ * through this client, including `swaps.*`.
30701
+ *
29509
30702
  * @param headers - Key-value pairs to add or overwrite in the default headers.
29510
30703
  */
29511
30704
  setHeaders(headers) {
29512
30705
  Object.entries(headers).forEach(([key, value]) => {
29513
30706
  this.headers[key] = value;
29514
30707
  });
30708
+ this.swaps.setHeaders(headers);
29515
30709
  }
29516
30710
  /**
29517
30711
  * Return the base URL the service is currently pointing at.
@@ -29759,49 +30953,71 @@ var BridgeService = class {
29759
30953
  * }
29760
30954
  */
29761
30955
  async bridge(_params) {
29762
- const { params, timeout } = _params;
29763
- const baseCtx = { srcChainKey: params.srcChainKey, dstChainKey: params.dstChainKey };
29764
- try {
29765
- const txResult = await this.createBridgeIntent(_params);
29766
- if (!txResult.ok) return { ok: false, error: txResult.error };
29767
- const verifyTxHashResult = await this.spoke.verifyTxHash({
29768
- txHash: txResult.value.tx,
29769
- chainKey: params.srcChainKey
29770
- });
29771
- if (!verifyTxHashResult.ok) {
29772
- return {
29773
- ok: false,
29774
- error: verifyFailed("bridge", verifyTxHashResult.error, baseCtx)
29775
- };
30956
+ return this.config.analytics.trackResult(
30957
+ "bridge",
30958
+ "bridge",
30959
+ async () => {
30960
+ const { params, timeout } = _params;
30961
+ const baseCtx = { srcChainKey: params.srcChainKey, dstChainKey: params.dstChainKey };
30962
+ try {
30963
+ const txResult = await this.createBridgeIntent(_params);
30964
+ if (!txResult.ok) return { ok: false, error: txResult.error };
30965
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
30966
+ txHash: txResult.value.tx,
30967
+ chainKey: params.srcChainKey
30968
+ });
30969
+ if (!verifyTxHashResult.ok) {
30970
+ return {
30971
+ ok: false,
30972
+ error: verifyFailed("bridge", verifyTxHashResult.error, baseCtx)
30973
+ };
30974
+ }
30975
+ const packetResult = await relayTxAndWaitPacket({
30976
+ srcTxHash: txResult.value.tx,
30977
+ data: txResult.value.relayData,
30978
+ chainKey: params.srcChainKey,
30979
+ relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
30980
+ timeout
30981
+ });
30982
+ if (!packetResult.ok)
30983
+ return {
30984
+ ok: false,
30985
+ error: mapRelayFailure(packetResult.error, {
30986
+ feature: "bridge",
30987
+ action: "bridge",
30988
+ srcChainKey: baseCtx.srcChainKey,
30989
+ dstChainKey: baseCtx.dstChainKey
30990
+ })
30991
+ };
30992
+ return {
30993
+ ok: true,
30994
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packetResult.value.dst_tx_hash }
30995
+ };
30996
+ } catch (error) {
30997
+ if (isBridgeOrchestrationError(error)) return { ok: false, error };
30998
+ return {
30999
+ ok: false,
31000
+ error: executionFailed("bridge", error, baseCtx)
31001
+ };
31002
+ }
31003
+ },
31004
+ {
31005
+ start: () => ({
31006
+ srcChainKey: _params.params.srcChainKey,
31007
+ dstChainKey: _params.params.dstChainKey,
31008
+ srcToken: _params.params.srcToken,
31009
+ dstToken: _params.params.dstToken,
31010
+ amount: _params.params.amount,
31011
+ srcAddress: _params.params.srcAddress,
31012
+ recipient: _params.params.recipient
31013
+ }),
31014
+ success: (value) => ({
31015
+ srcChainTxHash: value.srcChainTxHash,
31016
+ dstChainTxHash: value.dstChainTxHash
31017
+ }),
31018
+ failure: (error) => ({ code: error.code })
29776
31019
  }
29777
- const packetResult = await relayTxAndWaitPacket({
29778
- srcTxHash: txResult.value.tx,
29779
- data: txResult.value.relayData,
29780
- chainKey: params.srcChainKey,
29781
- relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
29782
- timeout
29783
- });
29784
- if (!packetResult.ok)
29785
- return {
29786
- ok: false,
29787
- error: mapRelayFailure(packetResult.error, {
29788
- feature: "bridge",
29789
- action: "bridge",
29790
- srcChainKey: baseCtx.srcChainKey,
29791
- dstChainKey: baseCtx.dstChainKey
29792
- })
29793
- };
29794
- return {
29795
- ok: true,
29796
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packetResult.value.dst_tx_hash }
29797
- };
29798
- } catch (error) {
29799
- if (isBridgeOrchestrationError(error)) return { ok: false, error };
29800
- return {
29801
- ok: false,
29802
- error: executionFailed("bridge", error, baseCtx)
29803
- };
29804
- }
31020
+ );
29805
31021
  }
29806
31022
  /**
29807
31023
  * Submits the spoke-side deposit transaction that initiates a bridge transfer,
@@ -29830,8 +31046,8 @@ var BridgeService = class {
29830
31046
  const baseCtx = { srcChainKey: params.srcChainKey, dstChainKey: params.dstChainKey };
29831
31047
  try {
29832
31048
  bridgeInvariant(params.amount > 0n, "Amount must be greater than 0", { ...baseCtx, field: "amount" });
29833
- const srcToken = this.config.getSpokeTokenFromOriginalAssetAddress(params.srcChainKey, params.srcToken);
29834
- const dstToken = this.config.getSpokeTokenFromOriginalAssetAddress(params.dstChainKey, params.dstToken);
31049
+ const srcToken = this.resolveBridgeEndpointToken(params.srcChainKey, params.srcToken);
31050
+ const dstToken = this.resolveBridgeEndpointToken(params.dstChainKey, params.dstToken);
29835
31051
  bridgeInvariant(srcToken, `Unsupported spoke chain (${params.srcChainKey}) token: ${params.srcToken}`, {
29836
31052
  ...baseCtx,
29837
31053
  field: "srcToken"
@@ -29899,6 +31115,20 @@ var BridgeService = class {
29899
31115
  };
29900
31116
  }
29901
31117
  }
31118
+ /**
31119
+ * Resolves a bridge endpoint's {@link XToken} descriptor from a chain key + token address.
31120
+ *
31121
+ * Spoke endpoints (and hub-native tokens such as USDC/WETH/S whose on-chain address equals their
31122
+ * hub asset) resolve by original asset address. On the hub a caller may instead hold a hub asset
31123
+ * that has no spoke-token entry under the hub chain — e.g. a partner BTC fee held as the BTC hub
31124
+ * asset, whose only spoke-token entry lives on Bitcoin. Resolve those by hub-asset address so the
31125
+ * Sonic-sourced "withdraw directly" bridge can find the matching vault/decimals.
31126
+ */
31127
+ resolveBridgeEndpointToken(chainKey, token) {
31128
+ const spokeToken = this.config.getSpokeTokenFromOriginalAssetAddress(chainKey, token);
31129
+ if (spokeToken) return spokeToken;
31130
+ return isHubChainKeyType(chainKey) ? this.config.getXTokenFromHubAsset(token) : void 0;
31131
+ }
29902
31132
  /**
29903
31133
  * Encodes the hub-side execution payload for a bridge operation.
29904
31134
  *
@@ -30781,52 +32011,71 @@ var StakingService = class {
30781
32011
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
30782
32012
  */
30783
32013
  async stake(_params) {
30784
- const { params, timeout } = _params;
30785
- const baseCtx = { srcChainKey: params.srcChainKey, action: "stake" };
30786
- try {
30787
- const txResult = await this.createStakeIntent(_params);
30788
- if (!txResult.ok) return { ok: false, error: txResult.error };
30789
- const verifyTxHashResult = await this.spoke.verifyTxHash({
30790
- txHash: txResult.value.tx,
30791
- chainKey: params.srcChainKey
30792
- });
30793
- if (!verifyTxHashResult.ok) {
30794
- return {
30795
- ok: false,
30796
- error: verifyFailed("staking", verifyTxHashResult.error, baseCtx)
30797
- };
30798
- }
30799
- let hubTxHash;
30800
- if (!isHubChainKeyType(params.srcChainKey)) {
30801
- const packetResult = await relayTxAndWaitPacket({
30802
- srcTxHash: txResult.value.tx,
30803
- data: txResult.value.relayData,
30804
- chainKey: params.srcChainKey,
30805
- relayerApiEndpoint: this.relayerApiEndpoint,
30806
- timeout
30807
- });
30808
- if (!packetResult.ok) {
32014
+ return this.config.analytics.trackResult(
32015
+ "staking",
32016
+ "stake",
32017
+ async () => {
32018
+ const { params, timeout } = _params;
32019
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "stake" };
32020
+ try {
32021
+ const txResult = await this.createStakeIntent(_params);
32022
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32023
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
32024
+ txHash: txResult.value.tx,
32025
+ chainKey: params.srcChainKey
32026
+ });
32027
+ if (!verifyTxHashResult.ok) {
32028
+ return {
32029
+ ok: false,
32030
+ error: verifyFailed("staking", verifyTxHashResult.error, baseCtx)
32031
+ };
32032
+ }
32033
+ let hubTxHash;
32034
+ if (!isHubChainKeyType(params.srcChainKey)) {
32035
+ const packetResult = await relayTxAndWaitPacket({
32036
+ srcTxHash: txResult.value.tx,
32037
+ data: txResult.value.relayData,
32038
+ chainKey: params.srcChainKey,
32039
+ relayerApiEndpoint: this.relayerApiEndpoint,
32040
+ timeout
32041
+ });
32042
+ if (!packetResult.ok) {
32043
+ return {
32044
+ ok: false,
32045
+ error: mapRelayFailure(packetResult.error, {
32046
+ feature: "staking",
32047
+ action: baseCtx.action,
32048
+ srcChainKey: baseCtx.srcChainKey
32049
+ })
32050
+ };
32051
+ }
32052
+ hubTxHash = packetResult.value.dst_tx_hash;
32053
+ } else {
32054
+ hubTxHash = txResult.value.tx;
32055
+ }
32056
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32057
+ } catch (error) {
32058
+ if (isStakeOrchestrationError(error)) return { ok: false, error };
30809
32059
  return {
30810
32060
  ok: false,
30811
- error: mapRelayFailure(packetResult.error, {
30812
- feature: "staking",
30813
- action: baseCtx.action,
30814
- srcChainKey: baseCtx.srcChainKey
30815
- })
32061
+ error: executionFailed("staking", error, baseCtx)
30816
32062
  };
30817
32063
  }
30818
- hubTxHash = packetResult.value.dst_tx_hash;
30819
- } else {
30820
- hubTxHash = txResult.value.tx;
32064
+ },
32065
+ {
32066
+ start: () => ({
32067
+ srcChainKey: _params.params.srcChainKey,
32068
+ srcAddress: _params.params.srcAddress,
32069
+ amount: _params.params.amount,
32070
+ minReceive: _params.params.minReceive
32071
+ }),
32072
+ success: (value) => ({
32073
+ srcChainTxHash: value.srcChainTxHash,
32074
+ dstChainTxHash: value.dstChainTxHash
32075
+ }),
32076
+ failure: (error) => ({ code: error.code })
30821
32077
  }
30822
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
30823
- } catch (error) {
30824
- if (isStakeOrchestrationError(error)) return { ok: false, error };
30825
- return {
30826
- ok: false,
30827
- error: executionFailed("staking", error, baseCtx)
30828
- };
30829
- }
32078
+ );
30830
32079
  }
30831
32080
  /**
30832
32081
  * Submits the stake transaction on the spoke chain without relaying to the hub.
@@ -30931,42 +32180,60 @@ var StakingService = class {
30931
32180
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
30932
32181
  */
30933
32182
  async unstake(_params) {
30934
- const { params, timeout } = _params;
30935
- const baseCtx = { srcChainKey: params.srcChainKey, action: "unstake" };
30936
- try {
30937
- const txResult = await this.createUnstakeIntent(_params);
30938
- if (!txResult.ok) return { ok: false, error: txResult.error };
30939
- let hubTxHash;
30940
- if (!isHubChainKeyType(params.srcChainKey)) {
30941
- const packetResult = await relayTxAndWaitPacket({
30942
- srcTxHash: txResult.value.tx,
30943
- data: txResult.value.relayData,
30944
- chainKey: params.srcChainKey,
30945
- relayerApiEndpoint: this.relayerApiEndpoint,
30946
- timeout
30947
- });
30948
- if (!packetResult.ok) {
32183
+ return this.config.analytics.trackResult(
32184
+ "staking",
32185
+ "unstake",
32186
+ async () => {
32187
+ const { params, timeout } = _params;
32188
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "unstake" };
32189
+ try {
32190
+ const txResult = await this.createUnstakeIntent(_params);
32191
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32192
+ let hubTxHash;
32193
+ if (!isHubChainKeyType(params.srcChainKey)) {
32194
+ const packetResult = await relayTxAndWaitPacket({
32195
+ srcTxHash: txResult.value.tx,
32196
+ data: txResult.value.relayData,
32197
+ chainKey: params.srcChainKey,
32198
+ relayerApiEndpoint: this.relayerApiEndpoint,
32199
+ timeout
32200
+ });
32201
+ if (!packetResult.ok) {
32202
+ return {
32203
+ ok: false,
32204
+ error: mapRelayFailure(packetResult.error, {
32205
+ feature: "staking",
32206
+ action: baseCtx.action,
32207
+ srcChainKey: baseCtx.srcChainKey
32208
+ })
32209
+ };
32210
+ }
32211
+ hubTxHash = packetResult.value.dst_tx_hash;
32212
+ } else {
32213
+ hubTxHash = txResult.value.tx;
32214
+ }
32215
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32216
+ } catch (error) {
32217
+ if (isStakingOrchestrationError(error)) return { ok: false, error };
30949
32218
  return {
30950
32219
  ok: false,
30951
- error: mapRelayFailure(packetResult.error, {
30952
- feature: "staking",
30953
- action: baseCtx.action,
30954
- srcChainKey: baseCtx.srcChainKey
30955
- })
32220
+ error: executionFailed("staking", error, baseCtx)
30956
32221
  };
30957
32222
  }
30958
- hubTxHash = packetResult.value.dst_tx_hash;
30959
- } else {
30960
- hubTxHash = txResult.value.tx;
32223
+ },
32224
+ {
32225
+ start: () => ({
32226
+ srcChainKey: _params.params.srcChainKey,
32227
+ srcAddress: _params.params.srcAddress,
32228
+ amount: _params.params.amount
32229
+ }),
32230
+ success: (value) => ({
32231
+ srcChainTxHash: value.srcChainTxHash,
32232
+ dstChainTxHash: value.dstChainTxHash
32233
+ }),
32234
+ failure: (error) => ({ code: error.code })
30961
32235
  }
30962
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
30963
- } catch (error) {
30964
- if (isStakingOrchestrationError(error)) return { ok: false, error };
30965
- return {
30966
- ok: false,
30967
- error: executionFailed("staking", error, baseCtx)
30968
- };
30969
- }
32236
+ );
30970
32237
  }
30971
32238
  /**
30972
32239
  * Submits the unstake transaction on the spoke chain without relaying to the hub.
@@ -31062,42 +32329,61 @@ var StakingService = class {
31062
32329
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
31063
32330
  */
31064
32331
  async instantUnstake(_params) {
31065
- const { params, timeout } = _params;
31066
- const baseCtx = { srcChainKey: params.srcChainKey, action: "instantUnstake" };
31067
- try {
31068
- const txResult = await this.createInstantUnstakeIntent(_params);
31069
- if (!txResult.ok) return { ok: false, error: txResult.error };
31070
- let hubTxHash;
31071
- if (!isHubChainKeyType(params.srcChainKey)) {
31072
- const packetResult = await relayTxAndWaitPacket({
31073
- srcTxHash: txResult.value.tx,
31074
- data: txResult.value.relayData,
31075
- chainKey: params.srcChainKey,
31076
- relayerApiEndpoint: this.relayerApiEndpoint,
31077
- timeout
31078
- });
31079
- if (!packetResult.ok) {
32332
+ return this.config.analytics.trackResult(
32333
+ "staking",
32334
+ "instantUnstake",
32335
+ async () => {
32336
+ const { params, timeout } = _params;
32337
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "instantUnstake" };
32338
+ try {
32339
+ const txResult = await this.createInstantUnstakeIntent(_params);
32340
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32341
+ let hubTxHash;
32342
+ if (!isHubChainKeyType(params.srcChainKey)) {
32343
+ const packetResult = await relayTxAndWaitPacket({
32344
+ srcTxHash: txResult.value.tx,
32345
+ data: txResult.value.relayData,
32346
+ chainKey: params.srcChainKey,
32347
+ relayerApiEndpoint: this.relayerApiEndpoint,
32348
+ timeout
32349
+ });
32350
+ if (!packetResult.ok) {
32351
+ return {
32352
+ ok: false,
32353
+ error: mapRelayFailure(packetResult.error, {
32354
+ feature: "staking",
32355
+ action: baseCtx.action,
32356
+ srcChainKey: baseCtx.srcChainKey
32357
+ })
32358
+ };
32359
+ }
32360
+ hubTxHash = packetResult.value.dst_tx_hash;
32361
+ } else {
32362
+ hubTxHash = txResult.value.tx;
32363
+ }
32364
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32365
+ } catch (error) {
32366
+ if (isStakingOrchestrationError(error)) return { ok: false, error };
31080
32367
  return {
31081
32368
  ok: false,
31082
- error: mapRelayFailure(packetResult.error, {
31083
- feature: "staking",
31084
- action: baseCtx.action,
31085
- srcChainKey: baseCtx.srcChainKey
31086
- })
32369
+ error: executionFailed("staking", error, baseCtx)
31087
32370
  };
31088
32371
  }
31089
- hubTxHash = packetResult.value.dst_tx_hash;
31090
- } else {
31091
- hubTxHash = txResult.value.tx;
32372
+ },
32373
+ {
32374
+ start: () => ({
32375
+ srcChainKey: _params.params.srcChainKey,
32376
+ srcAddress: _params.params.srcAddress,
32377
+ amount: _params.params.amount,
32378
+ minAmount: _params.params.minAmount
32379
+ }),
32380
+ success: (value) => ({
32381
+ srcChainTxHash: value.srcChainTxHash,
32382
+ dstChainTxHash: value.dstChainTxHash
32383
+ }),
32384
+ failure: (error) => ({ code: error.code })
31092
32385
  }
31093
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
31094
- } catch (error) {
31095
- if (isStakingOrchestrationError(error)) return { ok: false, error };
31096
- return {
31097
- ok: false,
31098
- error: executionFailed("staking", error, baseCtx)
31099
- };
31100
- }
32386
+ );
31101
32387
  }
31102
32388
  /**
31103
32389
  * Submits the instant-unstake transaction on the spoke chain without relaying to the hub.
@@ -31205,42 +32491,61 @@ var StakingService = class {
31205
32491
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
31206
32492
  */
31207
32493
  async claim(_params) {
31208
- const { params, timeout } = _params;
31209
- const baseCtx = { srcChainKey: params.srcChainKey, action: "claim" };
31210
- try {
31211
- const txResult = await this.createClaimIntent(_params);
31212
- if (!txResult.ok) return { ok: false, error: txResult.error };
31213
- let hubTxHash;
31214
- if (!isHubChainKeyType(params.srcChainKey)) {
31215
- const packetResult = await relayTxAndWaitPacket({
31216
- srcTxHash: txResult.value.tx,
31217
- data: txResult.value.relayData,
31218
- chainKey: params.srcChainKey,
31219
- relayerApiEndpoint: this.relayerApiEndpoint,
31220
- timeout
31221
- });
31222
- if (!packetResult.ok) {
32494
+ return this.config.analytics.trackResult(
32495
+ "staking",
32496
+ "claim",
32497
+ async () => {
32498
+ const { params, timeout } = _params;
32499
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "claim" };
32500
+ try {
32501
+ const txResult = await this.createClaimIntent(_params);
32502
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32503
+ let hubTxHash;
32504
+ if (!isHubChainKeyType(params.srcChainKey)) {
32505
+ const packetResult = await relayTxAndWaitPacket({
32506
+ srcTxHash: txResult.value.tx,
32507
+ data: txResult.value.relayData,
32508
+ chainKey: params.srcChainKey,
32509
+ relayerApiEndpoint: this.relayerApiEndpoint,
32510
+ timeout
32511
+ });
32512
+ if (!packetResult.ok) {
32513
+ return {
32514
+ ok: false,
32515
+ error: mapRelayFailure(packetResult.error, {
32516
+ feature: "staking",
32517
+ action: baseCtx.action,
32518
+ srcChainKey: baseCtx.srcChainKey
32519
+ })
32520
+ };
32521
+ }
32522
+ hubTxHash = packetResult.value.dst_tx_hash;
32523
+ } else {
32524
+ hubTxHash = txResult.value.tx;
32525
+ }
32526
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32527
+ } catch (error) {
32528
+ if (isStakingOrchestrationError(error)) return { ok: false, error };
31223
32529
  return {
31224
32530
  ok: false,
31225
- error: mapRelayFailure(packetResult.error, {
31226
- feature: "staking",
31227
- action: baseCtx.action,
31228
- srcChainKey: baseCtx.srcChainKey
31229
- })
32531
+ error: executionFailed("staking", error, baseCtx)
31230
32532
  };
31231
32533
  }
31232
- hubTxHash = packetResult.value.dst_tx_hash;
31233
- } else {
31234
- hubTxHash = txResult.value.tx;
32534
+ },
32535
+ {
32536
+ start: () => ({
32537
+ srcChainKey: _params.params.srcChainKey,
32538
+ srcAddress: _params.params.srcAddress,
32539
+ requestId: _params.params.requestId,
32540
+ amount: _params.params.amount
32541
+ }),
32542
+ success: (value) => ({
32543
+ srcChainTxHash: value.srcChainTxHash,
32544
+ dstChainTxHash: value.dstChainTxHash
32545
+ }),
32546
+ failure: (error) => ({ code: error.code })
31235
32547
  }
31236
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
31237
- } catch (error) {
31238
- if (isStakingOrchestrationError(error)) return { ok: false, error };
31239
- return {
31240
- ok: false,
31241
- error: executionFailed("staking", error, baseCtx)
31242
- };
31243
- }
32548
+ );
31244
32549
  }
31245
32550
  /**
31246
32551
  * Submits the claim transaction on the spoke chain without relaying to the hub.
@@ -31354,42 +32659,60 @@ var StakingService = class {
31354
32659
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
31355
32660
  */
31356
32661
  async cancelUnstake(_params) {
31357
- const { params, timeout } = _params;
31358
- const baseCtx = { srcChainKey: params.srcChainKey, action: "cancelUnstake" };
31359
- try {
31360
- const txResult = await this.createCancelUnstakeIntent(_params);
31361
- if (!txResult.ok) return { ok: false, error: txResult.error };
31362
- let hubTxHash;
31363
- if (!isHubChainKeyType(params.srcChainKey)) {
31364
- const packetResult = await relayTxAndWaitPacket({
31365
- srcTxHash: txResult.value.tx,
31366
- data: txResult.value.relayData,
31367
- chainKey: params.srcChainKey,
31368
- relayerApiEndpoint: this.relayerApiEndpoint,
31369
- timeout
31370
- });
31371
- if (!packetResult.ok) {
32662
+ return this.config.analytics.trackResult(
32663
+ "staking",
32664
+ "cancelUnstake",
32665
+ async () => {
32666
+ const { params, timeout } = _params;
32667
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "cancelUnstake" };
32668
+ try {
32669
+ const txResult = await this.createCancelUnstakeIntent(_params);
32670
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32671
+ let hubTxHash;
32672
+ if (!isHubChainKeyType(params.srcChainKey)) {
32673
+ const packetResult = await relayTxAndWaitPacket({
32674
+ srcTxHash: txResult.value.tx,
32675
+ data: txResult.value.relayData,
32676
+ chainKey: params.srcChainKey,
32677
+ relayerApiEndpoint: this.relayerApiEndpoint,
32678
+ timeout
32679
+ });
32680
+ if (!packetResult.ok) {
32681
+ return {
32682
+ ok: false,
32683
+ error: mapRelayFailure(packetResult.error, {
32684
+ feature: "staking",
32685
+ action: baseCtx.action,
32686
+ srcChainKey: baseCtx.srcChainKey
32687
+ })
32688
+ };
32689
+ }
32690
+ hubTxHash = packetResult.value.dst_tx_hash;
32691
+ } else {
32692
+ hubTxHash = txResult.value.tx;
32693
+ }
32694
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32695
+ } catch (error) {
32696
+ if (isStakingOrchestrationError(error)) return { ok: false, error };
31372
32697
  return {
31373
32698
  ok: false,
31374
- error: mapRelayFailure(packetResult.error, {
31375
- feature: "staking",
31376
- action: baseCtx.action,
31377
- srcChainKey: baseCtx.srcChainKey
31378
- })
32699
+ error: executionFailed("staking", error, baseCtx)
31379
32700
  };
31380
32701
  }
31381
- hubTxHash = packetResult.value.dst_tx_hash;
31382
- } else {
31383
- hubTxHash = txResult.value.tx;
32702
+ },
32703
+ {
32704
+ start: () => ({
32705
+ srcChainKey: _params.params.srcChainKey,
32706
+ srcAddress: _params.params.srcAddress,
32707
+ requestId: _params.params.requestId
32708
+ }),
32709
+ success: (value) => ({
32710
+ srcChainTxHash: value.srcChainTxHash,
32711
+ dstChainTxHash: value.dstChainTxHash
32712
+ }),
32713
+ failure: (error) => ({ code: error.code })
31384
32714
  }
31385
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
31386
- } catch (error) {
31387
- if (isStakingOrchestrationError(error)) return { ok: false, error };
31388
- return {
31389
- ok: false,
31390
- error: executionFailed("staking", error, baseCtx)
31391
- };
31392
- }
32715
+ );
31393
32716
  }
31394
32717
  /**
31395
32718
  * Submits the cancel-unstake transaction on the spoke chain without relaying to the hub.
@@ -32763,34 +34086,56 @@ var ClService = class _ClService {
32763
34086
  * `dstChainTxHash` (hub) once the packet has been confirmed.
32764
34087
  */
32765
34088
  async supplyLiquidity(_params) {
32766
- const { params, timeout } = _params;
32767
- try {
32768
- const txResult = await this.executeSupplyLiquidity(_params);
32769
- if (!txResult.ok) {
32770
- return txResult;
32771
- }
32772
- let hubTxHash;
32773
- if (!isHubChainKeyType(params.srcChainKey)) {
32774
- const packetResult = await relayTxAndWaitPacket({
32775
- srcTxHash: txResult.value.tx,
32776
- data: txResult.value.relayData,
32777
- chainKey: params.srcChainKey,
32778
- relayerApiEndpoint: this.relayerApiEndpoint,
32779
- timeout
32780
- });
32781
- if (!packetResult.ok) return packetResult;
32782
- hubTxHash = packetResult.value.dst_tx_hash;
32783
- } else {
32784
- hubTxHash = txResult.value.tx;
34089
+ return this.config.analytics.trackResult(
34090
+ "dex",
34091
+ "supplyLiquidity",
34092
+ async () => {
34093
+ const { params, timeout } = _params;
34094
+ try {
34095
+ const txResult = await this.executeSupplyLiquidity(_params);
34096
+ if (!txResult.ok) {
34097
+ return txResult;
34098
+ }
34099
+ let hubTxHash;
34100
+ if (!isHubChainKeyType(params.srcChainKey)) {
34101
+ const packetResult = await relayTxAndWaitPacket({
34102
+ srcTxHash: txResult.value.tx,
34103
+ data: txResult.value.relayData,
34104
+ chainKey: params.srcChainKey,
34105
+ relayerApiEndpoint: this.relayerApiEndpoint,
34106
+ timeout
34107
+ });
34108
+ if (!packetResult.ok) return packetResult;
34109
+ hubTxHash = packetResult.value.dst_tx_hash;
34110
+ } else {
34111
+ hubTxHash = txResult.value.tx;
34112
+ }
34113
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
34114
+ } catch (error) {
34115
+ this.config.logger.error("supplyLiquidity error", error);
34116
+ return {
34117
+ ok: false,
34118
+ error
34119
+ };
34120
+ }
34121
+ },
34122
+ {
34123
+ start: () => ({
34124
+ srcChainKey: _params.params.srcChainKey,
34125
+ srcAddress: _params.params.srcAddress,
34126
+ currency0: _params.params.poolKey.currency0,
34127
+ currency1: _params.params.poolKey.currency1,
34128
+ fee: _params.params.poolKey.fee,
34129
+ tickLower: _params.params.tickLower,
34130
+ tickUpper: _params.params.tickUpper,
34131
+ liquidity: _params.params.liquidity,
34132
+ amount0Max: _params.params.amount0Max,
34133
+ amount1Max: _params.params.amount1Max
34134
+ }),
34135
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
34136
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
32785
34137
  }
32786
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32787
- } catch (error) {
32788
- this.config.logger.error("supplyLiquidity error", error);
32789
- return {
32790
- ok: false,
32791
- error
32792
- };
32793
- }
34138
+ );
32794
34139
  }
32795
34140
  /**
32796
34141
  * Add liquidity to an existing position and wait for the cross-chain relay to complete.
@@ -32805,33 +34150,56 @@ var ClService = class _ClService {
32805
34150
  * `dstChainTxHash` (hub) once the packet has been confirmed.
32806
34151
  */
32807
34152
  async increaseLiquidity(_params) {
32808
- const { params, timeout } = _params;
32809
- try {
32810
- const txResult = await this.executeIncreaseLiquidity(_params);
32811
- if (!txResult.ok) {
32812
- return txResult;
32813
- }
32814
- let hubTxHash;
32815
- if (!isHubChainKeyType(params.srcChainKey)) {
32816
- const packetResult = await relayTxAndWaitPacket({
32817
- srcTxHash: txResult.value.tx,
32818
- data: txResult.value.relayData,
32819
- chainKey: params.srcChainKey,
32820
- relayerApiEndpoint: this.relayerApiEndpoint,
32821
- timeout
32822
- });
32823
- if (!packetResult.ok) return packetResult;
32824
- hubTxHash = packetResult.value.dst_tx_hash;
32825
- } else {
32826
- hubTxHash = txResult.value.tx;
34153
+ return this.config.analytics.trackResult(
34154
+ "dex",
34155
+ "increaseLiquidity",
34156
+ async () => {
34157
+ const { params, timeout } = _params;
34158
+ try {
34159
+ const txResult = await this.executeIncreaseLiquidity(_params);
34160
+ if (!txResult.ok) {
34161
+ return txResult;
34162
+ }
34163
+ let hubTxHash;
34164
+ if (!isHubChainKeyType(params.srcChainKey)) {
34165
+ const packetResult = await relayTxAndWaitPacket({
34166
+ srcTxHash: txResult.value.tx,
34167
+ data: txResult.value.relayData,
34168
+ chainKey: params.srcChainKey,
34169
+ relayerApiEndpoint: this.relayerApiEndpoint,
34170
+ timeout
34171
+ });
34172
+ if (!packetResult.ok) return packetResult;
34173
+ hubTxHash = packetResult.value.dst_tx_hash;
34174
+ } else {
34175
+ hubTxHash = txResult.value.tx;
34176
+ }
34177
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
34178
+ } catch (error) {
34179
+ return {
34180
+ ok: false,
34181
+ error
34182
+ };
34183
+ }
34184
+ },
34185
+ {
34186
+ start: () => ({
34187
+ srcChainKey: _params.params.srcChainKey,
34188
+ srcAddress: _params.params.srcAddress,
34189
+ currency0: _params.params.poolKey.currency0,
34190
+ currency1: _params.params.poolKey.currency1,
34191
+ fee: _params.params.poolKey.fee,
34192
+ tokenId: _params.params.tokenId,
34193
+ tickLower: _params.params.tickLower,
34194
+ tickUpper: _params.params.tickUpper,
34195
+ liquidity: _params.params.liquidity,
34196
+ amount0Max: _params.params.amount0Max,
34197
+ amount1Max: _params.params.amount1Max
34198
+ }),
34199
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
34200
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
32827
34201
  }
32828
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32829
- } catch (error) {
32830
- return {
32831
- ok: false,
32832
- error
32833
- };
32834
- }
34202
+ );
32835
34203
  }
32836
34204
  /**
32837
34205
  * Remove liquidity from an existing position and wait for the cross-chain relay to complete.
@@ -32846,33 +34214,54 @@ var ClService = class _ClService {
32846
34214
  * `dstChainTxHash` (hub) once the packet has been confirmed.
32847
34215
  */
32848
34216
  async decreaseLiquidity(_params) {
32849
- const { params, timeout } = _params;
32850
- try {
32851
- const txResult = await this.executeDecreaseLiquidity(_params);
32852
- if (!txResult.ok) {
32853
- return txResult;
32854
- }
32855
- let hubTxHash;
32856
- if (!isHubChainKeyType(params.srcChainKey)) {
32857
- const packetResult = await relayTxAndWaitPacket({
32858
- srcTxHash: txResult.value.tx,
32859
- data: txResult.value.relayData,
32860
- chainKey: params.srcChainKey,
32861
- relayerApiEndpoint: this.relayerApiEndpoint,
32862
- timeout
32863
- });
32864
- if (!packetResult.ok) return packetResult;
32865
- hubTxHash = packetResult.value.dst_tx_hash;
32866
- } else {
32867
- hubTxHash = txResult.value.tx;
34217
+ return this.config.analytics.trackResult(
34218
+ "dex",
34219
+ "decreaseLiquidity",
34220
+ async () => {
34221
+ const { params, timeout } = _params;
34222
+ try {
34223
+ const txResult = await this.executeDecreaseLiquidity(_params);
34224
+ if (!txResult.ok) {
34225
+ return txResult;
34226
+ }
34227
+ let hubTxHash;
34228
+ if (!isHubChainKeyType(params.srcChainKey)) {
34229
+ const packetResult = await relayTxAndWaitPacket({
34230
+ srcTxHash: txResult.value.tx,
34231
+ data: txResult.value.relayData,
34232
+ chainKey: params.srcChainKey,
34233
+ relayerApiEndpoint: this.relayerApiEndpoint,
34234
+ timeout
34235
+ });
34236
+ if (!packetResult.ok) return packetResult;
34237
+ hubTxHash = packetResult.value.dst_tx_hash;
34238
+ } else {
34239
+ hubTxHash = txResult.value.tx;
34240
+ }
34241
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
34242
+ } catch (error) {
34243
+ return {
34244
+ ok: false,
34245
+ error
34246
+ };
34247
+ }
34248
+ },
34249
+ {
34250
+ start: () => ({
34251
+ srcChainKey: _params.params.srcChainKey,
34252
+ srcAddress: _params.params.srcAddress,
34253
+ currency0: _params.params.poolKey.currency0,
34254
+ currency1: _params.params.poolKey.currency1,
34255
+ fee: _params.params.poolKey.fee,
34256
+ tokenId: _params.params.tokenId,
34257
+ liquidity: _params.params.liquidity,
34258
+ amount0Min: _params.params.amount0Min,
34259
+ amount1Min: _params.params.amount1Min
34260
+ }),
34261
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
34262
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
32868
34263
  }
32869
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32870
- } catch (error) {
32871
- return {
32872
- ok: false,
32873
- error
32874
- };
32875
- }
34264
+ );
32876
34265
  }
32877
34266
  /**
32878
34267
  * Fetch the reward configuration stored in the pool's hook contract.
@@ -33024,34 +34413,54 @@ var ClService = class _ClService {
33024
34413
  * `dstChainTxHash` (hub) once the packet has been confirmed.
33025
34414
  */
33026
34415
  async claimRewards(_params) {
33027
- const { params, timeout } = _params;
33028
- try {
33029
- const txResult = await this.executeClaimRewards(_params);
33030
- if (!txResult.ok) {
33031
- return txResult;
33032
- }
33033
- let hubTxHash;
33034
- if (!isHubChainKeyType(params.srcChainKey)) {
33035
- const packetResult = await relayTxAndWaitPacket({
33036
- srcTxHash: txResult.value.tx,
33037
- data: txResult.value.relayData,
33038
- chainKey: params.srcChainKey,
33039
- relayerApiEndpoint: this.relayerApiEndpoint,
33040
- timeout
33041
- });
33042
- if (!packetResult.ok) return packetResult;
33043
- hubTxHash = packetResult.value.dst_tx_hash;
33044
- } else {
33045
- hubTxHash = txResult.value.tx;
34416
+ return this.config.analytics.trackResult(
34417
+ "dex",
34418
+ "claimRewards",
34419
+ async () => {
34420
+ const { params, timeout } = _params;
34421
+ try {
34422
+ const txResult = await this.executeClaimRewards(_params);
34423
+ if (!txResult.ok) {
34424
+ return txResult;
34425
+ }
34426
+ let hubTxHash;
34427
+ if (!isHubChainKeyType(params.srcChainKey)) {
34428
+ const packetResult = await relayTxAndWaitPacket({
34429
+ srcTxHash: txResult.value.tx,
34430
+ data: txResult.value.relayData,
34431
+ chainKey: params.srcChainKey,
34432
+ relayerApiEndpoint: this.relayerApiEndpoint,
34433
+ timeout
34434
+ });
34435
+ if (!packetResult.ok) return packetResult;
34436
+ hubTxHash = packetResult.value.dst_tx_hash;
34437
+ } else {
34438
+ hubTxHash = txResult.value.tx;
34439
+ }
34440
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
34441
+ } catch (error) {
34442
+ this.config.logger.error("claimRewards error", error);
34443
+ return {
34444
+ ok: false,
34445
+ error
34446
+ };
34447
+ }
34448
+ },
34449
+ {
34450
+ start: () => ({
34451
+ srcChainKey: _params.params.srcChainKey,
34452
+ srcAddress: _params.params.srcAddress,
34453
+ currency0: _params.params.poolKey.currency0,
34454
+ currency1: _params.params.poolKey.currency1,
34455
+ fee: _params.params.poolKey.fee,
34456
+ tokenId: _params.params.tokenId,
34457
+ tickLower: _params.params.tickLower,
34458
+ tickUpper: _params.params.tickUpper
34459
+ }),
34460
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
34461
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
33046
34462
  }
33047
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
33048
- } catch (error) {
33049
- this.config.logger.error("claimRewards error", error);
33050
- return {
33051
- ok: false,
33052
- error
33053
- };
33054
- }
34463
+ );
33055
34464
  }
33056
34465
  /**
33057
34466
  * Return the list of configured concentrated-liquidity pool keys for the SODAX DEX.
@@ -35974,50 +37383,68 @@ var MoneyMarketService = class _MoneyMarketService {
35974
37383
  * @returns A pair of transaction hashes — `srcChainTxHash` (spoke) and `dstChainTxHash` (hub).
35975
37384
  */
35976
37385
  async supply(_params) {
35977
- const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
35978
- const srcChainKey = params.srcChainKey;
35979
- const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "supply" };
35980
- try {
35981
- const txResult = await this.createSupplyIntent(_params);
35982
- if (!txResult.ok) return { ok: false, error: txResult.error };
35983
- const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
35984
- if (!verify.ok) {
35985
- return {
35986
- ok: false,
35987
- error: verifyFailed("moneyMarket", verify.error, baseCtx)
35988
- };
35989
- }
35990
- if (isHubChainKeyType(srcChainKey)) {
35991
- return {
35992
- ok: true,
35993
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
35994
- };
37386
+ return this.config.analytics.trackResult(
37387
+ "moneyMarket",
37388
+ "supply",
37389
+ async () => {
37390
+ const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
37391
+ const srcChainKey = params.srcChainKey;
37392
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "supply" };
37393
+ try {
37394
+ const txResult = await this.createSupplyIntent(_params);
37395
+ if (!txResult.ok) return { ok: false, error: txResult.error };
37396
+ const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
37397
+ if (!verify.ok) {
37398
+ return {
37399
+ ok: false,
37400
+ error: verifyFailed("moneyMarket", verify.error, baseCtx)
37401
+ };
37402
+ }
37403
+ if (isHubChainKeyType(srcChainKey)) {
37404
+ return {
37405
+ ok: true,
37406
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
37407
+ };
37408
+ }
37409
+ const packet = await relayTxAndWaitPacket({
37410
+ srcTxHash: txResult.value.tx,
37411
+ data: txResult.value.relayData,
37412
+ chainKey: srcChainKey,
37413
+ relayerApiEndpoint: this.relayerApiEndpoint,
37414
+ timeout
37415
+ });
37416
+ if (!packet.ok)
37417
+ return {
37418
+ ok: false,
37419
+ error: mapRelayFailure(packet.error, {
37420
+ feature: "moneyMarket",
37421
+ action: baseCtx.action,
37422
+ srcChainKey: baseCtx.srcChainKey,
37423
+ dstChainKey: baseCtx.dstChainKey
37424
+ })
37425
+ };
37426
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
37427
+ } catch (error) {
37428
+ if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
37429
+ return {
37430
+ ok: false,
37431
+ error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
37432
+ };
37433
+ }
37434
+ },
37435
+ {
37436
+ start: () => ({
37437
+ srcChainKey: _params.params.srcChainKey,
37438
+ srcAddress: _params.params.srcAddress,
37439
+ dstChainKey: _params.params.dstChainKey,
37440
+ dstAddress: _params.params.dstAddress,
37441
+ token: _params.params.token,
37442
+ amount: _params.params.amount
37443
+ }),
37444
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
37445
+ failure: (error) => ({ code: error.code })
35995
37446
  }
35996
- const packet = await relayTxAndWaitPacket({
35997
- srcTxHash: txResult.value.tx,
35998
- data: txResult.value.relayData,
35999
- chainKey: srcChainKey,
36000
- relayerApiEndpoint: this.relayerApiEndpoint,
36001
- timeout
36002
- });
36003
- if (!packet.ok)
36004
- return {
36005
- ok: false,
36006
- error: mapRelayFailure(packet.error, {
36007
- feature: "moneyMarket",
36008
- action: baseCtx.action,
36009
- srcChainKey: baseCtx.srcChainKey,
36010
- dstChainKey: baseCtx.dstChainKey
36011
- })
36012
- };
36013
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
36014
- } catch (error) {
36015
- if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
36016
- return {
36017
- ok: false,
36018
- error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
36019
- };
36020
- }
37447
+ );
36021
37448
  }
36022
37449
  /**
36023
37450
  * Build and optionally broadcast the spoke-side supply transaction without waiting for the
@@ -36134,58 +37561,76 @@ var MoneyMarketService = class _MoneyMarketService {
36134
37561
  * `dstChainTxHash` (hub delivery or relay destination).
36135
37562
  */
36136
37563
  async borrow(_params) {
36137
- const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
36138
- const srcChainKey = params.srcChainKey;
36139
- const hubChainId = this.hubProvider.chainConfig.chain.key;
36140
- const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "borrow" };
36141
- try {
36142
- const txResult = await this.createBorrowIntent(_params);
36143
- if (!txResult.ok) return { ok: false, error: txResult.error };
36144
- const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
36145
- if (!verify.ok) {
36146
- return {
36147
- ok: false,
36148
- error: verifyFailed("moneyMarket", verify.error, baseCtx)
36149
- };
36150
- }
36151
- const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId;
36152
- if (!needsRelay) {
36153
- return {
36154
- ok: true,
36155
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
36156
- };
36157
- }
36158
- const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
36159
- const packet = await relayTxAndWaitPacket({
36160
- ...relayIdentity,
36161
- chainKey: srcChainKey,
36162
- relayerApiEndpoint: this.relayerApiEndpoint,
36163
- timeout
36164
- });
36165
- if (!packet.ok)
36166
- return {
36167
- ok: false,
36168
- error: mapRelayFailure(packet.error, {
36169
- feature: "moneyMarket",
36170
- action: baseCtx.action,
36171
- srcChainKey: baseCtx.srcChainKey,
36172
- dstChainKey: baseCtx.dstChainKey
36173
- })
36174
- };
36175
- return {
36176
- ok: true,
36177
- value: {
36178
- srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
36179
- dstChainTxHash: packet.value.dst_tx_hash
37564
+ return this.config.analytics.trackResult(
37565
+ "moneyMarket",
37566
+ "borrow",
37567
+ async () => {
37568
+ const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
37569
+ const srcChainKey = params.srcChainKey;
37570
+ const hubChainId = this.hubProvider.chainConfig.chain.key;
37571
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "borrow" };
37572
+ try {
37573
+ const txResult = await this.createBorrowIntent(_params);
37574
+ if (!txResult.ok) return { ok: false, error: txResult.error };
37575
+ const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
37576
+ if (!verify.ok) {
37577
+ return {
37578
+ ok: false,
37579
+ error: verifyFailed("moneyMarket", verify.error, baseCtx)
37580
+ };
37581
+ }
37582
+ const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId;
37583
+ if (!needsRelay) {
37584
+ return {
37585
+ ok: true,
37586
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
37587
+ };
37588
+ }
37589
+ const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
37590
+ const packet = await relayTxAndWaitPacket({
37591
+ ...relayIdentity,
37592
+ chainKey: srcChainKey,
37593
+ relayerApiEndpoint: this.relayerApiEndpoint,
37594
+ timeout
37595
+ });
37596
+ if (!packet.ok)
37597
+ return {
37598
+ ok: false,
37599
+ error: mapRelayFailure(packet.error, {
37600
+ feature: "moneyMarket",
37601
+ action: baseCtx.action,
37602
+ srcChainKey: baseCtx.srcChainKey,
37603
+ dstChainKey: baseCtx.dstChainKey
37604
+ })
37605
+ };
37606
+ return {
37607
+ ok: true,
37608
+ value: {
37609
+ srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
37610
+ dstChainTxHash: packet.value.dst_tx_hash
37611
+ }
37612
+ };
37613
+ } catch (error) {
37614
+ if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
37615
+ return {
37616
+ ok: false,
37617
+ error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
37618
+ };
36180
37619
  }
36181
- };
36182
- } catch (error) {
36183
- if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
36184
- return {
36185
- ok: false,
36186
- error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
36187
- };
36188
- }
37620
+ },
37621
+ {
37622
+ start: () => ({
37623
+ srcChainKey: _params.params.srcChainKey,
37624
+ srcAddress: _params.params.srcAddress,
37625
+ dstChainKey: _params.params.dstChainKey,
37626
+ dstAddress: _params.params.dstAddress,
37627
+ token: _params.params.token,
37628
+ amount: _params.params.amount
37629
+ }),
37630
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
37631
+ failure: (error) => ({ code: error.code })
37632
+ }
37633
+ );
36189
37634
  }
36190
37635
  /**
36191
37636
  * Build and optionally broadcast the spoke-side borrow message without waiting for the
@@ -36283,59 +37728,77 @@ var MoneyMarketService = class _MoneyMarketService {
36283
37728
  * `dstChainTxHash` (hub or relay destination).
36284
37729
  */
36285
37730
  async withdraw(_params) {
36286
- const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
36287
- const srcChainKey = params.srcChainKey;
36288
- const hubChainId = this.hubProvider.chainConfig.chain.key;
36289
- const walletRouter = this.hubProvider.chainConfig.addresses.walletRouter;
36290
- const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "withdraw" };
36291
- try {
36292
- const txResult = await this.createWithdrawIntent(_params);
36293
- if (!txResult.ok) return { ok: false, error: txResult.error };
36294
- const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
36295
- if (!verify.ok) {
36296
- return {
36297
- ok: false,
36298
- error: verifyFailed("moneyMarket", verify.error, baseCtx)
36299
- };
36300
- }
36301
- const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId && params.dstAddress !== walletRouter;
36302
- if (!needsRelay) {
36303
- return {
36304
- ok: true,
36305
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
36306
- };
36307
- }
36308
- const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
36309
- const packet = await relayTxAndWaitPacket({
36310
- ...relayIdentity,
36311
- chainKey: srcChainKey,
36312
- relayerApiEndpoint: this.relayerApiEndpoint,
36313
- timeout
36314
- });
36315
- if (!packet.ok)
36316
- return {
36317
- ok: false,
36318
- error: mapRelayFailure(packet.error, {
36319
- feature: "moneyMarket",
36320
- action: baseCtx.action,
36321
- srcChainKey: baseCtx.srcChainKey,
36322
- dstChainKey: baseCtx.dstChainKey
36323
- })
36324
- };
36325
- return {
36326
- ok: true,
36327
- value: {
36328
- srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
36329
- dstChainTxHash: packet.value.dst_tx_hash
37731
+ return this.config.analytics.trackResult(
37732
+ "moneyMarket",
37733
+ "withdraw",
37734
+ async () => {
37735
+ const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
37736
+ const srcChainKey = params.srcChainKey;
37737
+ const hubChainId = this.hubProvider.chainConfig.chain.key;
37738
+ const walletRouter = this.hubProvider.chainConfig.addresses.walletRouter;
37739
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "withdraw" };
37740
+ try {
37741
+ const txResult = await this.createWithdrawIntent(_params);
37742
+ if (!txResult.ok) return { ok: false, error: txResult.error };
37743
+ const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
37744
+ if (!verify.ok) {
37745
+ return {
37746
+ ok: false,
37747
+ error: verifyFailed("moneyMarket", verify.error, baseCtx)
37748
+ };
37749
+ }
37750
+ const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId && params.dstAddress !== walletRouter;
37751
+ if (!needsRelay) {
37752
+ return {
37753
+ ok: true,
37754
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
37755
+ };
37756
+ }
37757
+ const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
37758
+ const packet = await relayTxAndWaitPacket({
37759
+ ...relayIdentity,
37760
+ chainKey: srcChainKey,
37761
+ relayerApiEndpoint: this.relayerApiEndpoint,
37762
+ timeout
37763
+ });
37764
+ if (!packet.ok)
37765
+ return {
37766
+ ok: false,
37767
+ error: mapRelayFailure(packet.error, {
37768
+ feature: "moneyMarket",
37769
+ action: baseCtx.action,
37770
+ srcChainKey: baseCtx.srcChainKey,
37771
+ dstChainKey: baseCtx.dstChainKey
37772
+ })
37773
+ };
37774
+ return {
37775
+ ok: true,
37776
+ value: {
37777
+ srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
37778
+ dstChainTxHash: packet.value.dst_tx_hash
37779
+ }
37780
+ };
37781
+ } catch (error) {
37782
+ if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
37783
+ return {
37784
+ ok: false,
37785
+ error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
37786
+ };
36330
37787
  }
36331
- };
36332
- } catch (error) {
36333
- if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
36334
- return {
36335
- ok: false,
36336
- error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
36337
- };
36338
- }
37788
+ },
37789
+ {
37790
+ start: () => ({
37791
+ srcChainKey: _params.params.srcChainKey,
37792
+ srcAddress: _params.params.srcAddress,
37793
+ dstChainKey: _params.params.dstChainKey,
37794
+ dstAddress: _params.params.dstAddress,
37795
+ token: _params.params.token,
37796
+ amount: _params.params.amount
37797
+ }),
37798
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
37799
+ failure: (error) => ({ code: error.code })
37800
+ }
37801
+ );
36339
37802
  }
36340
37803
  /**
36341
37804
  * Build and optionally broadcast the spoke-side withdraw message without waiting for the
@@ -36431,50 +37894,68 @@ var MoneyMarketService = class _MoneyMarketService {
36431
37894
  * @returns A pair of transaction hashes — `srcChainTxHash` (spoke) and `dstChainTxHash` (hub).
36432
37895
  */
36433
37896
  async repay(_params) {
36434
- const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
36435
- const srcChainKey = params.srcChainKey;
36436
- const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "repay" };
36437
- try {
36438
- const txResult = await this.createRepayIntent(_params);
36439
- if (!txResult.ok) return { ok: false, error: txResult.error };
36440
- const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
36441
- if (!verify.ok) {
36442
- return {
36443
- ok: false,
36444
- error: verifyFailed("moneyMarket", verify.error, baseCtx)
36445
- };
36446
- }
36447
- if (isHubChainKeyType(srcChainKey)) {
36448
- return {
36449
- ok: true,
36450
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
36451
- };
37897
+ return this.config.analytics.trackResult(
37898
+ "moneyMarket",
37899
+ "repay",
37900
+ async () => {
37901
+ const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
37902
+ const srcChainKey = params.srcChainKey;
37903
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "repay" };
37904
+ try {
37905
+ const txResult = await this.createRepayIntent(_params);
37906
+ if (!txResult.ok) return { ok: false, error: txResult.error };
37907
+ const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
37908
+ if (!verify.ok) {
37909
+ return {
37910
+ ok: false,
37911
+ error: verifyFailed("moneyMarket", verify.error, baseCtx)
37912
+ };
37913
+ }
37914
+ if (isHubChainKeyType(srcChainKey)) {
37915
+ return {
37916
+ ok: true,
37917
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
37918
+ };
37919
+ }
37920
+ const packet = await relayTxAndWaitPacket({
37921
+ srcTxHash: txResult.value.tx,
37922
+ data: txResult.value.relayData,
37923
+ chainKey: srcChainKey,
37924
+ relayerApiEndpoint: this.relayerApiEndpoint,
37925
+ timeout
37926
+ });
37927
+ if (!packet.ok)
37928
+ return {
37929
+ ok: false,
37930
+ error: mapRelayFailure(packet.error, {
37931
+ feature: "moneyMarket",
37932
+ action: baseCtx.action,
37933
+ srcChainKey: baseCtx.srcChainKey,
37934
+ dstChainKey: baseCtx.dstChainKey
37935
+ })
37936
+ };
37937
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
37938
+ } catch (error) {
37939
+ if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
37940
+ return {
37941
+ ok: false,
37942
+ error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
37943
+ };
37944
+ }
37945
+ },
37946
+ {
37947
+ start: () => ({
37948
+ srcChainKey: _params.params.srcChainKey,
37949
+ srcAddress: _params.params.srcAddress,
37950
+ dstChainKey: _params.params.dstChainKey,
37951
+ dstAddress: _params.params.dstAddress,
37952
+ token: _params.params.token,
37953
+ amount: _params.params.amount
37954
+ }),
37955
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
37956
+ failure: (error) => ({ code: error.code })
36452
37957
  }
36453
- const packet = await relayTxAndWaitPacket({
36454
- srcTxHash: txResult.value.tx,
36455
- data: txResult.value.relayData,
36456
- chainKey: srcChainKey,
36457
- relayerApiEndpoint: this.relayerApiEndpoint,
36458
- timeout
36459
- });
36460
- if (!packet.ok)
36461
- return {
36462
- ok: false,
36463
- error: mapRelayFailure(packet.error, {
36464
- feature: "moneyMarket",
36465
- action: baseCtx.action,
36466
- srcChainKey: baseCtx.srcChainKey,
36467
- dstChainKey: baseCtx.dstChainKey
36468
- })
36469
- };
36470
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
36471
- } catch (error) {
36472
- if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
36473
- return {
36474
- ok: false,
36475
- error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
36476
- };
36477
- }
37958
+ );
36478
37959
  }
36479
37960
  /**
36480
37961
  * Build and optionally broadcast the spoke-side repay transaction without waiting for the
@@ -37175,50 +38656,67 @@ var PartnerFeeClaimService = class {
37175
38656
  * unsigned raw transaction (`raw: true`). Returns an error on failure.
37176
38657
  */
37177
38658
  async setSwapPreference(_params) {
37178
- const { params, walletProvider, raw } = _params;
37179
- try {
37180
- invariant(isHubChainKeyType(params.srcChainKey), "PartnerFeeClaimService only supports Sonic spoke provider");
37181
- invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
37182
- const outputToken = params.dstChainKey !== this.hubProvider.chainConfig.chain.key ? this.hubProvider.config.getSpokeTokenFromOriginalAssetAddress(params.dstChainKey, params.outputToken)?.hubAsset : params.outputToken;
37183
- invariant(
37184
- outputToken,
37185
- `hub asset not found for spoke chain token (params.outputToken): ${params.outputToken} with chain key: ${params.dstChainKey}`
37186
- );
37187
- const rawTx = {
37188
- from: params.srcAddress,
37189
- to: this.protocolIntentsContract,
37190
- value: 0n,
37191
- data: encodeFunctionData({
37192
- abi: ProtocolIntentsAbi,
37193
- functionName: "setAutoSwapPreferences",
37194
- args: [
38659
+ return this.config.analytics.trackResult(
38660
+ "partner",
38661
+ "setSwapPreference",
38662
+ async () => {
38663
+ const { params, walletProvider, raw } = _params;
38664
+ try {
38665
+ invariant(isHubChainKeyType(params.srcChainKey), "PartnerFeeClaimService only supports Sonic spoke provider");
38666
+ invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
38667
+ const outputToken = params.dstChainKey !== this.hubProvider.chainConfig.chain.key ? this.hubProvider.config.getSpokeTokenFromOriginalAssetAddress(params.dstChainKey, params.outputToken)?.hubAsset : params.outputToken;
38668
+ invariant(
37195
38669
  outputToken,
37196
- BigInt(getIntentRelayChainId(params.dstChainKey)),
37197
- encodeAddress(params.dstChainKey, params.dstAddress)
37198
- ]
37199
- })
37200
- };
37201
- if (raw) {
37202
- return {
37203
- ok: true,
37204
- value: rawTx
37205
- };
38670
+ `hub asset not found for spoke chain token (params.outputToken): ${params.outputToken} with chain key: ${params.dstChainKey}`
38671
+ );
38672
+ const rawTx = {
38673
+ from: params.srcAddress,
38674
+ to: this.protocolIntentsContract,
38675
+ value: 0n,
38676
+ data: encodeFunctionData({
38677
+ abi: ProtocolIntentsAbi,
38678
+ functionName: "setAutoSwapPreferences",
38679
+ args: [
38680
+ outputToken,
38681
+ BigInt(getIntentRelayChainId(params.dstChainKey)),
38682
+ encodeAddress(params.dstChainKey, params.dstAddress)
38683
+ ]
38684
+ })
38685
+ };
38686
+ if (raw) {
38687
+ return {
38688
+ ok: true,
38689
+ value: rawTx
38690
+ };
38691
+ }
38692
+ invariant(
38693
+ isEvmWalletProviderType(walletProvider),
38694
+ "PartnerFeeClaimService only supports Evm (sonic) wallet provider"
38695
+ );
38696
+ const txHash = await walletProvider.sendTransaction(rawTx);
38697
+ return {
38698
+ ok: true,
38699
+ value: txHash
38700
+ };
38701
+ } catch (error) {
38702
+ return {
38703
+ ok: false,
38704
+ error
38705
+ };
38706
+ }
38707
+ },
38708
+ {
38709
+ start: () => ({
38710
+ srcChainKey: _params.params.srcChainKey,
38711
+ srcAddress: _params.params.srcAddress,
38712
+ outputToken: _params.params.outputToken,
38713
+ dstChainKey: _params.params.dstChainKey,
38714
+ dstAddress: _params.params.dstAddress
38715
+ }),
38716
+ success: (value) => ({ txHash: value }),
38717
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
37206
38718
  }
37207
- invariant(
37208
- isEvmWalletProviderType(walletProvider),
37209
- "PartnerFeeClaimService only supports Evm (sonic) wallet provider"
37210
- );
37211
- const txHash = await walletProvider.sendTransaction(rawTx);
37212
- return {
37213
- ok: true,
37214
- value: txHash
37215
- };
37216
- } catch (error) {
37217
- return {
37218
- ok: false,
37219
- error
37220
- };
37221
- }
38719
+ );
37222
38720
  }
37223
38721
  /**
37224
38722
  * Checks whether a hub-chain ERC-20 token is already approved for the ProtocolIntents contract.
@@ -37330,6 +38828,11 @@ var PartnerFeeClaimService = class {
37330
38828
  * This is the low-level building block. Use `swap` for the full flow that also waits for
37331
38829
  * the solver to execute the intent.
37332
38830
  *
38831
+ * Guards against same-token intents: if the configured output token equals `fromToken` the solver
38832
+ * cannot fill the swap, so the call is rejected with `VALIDATION_FAILED` before any transaction is
38833
+ * built. The guard fails closed — if the preference lookup fails the call returns that error rather
38834
+ * than submitting an intent that may become unfillable.
38835
+ *
37333
38836
  * @param _params - Action descriptor containing:
37334
38837
  * - `params.srcChainKey` — must be the hub chain key (Sonic).
37335
38838
  * - `params.srcAddress` — partner's EVM address; also used as the intent creator.
@@ -37353,6 +38856,25 @@ var PartnerFeeClaimService = class {
37353
38856
  this.config.solver.protocolIntentsContract,
37354
38857
  "protocolIntentsContract is not configured in solver config"
37355
38858
  );
38859
+ const prefs = await this.getAutoSwapPreferences(params.srcAddress);
38860
+ if (!prefs.ok) return prefs;
38861
+ if (prefs.value.outputToken.toLowerCase() === params.fromToken.toLowerCase()) {
38862
+ return {
38863
+ ok: false,
38864
+ error: new SodaxError(
38865
+ "VALIDATION_FAILED",
38866
+ "Auto-swap output token equals the fee token; the solver cannot swap a token into itself. Withdraw the fee token directly (bridge/transfer) or change the swap preference before claiming.",
38867
+ {
38868
+ feature: "partner",
38869
+ context: {
38870
+ action: "createIntentAutoSwap",
38871
+ fromToken: params.fromToken,
38872
+ outputToken: prefs.value.outputToken
38873
+ }
38874
+ }
38875
+ )
38876
+ };
38877
+ }
37356
38878
  const minOutputAmount = 0n;
37357
38879
  const rawTx = {
37358
38880
  from: params.srcAddress,
@@ -37400,47 +38922,218 @@ var PartnerFeeClaimService = class {
37400
38922
  *
37401
38923
  * On failure the `error` is tagged:
37402
38924
  * - `WAIT_INTENT_AUTO_SWAP_FAILED` — transaction was submitted but receipt polling failed.
37403
- * - Error from `createIntentAutoSwap` — if the initial submission failed.
38925
+ * - Error from `createIntentAutoSwap` — if the initial submission failed, including
38926
+ * `VALIDATION_FAILED` when the output token equals the fee token (same-token guard) or the
38927
+ * preference lookup that backs the guard fails.
37404
38928
  * - Error from `SolverApiService.postExecution` — if the solver notification failed.
37405
38929
  */
37406
38930
  async swap(_params) {
37407
- try {
37408
- const txHash = await this.createIntentAutoSwap(_params);
37409
- if (!txHash.ok) {
37410
- return txHash;
38931
+ return this.config.analytics.trackResult(
38932
+ "partner",
38933
+ "swap",
38934
+ async () => {
38935
+ try {
38936
+ const txHash = await this.createIntentAutoSwap(_params);
38937
+ if (!txHash.ok) {
38938
+ return txHash;
38939
+ }
38940
+ let intentTxHash;
38941
+ try {
38942
+ const receipt = await this.hubProvider.publicClient.waitForTransactionReceipt({ hash: txHash.value });
38943
+ intentTxHash = receipt.transactionHash;
38944
+ } catch (error) {
38945
+ return {
38946
+ ok: false,
38947
+ error: new SodaxError(
38948
+ "EXECUTION_FAILED",
38949
+ error instanceof Error ? error.message : "waitIntentAutoSwap failed",
38950
+ { feature: "partner", cause: error, context: { action: "waitAutoSwap", phase: "execution" } }
38951
+ )
38952
+ };
38953
+ }
38954
+ const solverExecutionResponse = await SolverApiService.postExecution(
38955
+ { intent_tx_hash: intentTxHash },
38956
+ this.config.solver,
38957
+ this.config.logger
38958
+ );
38959
+ if (!solverExecutionResponse.ok) {
38960
+ return solverExecutionResponse;
38961
+ }
38962
+ return {
38963
+ ok: true,
38964
+ value: {
38965
+ srcTxHash: txHash.value,
38966
+ solverExecutionResponse: solverExecutionResponse.value,
38967
+ intentTxHash
38968
+ }
38969
+ };
38970
+ } catch (error) {
38971
+ return { ok: false, error };
38972
+ }
38973
+ },
38974
+ {
38975
+ start: () => ({
38976
+ srcChainKey: _params.params.srcChainKey,
38977
+ srcAddress: _params.params.srcAddress,
38978
+ fromToken: _params.params.fromToken,
38979
+ amount: _params.params.amount
38980
+ }),
38981
+ success: (value) => ({
38982
+ srcTxHash: value.srcTxHash,
38983
+ intentTxHash: value.intentTxHash
38984
+ }),
38985
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
37411
38986
  }
37412
- let intentTxHash;
37413
- try {
37414
- const receipt = await this.hubProvider.publicClient.waitForTransactionReceipt({ hash: txHash.value });
37415
- intentTxHash = receipt.transactionHash;
37416
- } catch (error) {
38987
+ );
38988
+ }
38989
+ /**
38990
+ * Cancels a partner fee-claim auto-swap intent and refunds the input token to the partner.
38991
+ *
38992
+ * This targets the ProtocolIntents contract's own `cancelIntent(fromToken, toToken)`, which is
38993
+ * the only authorized cancel path for partner auto-swap intents: the intent's `creator` is the
38994
+ * ProtocolIntents contract (not the partner), so the generic `SwapService.cancelIntent` reverts
38995
+ * with `Unauthorized()`. ProtocolIntents looks up the caller's intent for the token pair, cancels
38996
+ * it in the main intents contract (as the creator), and transfers the locked `inputAmount` back
38997
+ * to the partner (`msg.sender`).
38998
+ *
38999
+ * Use this to recover funds stuck in an unfillable intent — most commonly a same-token claim
39000
+ * (`fromToken === toToken`) the solver refused to fill (see the guard in {@link swap}).
39001
+ *
39002
+ * @param _params - Action descriptor containing:
39003
+ * - `params.srcChainKey` — must be the hub chain key (Sonic).
39004
+ * - `params.srcAddress` — the partner's EVM address; must match the intent's owner.
39005
+ * - `params.fromToken` — the stuck intent's input (fee) token, hub-chain address.
39006
+ * - `params.toToken` — the stuck intent's output token, hub-chain address.
39007
+ * - `raw` — when `true`, returns the unsigned transaction object instead of submitting it.
39008
+ * - `walletProvider` — required when `raw` is `false`; must be an EVM wallet provider.
39009
+ * @returns A `Result` containing the submitted transaction hash (`raw: false`) or the unsigned
39010
+ * raw transaction (`raw: true`).
39011
+ */
39012
+ async cancelIntent(_params) {
39013
+ return this.config.analytics.trackResult(
39014
+ "partner",
39015
+ "cancelIntent",
39016
+ async () => {
39017
+ const { params } = _params;
39018
+ try {
39019
+ invariant(isHubChainKeyType(params.srcChainKey), "PartnerFeeClaimService only supports Hub srcChainKey");
39020
+ invariant(
39021
+ isOptionalEvmWalletProviderType(_params.walletProvider),
39022
+ "PartnerFeeClaimService only supports Evm wallet provider"
39023
+ );
39024
+ invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
39025
+ const rawTx = {
39026
+ from: params.srcAddress,
39027
+ to: this.protocolIntentsContract,
39028
+ value: 0n,
39029
+ data: encodeFunctionData({
39030
+ abi: ProtocolIntentsAbi,
39031
+ functionName: "cancelIntent",
39032
+ args: [params.fromToken, params.toToken]
39033
+ })
39034
+ };
39035
+ if (_params.raw) {
39036
+ return {
39037
+ ok: true,
39038
+ value: rawTx
39039
+ };
39040
+ }
39041
+ const txHash = await _params.walletProvider.sendTransaction(rawTx);
39042
+ return {
39043
+ ok: true,
39044
+ value: txHash
39045
+ };
39046
+ } catch (error) {
39047
+ return { ok: false, error };
39048
+ }
39049
+ },
39050
+ {
39051
+ start: () => ({
39052
+ srcChainKey: _params.params.srcChainKey,
39053
+ srcAddress: _params.params.srcAddress,
39054
+ fromToken: _params.params.fromToken,
39055
+ toToken: _params.params.toToken
39056
+ }),
39057
+ success: (value) => ({ txHash: value }),
39058
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
39059
+ }
39060
+ );
39061
+ }
39062
+ /**
39063
+ * Returns the stored intent hash for a partner's `(user, fromToken, toToken)` token pair.
39064
+ *
39065
+ * A non-zero hash means an auto-swap intent currently exists for that pair (e.g. an unfilled
39066
+ * same-token claim) and can be cancelled via {@link cancelIntent}. A zero hash
39067
+ * (`0x000…0`) means there is no open intent for the pair.
39068
+ *
39069
+ * @param params.user - The partner's EVM address on Sonic.
39070
+ * @param params.fromToken - Input (fee) token, hub-chain address.
39071
+ * @param params.toToken - Output token, hub-chain address.
39072
+ * @returns A `Result` containing the intent hash, or an `Error` tagged `LOOKUP_FAILED`.
39073
+ */
39074
+ async getUserIntent({ user, fromToken, toToken }) {
39075
+ try {
39076
+ invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
39077
+ const intentHash = await this.hubProvider.publicClient.readContract({
39078
+ address: this.protocolIntentsContract,
39079
+ abi: ProtocolIntentsAbi,
39080
+ functionName: "getUserIntent",
39081
+ args: [user, fromToken, toToken]
39082
+ });
39083
+ return { ok: true, value: intentHash };
39084
+ } catch (error) {
39085
+ return { ok: false, error: lookupFailed("partner", "getUserIntent", error) };
39086
+ }
39087
+ }
39088
+ /**
39089
+ * Reads the full {@link Intent} details for a stored intent hash from the ProtocolIntents contract.
39090
+ *
39091
+ * Useful for displaying a stuck intent before cancelling it — e.g. the locked `inputAmount` and
39092
+ * `inputToken`. Pair with {@link getUserIntent} to resolve the hash from a token pair.
39093
+ *
39094
+ * @param intentHash - The intent hash (from {@link getUserIntent}).
39095
+ * @returns A `Result` containing the `Intent`, or an `Error` tagged `LOOKUP_FAILED`.
39096
+ */
39097
+ async getIntentDetails(intentHash) {
39098
+ try {
39099
+ invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
39100
+ const intent = await this.hubProvider.publicClient.readContract({
39101
+ address: this.protocolIntentsContract,
39102
+ abi: ProtocolIntentsAbi,
39103
+ functionName: "getIntentDetails",
39104
+ args: [intentHash]
39105
+ });
39106
+ if (!this.config.isValidIntentRelayChainId(intent.srcChain) || !this.config.isValidIntentRelayChainId(intent.dstChain)) {
37417
39107
  return {
37418
39108
  ok: false,
37419
- error: new SodaxError(
37420
- "EXECUTION_FAILED",
37421
- error instanceof Error ? error.message : "waitIntentAutoSwap failed",
37422
- { feature: "partner", cause: error, context: { action: "waitAutoSwap", phase: "execution" } }
39109
+ error: lookupFailed(
39110
+ "partner",
39111
+ "getIntentDetails",
39112
+ new Error(`Invalid intent relay chain id: ${intent.srcChain} or ${intent.dstChain}`)
37423
39113
  )
37424
39114
  };
37425
39115
  }
37426
- const solverExecutionResponse = await SolverApiService.postExecution(
37427
- { intent_tx_hash: intentTxHash },
37428
- this.config.solver,
37429
- this.config.logger
37430
- );
37431
- if (!solverExecutionResponse.ok) {
37432
- return solverExecutionResponse;
37433
- }
37434
39116
  return {
37435
39117
  ok: true,
37436
39118
  value: {
37437
- srcTxHash: txHash.value,
37438
- solverExecutionResponse: solverExecutionResponse.value,
37439
- intentTxHash
39119
+ intentId: intent.intentId,
39120
+ creator: intent.creator,
39121
+ inputToken: intent.inputToken,
39122
+ outputToken: intent.outputToken,
39123
+ inputAmount: intent.inputAmount,
39124
+ minOutputAmount: intent.minOutputAmount,
39125
+ deadline: intent.deadline,
39126
+ allowPartialFill: intent.allowPartialFill,
39127
+ srcChain: intent.srcChain,
39128
+ dstChain: intent.dstChain,
39129
+ srcAddress: intent.srcAddress,
39130
+ dstAddress: intent.dstAddress,
39131
+ solver: intent.solver,
39132
+ data: intent.data
37440
39133
  }
37441
39134
  };
37442
39135
  } catch (error) {
37443
- return { ok: false, error };
39136
+ return { ok: false, error: lookupFailed("partner", "getIntentDetails", error) };
37444
39137
  }
37445
39138
  }
37446
39139
  };
@@ -37575,42 +39268,62 @@ var RecoveryService = class {
37575
39268
  * transaction object when `raw: true`, or the broadcast transaction hash when `raw: false`.
37576
39269
  */
37577
39270
  async withdrawHubAsset(_params) {
37578
- const { params } = _params;
37579
- try {
37580
- const hubWallet = await this.hubProvider.getUserHubWalletAddress(params.srcAddress, params.srcChainKey);
37581
- const payload = EvmAssetManagerService.withdrawAssetData(
37582
- {
37583
- token: params.token,
37584
- to: encodeAddress(params.srcChainKey, params.srcAddress),
37585
- amount: params.amount
37586
- },
37587
- this.hubProvider,
37588
- params.srcChainKey
37589
- );
37590
- const coreParams = {
37591
- srcChainKey: params.srcChainKey,
37592
- srcAddress: params.srcAddress,
37593
- dstChainKey: this.hubProvider.chainConfig.chain.key,
37594
- dstAddress: hubWallet,
37595
- payload
37596
- };
37597
- const sendMessageParams = _params.raw ? { ...coreParams, raw: true } : { ...coreParams, raw: false, walletProvider: _params.walletProvider };
37598
- const txResult = await this.spoke.sendMessage(sendMessageParams);
37599
- if (!txResult.ok) return txResult;
37600
- return {
37601
- ok: true,
37602
- value: txResult.value
37603
- };
37604
- } catch (error) {
37605
- return {
37606
- ok: false,
37607
- error: new SodaxError("EXECUTION_FAILED", error instanceof Error ? error.message : "withdrawHubAsset failed", {
37608
- feature: "recovery",
37609
- cause: error,
37610
- context: { action: "withdrawHubAsset", phase: "execution" }
37611
- })
37612
- };
37613
- }
39271
+ return this.config.analytics.trackResult(
39272
+ "recovery",
39273
+ "withdrawHubAsset",
39274
+ async () => {
39275
+ const { params } = _params;
39276
+ try {
39277
+ const hubWallet = await this.hubProvider.getUserHubWalletAddress(params.srcAddress, params.srcChainKey);
39278
+ const payload = EvmAssetManagerService.withdrawAssetData(
39279
+ {
39280
+ token: params.token,
39281
+ to: encodeAddress(params.srcChainKey, params.srcAddress),
39282
+ amount: params.amount
39283
+ },
39284
+ this.hubProvider,
39285
+ params.srcChainKey
39286
+ );
39287
+ const coreParams = {
39288
+ srcChainKey: params.srcChainKey,
39289
+ srcAddress: params.srcAddress,
39290
+ dstChainKey: this.hubProvider.chainConfig.chain.key,
39291
+ dstAddress: hubWallet,
39292
+ payload
39293
+ };
39294
+ const sendMessageParams = _params.raw ? { ...coreParams, raw: true } : {
39295
+ ...coreParams,
39296
+ raw: false,
39297
+ walletProvider: _params.walletProvider
39298
+ };
39299
+ const txResult = await this.spoke.sendMessage(sendMessageParams);
39300
+ if (!txResult.ok) return txResult;
39301
+ return {
39302
+ ok: true,
39303
+ value: txResult.value
39304
+ };
39305
+ } catch (error) {
39306
+ return {
39307
+ ok: false,
39308
+ error: new SodaxError(
39309
+ "EXECUTION_FAILED",
39310
+ error instanceof Error ? error.message : "withdrawHubAsset failed",
39311
+ { feature: "recovery", cause: error, context: { action: "withdrawHubAsset", phase: "execution" } }
39312
+ )
39313
+ };
39314
+ }
39315
+ },
39316
+ {
39317
+ start: () => ({
39318
+ srcChainKey: _params.params.srcChainKey,
39319
+ srcAddress: _params.params.srcAddress,
39320
+ token: _params.params.token,
39321
+ amount: _params.params.amount
39322
+ }),
39323
+ success: (value) => ({ txHash: typeof value === "string" ? value : void 0 }),
39324
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
39325
+ }
39326
+ );
37614
39327
  }
37615
39328
  };
37616
39329
 
@@ -37712,7 +39425,7 @@ var LeverageYieldService = class {
37712
39425
  }
37713
39426
  /** Looks up a vault by its `name` field. Returns `undefined` when not registered. */
37714
39427
  getVault(name) {
37715
- return this.listVaults().find((v) => v.name === name);
39428
+ return this.listVaults().find((v6) => v6.name === name);
37716
39429
  }
37717
39430
  /**
37718
39431
  * Looks up a registered vault by its on-chain proxy address (case-insensitive).
@@ -37720,7 +39433,7 @@ var LeverageYieldService = class {
37720
39433
  */
37721
39434
  getVaultByAddress(address) {
37722
39435
  const normalized = address.toLowerCase();
37723
- return this.listVaults().find((v) => v.vault.toLowerCase() === normalized);
39436
+ return this.listVaults().find((v6) => v6.vault.toLowerCase() === normalized);
37724
39437
  }
37725
39438
  /**
37726
39439
  * Resolves the intent `deadline`: returns the caller-supplied value verbatim, otherwise
@@ -38070,70 +39783,93 @@ var LeverageYieldService = class {
38070
39783
  const { params } = _params;
38071
39784
  const srcChainKey = params.srcChainKey;
38072
39785
  const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "vaultSwap" };
38073
- try {
38074
- const timeout = _params.timeout;
38075
- const createIntentResult = await this.createVaultIntent(_params);
38076
- if (!createIntentResult.ok) {
38077
- return { ok: false, error: createIntentResult.error };
38078
- }
38079
- const { tx: spokeTxHash, intent, relayData } = createIntentResult.value;
38080
- const verifyTxHashResult = await this.spoke.verifyTxHash({
38081
- txHash: spokeTxHash,
38082
- chainKey: srcChainKey
38083
- });
38084
- if (!verifyTxHashResult.ok) {
38085
- return {
38086
- ok: false,
38087
- error: verifyFailed("leverageYield", verifyTxHashResult.error, baseCtx)
38088
- };
38089
- }
38090
- let dstIntentTxHash;
38091
- if (isHubChainKeyType(srcChainKey)) {
38092
- dstIntentTxHash = spokeTxHash;
38093
- } else {
38094
- const packet = await relayTxAndWaitPacket({
38095
- srcTxHash: spokeTxHash,
38096
- data: relayData,
38097
- chainKey: srcChainKey,
38098
- relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
38099
- timeout
38100
- });
38101
- if (!packet.ok) {
39786
+ return this.config.analytics.trackResult(
39787
+ "leverageYield",
39788
+ "vaultSwap",
39789
+ async () => {
39790
+ try {
39791
+ const timeout = _params.timeout;
39792
+ const createIntentResult = await this.createVaultIntent(_params);
39793
+ if (!createIntentResult.ok) {
39794
+ return { ok: false, error: createIntentResult.error };
39795
+ }
39796
+ const { tx: spokeTxHash, intent, relayData } = createIntentResult.value;
39797
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
39798
+ txHash: spokeTxHash,
39799
+ chainKey: srcChainKey
39800
+ });
39801
+ if (!verifyTxHashResult.ok) {
39802
+ return {
39803
+ ok: false,
39804
+ error: verifyFailed("leverageYield", verifyTxHashResult.error, baseCtx)
39805
+ };
39806
+ }
39807
+ let dstIntentTxHash;
39808
+ if (isHubChainKeyType(srcChainKey)) {
39809
+ dstIntentTxHash = spokeTxHash;
39810
+ } else {
39811
+ const packet = await relayTxAndWaitPacket({
39812
+ srcTxHash: spokeTxHash,
39813
+ data: relayData,
39814
+ chainKey: srcChainKey,
39815
+ relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
39816
+ timeout
39817
+ });
39818
+ if (!packet.ok) {
39819
+ return {
39820
+ ok: false,
39821
+ error: mapRelayFailure(packet.error, { feature: "leverageYield", ...baseCtx })
39822
+ };
39823
+ }
39824
+ dstIntentTxHash = packet.value.dst_tx_hash;
39825
+ }
39826
+ const postExecResult = await this.notifySolver({
39827
+ intent_tx_hash: dstIntentTxHash
39828
+ });
39829
+ if (!postExecResult.ok) {
39830
+ return { ok: false, error: postExecResult.error };
39831
+ }
39832
+ return {
39833
+ ok: true,
39834
+ value: {
39835
+ solverExecutionResponse: postExecResult.value,
39836
+ intent,
39837
+ intentDeliveryInfo: {
39838
+ srcChainKey,
39839
+ srcTxHash: spokeTxHash,
39840
+ srcAddress: params.srcAddress,
39841
+ dstChainKey: params.dstChainKey,
39842
+ dstTxHash: dstIntentTxHash,
39843
+ dstAddress: params.dstAddress
39844
+ }
39845
+ }
39846
+ };
39847
+ } catch (error) {
39848
+ if (isLeverageYieldSwapError(error)) return { ok: false, error };
38102
39849
  return {
38103
39850
  ok: false,
38104
- error: mapRelayFailure(packet.error, { feature: "leverageYield", ...baseCtx })
39851
+ error: unknownFailed("leverageYield", error, baseCtx)
38105
39852
  };
38106
39853
  }
38107
- dstIntentTxHash = packet.value.dst_tx_hash;
38108
- }
38109
- const postExecResult = await this.notifySolver({
38110
- intent_tx_hash: dstIntentTxHash
38111
- });
38112
- if (!postExecResult.ok) {
38113
- return { ok: false, error: postExecResult.error };
39854
+ },
39855
+ {
39856
+ start: () => ({
39857
+ srcChainKey: _params.params.srcChainKey,
39858
+ dstChainKey: _params.params.dstChainKey,
39859
+ srcAddress: _params.params.srcAddress,
39860
+ dstAddress: _params.params.dstAddress,
39861
+ inputToken: _params.params.inputToken,
39862
+ outputToken: _params.params.outputToken,
39863
+ inputAmount: _params.params.inputAmount
39864
+ }),
39865
+ success: (value) => ({
39866
+ intentId: value.intent.intentId,
39867
+ srcTxHash: value.intentDeliveryInfo.srcTxHash,
39868
+ dstTxHash: value.intentDeliveryInfo.dstTxHash
39869
+ }),
39870
+ failure: (error) => ({ code: error.code })
38114
39871
  }
38115
- return {
38116
- ok: true,
38117
- value: {
38118
- solverExecutionResponse: postExecResult.value,
38119
- intent,
38120
- intentDeliveryInfo: {
38121
- srcChainKey,
38122
- srcTxHash: spokeTxHash,
38123
- srcAddress: params.srcAddress,
38124
- dstChainKey: params.dstChainKey,
38125
- dstTxHash: dstIntentTxHash,
38126
- dstAddress: params.dstAddress
38127
- }
38128
- }
38129
- };
38130
- } catch (error) {
38131
- if (isLeverageYieldSwapError(error)) return { ok: false, error };
38132
- return {
38133
- ok: false,
38134
- error: unknownFailed("leverageYield", error, baseCtx)
38135
- };
38136
- }
39872
+ );
38137
39873
  }
38138
39874
  /**
38139
39875
  * Notifies the solver that the vault intent landed on the hub, triggering it to fill.
@@ -38490,6 +40226,8 @@ var Sodax = class {
38490
40226
  // ICX migration service enabling ICX migration to SODA
38491
40227
  backendApi;
38492
40228
  // backend API service enabling backend API endpoints
40229
+ api;
40230
+ // syntactic sugar for backend API service
38493
40231
  bridge;
38494
40232
  // Bridge service enabling cross-chain transfers
38495
40233
  staking;
@@ -38510,14 +40248,18 @@ var Sodax = class {
38510
40248
  // spoke service enabling spoke chain operations
38511
40249
  constructor(options) {
38512
40250
  const logger = resolveLogger(options?.logger);
40251
+ const analytics = resolveAnalytics(options?.analytics);
38513
40252
  const fee = options?.fee;
40253
+ const useBackendSubmitTx = options?.swapsOptions?.useBackendSubmitTx ?? false;
38514
40254
  this.instanceConfig = options ? mergeSodaxConfig(sodaxConfig, options) : sodaxConfig;
38515
40255
  this.backendApi = new BackendApiService(this.instanceConfig.api, logger);
40256
+ this.api = this.backendApi;
38516
40257
  this.config = new ConfigService({
38517
40258
  api: this.backendApi,
38518
40259
  config: this.instanceConfig,
38519
40260
  userConfig: options,
38520
40261
  logger,
40262
+ analytics,
38521
40263
  fee
38522
40264
  });
38523
40265
  this.hubProvider = new EvmHubProvider({ config: this.config });
@@ -38525,7 +40267,9 @@ var Sodax = class {
38525
40267
  this.swaps = new SwapService({
38526
40268
  config: this.config,
38527
40269
  hubProvider: this.hubProvider,
38528
- spoke: this.spoke
40270
+ spoke: this.spoke,
40271
+ backendApi: this.backendApi,
40272
+ useBackendSubmitTx
38529
40273
  });
38530
40274
  this.moneyMarket = new MoneyMarketService({
38531
40275
  config: this.config,
@@ -38599,4 +40343,4 @@ var isPartnerError = isCodeMember(PARTNER_CODES);
38599
40343
  (*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
38600
40344
  */
38601
40345
 
38602
- export { ALLOWANCE_CHECK_CODES, APPROVE_CODES, AssetService, BITCOIN_CHAIN_KEYS, BITCOIN_CHAIN_KEYS_SET, BTC_WALLET_ADDRESS_TYPES, BackendApiService, BalnSwapService, BigIntToHex, BigNumberZeroDecimal, BitcoinSpokeService, BnUSDMigrationService, BridgeService, CHAIN_KEYS, CHAIN_LOGO_BASE_URL, CONFIG_VERSION, CREATE_INTENT_CODES, ChainKeys, ChainTypeArr, ClService, ConfigService, CustomSorobanServer, CustomStellarAccount, DEFAULT_BACKEND_API_ENDPOINT, DEFAULT_BACKEND_API_HEADERS, DEFAULT_BACKEND_API_TIMEOUT, DEFAULT_DEADLINE_OFFSET, DEFAULT_MAX_RETRY, DEFAULT_RELAYER_API_ENDPOINT, DEFAULT_RELAY_TX_TIMEOUT, DEFAULT_RETRY_DELAY_MS, DexService, EVM_CHAIN_ID_TO_KEY, EVM_CHAIN_KEYS, EVM_CHAIN_KEYS_SET, EVM_SPOKE_ONLY_CHAIN_KEYS, EVM_SPOKE_ONLY_CHAIN_KEYS_SET, Erc20Service, Erc4626Service, EvmAssetManagerService, EvmHubProvider, EvmSolverService, EvmSpokeService, EvmVaultTokenService, FEE_PERCENTAGE_SCALE, GAS_ESTIMATION_CODES, HALF_RAY, HALF_WAD, HUB_CHAIN_KEY, HookKind, HookService, HttpRelayError, HubVaultSymbols, ICON_CHAIN_KEYS, ICON_CHAIN_KEYS_SET, ICON_TX_RESULT_WAIT_MAX_RETRY, INJECTIVE_CHAIN_KEYS, INJECTIVE_CHAIN_KEYS_SET, INTENT_CHAIN_IDS, IconSpokeService, IcxMigrationService, Injective20Token, InjectiveSpokeService, IntentCreatedEventAbi, IntentDataService, IntentDataType, IntentFilledEventAbi, IntentRelayChainIdToChainKey, IntentsAbi, LOOKUP_CODES, LTV_PRECISION, LendingPoolService, LeverageYieldService, LockupMultiplier, LockupPeriod, LsodaSymbols, LsodaTokens, MAX_UINT256, MigrationService, MintPositionEventAbi, MoneyMarketDataService, MoneyMarketService, NEAR_CHAIN_KEYS, NEAR_CHAIN_KEYS_SET, NEAR_DEFAULT_GAS, NEAR_STORAGE_DEPOSIT, NearSpokeService, PartnerFeeClaimService, PartnerService, Permit2Service, ProtocolIntentsAbi, RAY, RAY_DECIMALS, RELAY_ERROR_CODES, RadfiApiError, RadfiProvider, RecoveryService, RelayChainIdMap, SECONDS_PER_YEAR, SODAX_ERROR_CODES, SODAX_FEATURES, SOLANA_CHAIN_KEYS, SOLANA_CHAIN_KEYS_SET, SONIC_CHAIN_KEYS, SONIC_CHAIN_KEYS_SET, STACKS_CHAIN_KEYS, STACKS_CHAIN_KEYS_SET, STELLAR_CHAIN_KEYS, STELLAR_CHAIN_KEYS_SET, SUI_CHAIN_KEYS, SUI_CHAIN_KEYS_SET, SodaTokens, Sodax, SodaxError, SolanaSpokeService, SolverApiService, SolverIntentErrorCode, SolverIntentStatusCode, SonicSpokeService, SpokeService, StacksSpokeService, StakingLogic, StakingService, StatATokenAddresses, StellarSpokeService, SuiSpokeService, SupportedMigrationTokens, SwapService, USD_DECIMALS, UiPoolDataProviderService, VAULT_TOKEN_DECIMALS, WAD, WAD_RAY_RATIO, WEI_DECIMALS, adjustAmountByFee, allowanceCheckFailed, apiConfig, approveFailed, arbitrumSupportedTokens, assetManagerAbi, avalancheSupportedTokens, balnSwapAbi, baseChainInfo, baseSupportedTokens, binomialApproximatedRayPow, bitcoinSupportedTokens, bnUSDLegacySpokeChainIds, bnUSDLegacyTokens, bridgeConfig, bridgeInvariant, bscSupportedTokens, calcOpReturnOutputVbytes, calculateAllReserveIncentives, calculateAllUserIncentives, calculateAvailableBorrowsMarketReferenceCurrency, calculateCompoundedInterest, calculateCompoundedRate, calculateFeeAmount, calculateHealthFactorFromBalances, calculateHealthFactorFromBalancesBigUnits, calculateLinearInterest, calculatePercentageFeeAmount, clPoolAbi, clPoolManagerAbi, clPositionManagerAbi, clQuoterAbi, clRouterAbi, clTickLensAbi, concentratedLiquidityConfig, connectionAbi, consoleLogger, convertTransactionInstructionToRaw, createInvariant, deepMerge, defaultHookAbi, detectBitcoinAddressType, dexConfig, dexInvariant, dexPools, encodeAddress, encodeBtcPayloadToBytes, encodeContractCalls, erc20Abi, erc4626Abi, estimateBitcoinTxSize, ethereumSupportedTokens, executionFailed, formatBasisPoints, formatEModeCategory, formatEModes, formatPercentage, formatReserve, formatReserveUSD, formatReserves, formatReservesAndIncentives, formatUserSummary, formatUserSummaryAndIncentives, gasEstimationFailed, getAllLegacybnUSDTokens, getAndFormatReserveEModes, getAssetManagerIdl, getAssetManagerProgram, getChainKeyFromRelayChainId, getChainType, getCompoundedBalance, getConcentratedLiquidityConfig, getConnectionIdl, getConnectionProgram, getEvmChainKeyByChainId, getEvmViemChain, getIconAddressBytes, getIntentRelayChainId, getLinearBalance, getMarketReferenceCurrencyAndUsdBalance, getPacket, getProvider, getRandomBytes, getReserveNormalizedIncome, getReservesEModes, getSolanaAddressBytes, getSolverConfig, getSpokeHook, getSpokeHooks, getStagingSolverTokens, getSupportedSolverTokens, getTransactionPackets, getbnUSDToken, hexToBigInt, hexToSolanaAddress, hubConfig, hyper, hyperevmSupportedTokens, iconSupportedTokens, injectiveSupportedTokens, intentCreationFailed, isBalnMigrateParams, isBitcoinChainKey, isBitcoinChainKeyType, isBitcoinWalletProviderType, isBridgeAllowanceCheckError, isBridgeApproveError, isBridgeCreateIntentError, isBridgeError, isBridgeLookupError, isBridgeOrchestrationError, isCodeMember, isDexError, isEvmChainKey, isEvmChainKeyType, isEvmSpokeChainConfig, isEvmSpokeOnlyChainKey, isEvmSpokeOnlyChainKeyType, isEvmWalletProviderType, isFeatureError, isHookSupportedToken, isHubChainKey, isHubChainKeyType, isIconAddress, isIconChainKey, isIconChainKeyType, isIcxCreateRevertMigrationParams, isIcxMigrateParams, isInjectiveChainKey, isInjectiveChainKeyType, isJsonRpcPayloadResponse, isLegacybnUSDChainId, isLegacybnUSDToken, isLeverageYieldAllowanceCheckError, isLeverageYieldApproveError, isLeverageYieldCreateIntentError, isLeverageYieldError, isLeverageYieldLookupError, isLeverageYieldPostExecutionError, isLeverageYieldSwapError, isMigrateOrchestrationError, isMigrationAllowanceCheckError, isMigrationApproveError, isMigrationCreateIntentError, isMigrationError, isMigrationLookupError, isMoneyMarketAllowanceCheckError, isMoneyMarketApproveError, isMoneyMarketCreateIntentError, isMoneyMarketError, isMoneyMarketGasEstimationError, isMoneyMarketOrchestrationError, isNativeToken, isNearChainKey, isNearChainKeyType, isNewbnUSDChainId, isNewbnUSDToken, isOptionalBitcoinWalletProviderType, isOptionalEvmWalletProviderType, isOptionalStellarWalletProviderType, isPartnerError, isPartnerFeeAmount, isPartnerFeePercentage, isPostExecutionError, isRawDestinationParams, isRecoveryError, isResponseAddressType, isResponseSigningType, isRevertMigrationOrchestrationError, isSodaxError, isSolanaChainKey, isSolanaChainKeyType, isSolanaNativeToken, isSonicChainKey, isSonicChainKeyType, isSpokeApproveParamsEvmSpoke, isSpokeApproveParamsHub, isSpokeApproveParamsStellar, isSpokeChainKey, isSpokeIsAllowanceValidParamsEvmSpoke, isSpokeIsAllowanceValidParamsHub, isSpokeIsAllowanceValidParamsStellar, isStacksChainKey, isStacksChainKeyType, isStakeOrchestrationError, isStakingAllowanceCheckError, isStakingApproveError, isStakingCreateIntentError, isStakingError, isStakingInfoFetchError, isStakingOrchestrationError, isStellarChainKey, isStellarChainKeyType, isStellarWalletProviderType, isSubmitSwapTxResponse, isSubmitSwapTxStatusResponse, isSuiChainKey, isSuiChainKeyType, isSupportedBitcoinAddressType, isSwapCreateIntentError, isSwapError, isSwapSupportedToken, isUndefinedOrValidWalletProviderForChainKey, isUnifiedBnUSDMigrateParams, isValidWalletProviderForChainKey, kaiaSupportedTokens, leverageYieldConfig, leverageYieldInvariant, leverageYieldVaults, lightlinkSupportedTokens, lookupFailed, mapRelayFailure, mergeSodaxConfig, messageOf, migrationInvariant, mintPositionParamsAbi, mmInvariant, modifyLiquidityParamsAbi, moneyMarketConfig, moneyMarketReserveAssets, moneyMarketSupportedTokens, nativeToUSD, nearSupportedTokens, newbnUSDSpokeChainIds, normalize, normalizeBN, normalizePsbtToBase64, normalizeSignatureToBase64, normalizedToUsd, optimismSupportedTokens, pancakeSwapInfinityDefaultHookAbi, pancakeSwapInfinityPoolManagerAbi, pancakeSwapInfinityPositionManagerAbi, parseToStroops, parseTokenArrayFromJson, partnerInvariant, permit2Abi, polygonSupportedTokens, poolAbi, poolKeyAbi, randomUint256, rayDiv, rayMul, rayPow, rayToWad, recoveryInvariant, redbellySupportedTokens, relayConfig, relayTxAndWaitPacket, requestAddress, requestJsonRpc, requestSigning, resolveLogger, retry, reverseEncodeAddress, serializeAddressData, silentLogger, sleep, sodaxConfig, sodaxInvariant, solanaSupportedTokens, solverConfig, sonicSupportedTokens, sonicWalletFactoryAbi, spokeAssetManagerAbi, spokeChainConfig, spokeChainKeysSet, spokeHooks, stacksSupportedTokens, stagingSwapSupportedTokens, stakedSodaAbi, stakingInvariant, stakingRouterAbi, stataTokenFactoryAbi, stellarSupportedTokens, submitTransaction, suiSupportedTokens, supportedSpokeChains, supportedTokensByChain, swapExactInSingleParamsAbi, swapInvariant, swapSupportedTokens, swapsConfig, uiPoolDataAbi, universalRouterAbi, unknownFailed, usesBip322MessageSigning, valueToBigNumber, valueToZDBigNumber, variableDebtTokenAbi, vaultTokenAbi, verifyFailed, wadToRay, waitForStacksTransaction, waitForTransactionReceipt, waitUntilIntentExecuted, walletFactoryAbi, wrappedSonicAbi };
40346
+ export { ALLOWANCE_CHECK_CODES, APPROVE_CODES, AssetService, BITCOIN_CHAIN_KEYS, BITCOIN_CHAIN_KEYS_SET, BTC_WALLET_ADDRESS_TYPES, BackendApiService, BalnSwapService, BigIntToHex, BigNumberZeroDecimal, BitcoinSpokeService, BnUSDMigrationService, BridgeService, CHAIN_KEYS, CHAIN_LOGO_BASE_URL, CONFIG_VERSION, CREATE_INTENT_CODES, ChainKeys, ChainTypeArr, ClService, ConfigService, CustomSorobanServer, CustomStellarAccount, DEFAULT_BACKEND_API_ENDPOINT, DEFAULT_BACKEND_API_HEADERS, DEFAULT_BACKEND_API_TIMEOUT, DEFAULT_DEADLINE_OFFSET, DEFAULT_MAX_RETRY, DEFAULT_RELAYER_API_ENDPOINT, DEFAULT_RELAY_TX_TIMEOUT, DEFAULT_RETRY_DELAY_MS, DexService, EVM_CHAIN_ID_TO_KEY, EVM_CHAIN_KEYS, EVM_CHAIN_KEYS_SET, EVM_SPOKE_ONLY_CHAIN_KEYS, EVM_SPOKE_ONLY_CHAIN_KEYS_SET, Erc20Service, Erc4626Service, EvmAssetManagerService, EvmHubProvider, EvmSolverService, EvmSpokeService, EvmVaultTokenService, FEE_PERCENTAGE_SCALE, GAS_ESTIMATION_CODES, HALF_RAY, HALF_WAD, HUB_CHAIN_KEY, HookKind, HookService, HttpRelayError, HubVaultSymbols, ICON_CHAIN_KEYS, ICON_CHAIN_KEYS_SET, ICON_TX_RESULT_WAIT_MAX_RETRY, INJECTIVE_CHAIN_KEYS, INJECTIVE_CHAIN_KEYS_SET, INTENT_CHAIN_IDS, IconSpokeService, IcxMigrationService, Injective20Token, InjectiveSpokeService, IntentCreatedEventAbi, IntentDataService, IntentDataType, IntentFilledEventAbi, IntentRelayChainIdToChainKey, IntentsAbi, LOOKUP_CODES, LTV_PRECISION, LendingPoolService, LeverageYieldService, LockupMultiplier, LockupPeriod, LsodaSymbols, LsodaTokens, MAX_UINT256, MigrationService, MintPositionEventAbi, MoneyMarketDataService, MoneyMarketService, NEAR_CHAIN_KEYS, NEAR_CHAIN_KEYS_SET, NEAR_DEFAULT_GAS, NEAR_STORAGE_DEPOSIT, NearSpokeService, PartnerFeeClaimService, PartnerService, Permit2Service, ProtocolIntentsAbi, RAY, RAY_DECIMALS, RELAY_ERROR_CODES, RadfiApiError, RadfiProvider, RecoveryService, RelayChainIdMap, SECONDS_PER_YEAR, SODAX_ERROR_CODES, SODAX_FEATURES, SOLANA_CHAIN_KEYS, SOLANA_CHAIN_KEYS_SET, SONIC_CHAIN_KEYS, SONIC_CHAIN_KEYS_SET, STACKS_CHAIN_KEYS, STACKS_CHAIN_KEYS_SET, STELLAR_CHAIN_KEYS, STELLAR_CHAIN_KEYS_SET, SUI_CHAIN_KEYS, SUI_CHAIN_KEYS_SET, SodaTokens, Sodax, SodaxError, SolanaSpokeService, SolverApiService, SolverIntentErrorCode, SolverIntentStatusCode, SonicSpokeService, SpokeService, StacksSpokeService, StakingLogic, StakingService, StatATokenAddresses, StellarSpokeService, SuiSpokeService, SupportedMigrationTokens, SwapService, SwapsApiService, USD_DECIMALS, UiPoolDataProviderService, VAULT_TOKEN_DECIMALS, WAD, WAD_RAY_RATIO, WEI_DECIMALS, adjustAmountByFee, allowanceCheckFailed, apiConfig, approveFailed, arbitrumSupportedTokens, assetManagerAbi, avalancheSupportedTokens, balnSwapAbi, baseChainInfo, baseSupportedTokens, binomialApproximatedRayPow, bitcoinSupportedTokens, bnUSDLegacySpokeChainIds, bnUSDLegacyTokens, bridgeConfig, bridgeInvariant, bscSupportedTokens, calcOpReturnOutputVbytes, calculateAllReserveIncentives, calculateAllUserIncentives, calculateAvailableBorrowsMarketReferenceCurrency, calculateCompoundedInterest, calculateCompoundedRate, calculateFeeAmount, calculateHealthFactorFromBalances, calculateHealthFactorFromBalancesBigUnits, calculateLinearInterest, calculatePercentageFeeAmount, clPoolAbi, clPoolManagerAbi, clPositionManagerAbi, clQuoterAbi, clRouterAbi, clTickLensAbi, concentratedLiquidityConfig, connectionAbi, consoleLogger, convertTransactionInstructionToRaw, createInvariant, deepMerge, defaultHookAbi, detectBitcoinAddressType, dexConfig, dexInvariant, dexPools, encodeAddress, encodeBtcPayloadToBytes, encodeContractCalls, erc20Abi, erc4626Abi, estimateBitcoinTxSize, ethereumSupportedTokens, executionFailed, formatBasisPoints, formatEModeCategory, formatEModes, formatPercentage, formatReserve, formatReserveUSD, formatReserves, formatReservesAndIncentives, formatUserSummary, formatUserSummaryAndIncentives, gasEstimationFailed, getAllLegacybnUSDTokens, getAndFormatReserveEModes, getAssetManagerIdl, getAssetManagerProgram, getChainKeyFromRelayChainId, getChainType, getCompoundedBalance, getConcentratedLiquidityConfig, getConnectionIdl, getConnectionProgram, getEvmChainKeyByChainId, getEvmViemChain, getIconAddressBytes, getIntentRelayChainId, getLinearBalance, getMarketReferenceCurrencyAndUsdBalance, getPacket, getProvider, getRandomBytes, getReserveNormalizedIncome, getReservesEModes, getSolanaAddressBytes, getSolverConfig, getSpokeHook, getSpokeHooks, getStagingSolverTokens, getSupportedSolverTokens, getTransactionPackets, getbnUSDToken, hexToBigInt, hexToSolanaAddress, hubConfig, hyper, hyperevmSupportedTokens, iconSupportedTokens, injectiveSupportedTokens, intentCreationFailed, isBalnMigrateParams, isBitcoinChainKey, isBitcoinChainKeyType, isBitcoinWalletProviderType, isBridgeAllowanceCheckError, isBridgeApproveError, isBridgeCreateIntentError, isBridgeError, isBridgeLookupError, isBridgeOrchestrationError, isCodeMember, isDexError, isEvmChainKey, isEvmChainKeyType, isEvmSpokeChainConfig, isEvmSpokeOnlyChainKey, isEvmSpokeOnlyChainKeyType, isEvmWalletProviderType, isFeatureError, isHookSupportedToken, isHubChainKey, isHubChainKeyType, isIconAddress, isIconChainKey, isIconChainKeyType, isIcxCreateRevertMigrationParams, isIcxMigrateParams, isInjectiveChainKey, isInjectiveChainKeyType, isJsonRpcPayloadResponse, isLegacybnUSDChainId, isLegacybnUSDToken, isLeverageYieldAllowanceCheckError, isLeverageYieldApproveError, isLeverageYieldCreateIntentError, isLeverageYieldError, isLeverageYieldLookupError, isLeverageYieldPostExecutionError, isLeverageYieldSwapError, isMigrateOrchestrationError, isMigrationAllowanceCheckError, isMigrationApproveError, isMigrationCreateIntentError, isMigrationError, isMigrationLookupError, isMoneyMarketAllowanceCheckError, isMoneyMarketApproveError, isMoneyMarketCreateIntentError, isMoneyMarketError, isMoneyMarketGasEstimationError, isMoneyMarketOrchestrationError, isNativeToken, isNearChainKey, isNearChainKeyType, isNewbnUSDChainId, isNewbnUSDToken, isOptionalBitcoinWalletProviderType, isOptionalEvmWalletProviderType, isOptionalStellarWalletProviderType, isPartnerError, isPartnerFeeAmount, isPartnerFeePercentage, isPostExecutionError, isRawDestinationParams, isRecoveryError, isResponseAddressType, isResponseSigningType, isRevertMigrationOrchestrationError, isSodaxError, isSolanaChainKey, isSolanaChainKeyType, isSolanaNativeToken, isSonicChainKey, isSonicChainKeyType, isSpokeApproveParamsEvmSpoke, isSpokeApproveParamsHub, isSpokeApproveParamsStellar, isSpokeChainKey, isSpokeIsAllowanceValidParamsEvmSpoke, isSpokeIsAllowanceValidParamsHub, isSpokeIsAllowanceValidParamsStellar, isStacksChainKey, isStacksChainKeyType, isStakeOrchestrationError, isStakingAllowanceCheckError, isStakingApproveError, isStakingCreateIntentError, isStakingError, isStakingInfoFetchError, isStakingOrchestrationError, isStellarChainKey, isStellarChainKeyType, isStellarWalletProviderType, isSuiChainKey, isSuiChainKeyType, isSupportedBitcoinAddressType, isSwapCreateIntentError, isSwapError, isSwapSupportedToken, isUndefinedOrValidWalletProviderForChainKey, isUnifiedBnUSDMigrateParams, isValidWalletProviderForChainKey, kaiaSupportedTokens, leverageYieldConfig, leverageYieldInvariant, leverageYieldVaults, lightlinkSupportedTokens, lookupFailed, mapRelayFailure, mergeSodaxConfig, messageOf, migrationInvariant, mintPositionParamsAbi, mmInvariant, modifyLiquidityParamsAbi, moneyMarketConfig, moneyMarketReserveAssets, moneyMarketSupportedTokens, nativeToUSD, nearSupportedTokens, newbnUSDSpokeChainIds, noopAnalytics, normalize, normalizeBN, normalizePsbtToBase64, normalizeSignatureToBase64, normalizedToUsd, optimismSupportedTokens, pancakeSwapInfinityDefaultHookAbi, pancakeSwapInfinityPoolManagerAbi, pancakeSwapInfinityPositionManagerAbi, parseToStroops, parseTokenArrayFromJson, partnerInvariant, permit2Abi, polygonSupportedTokens, poolAbi, poolKeyAbi, randomUint256, rayDiv, rayMul, rayPow, rayToWad, recoveryInvariant, redbellySupportedTokens, relayConfig, relayTxAndWaitPacket, requestAddress, requestJsonRpc, requestSigning, resolveAnalytics, resolveLogger, retry, reverseEncodeAddress, serializeAddressData, silentLogger, sleep, sodaxConfig, sodaxInvariant, solanaSupportedTokens, solverConfig, sonicSupportedTokens, sonicWalletFactoryAbi, spokeAssetManagerAbi, spokeChainConfig, spokeChainKeysSet, spokeHooks, stacksSupportedTokens, stagingSwapSupportedTokens, stakedSodaAbi, stakingInvariant, stakingRouterAbi, stataTokenFactoryAbi, stellarSupportedTokens, submitTransaction, suiSupportedTokens, supportedSpokeChains, supportedTokensByChain, swapExactInSingleParamsAbi, swapInvariant, swapSupportedTokens, swapsConfig, uiPoolDataAbi, universalRouterAbi, unknownFailed, usesBip322MessageSigning, valueToBigNumber, valueToZDBigNumber, variableDebtTokenAbi, vaultTokenAbi, verifyFailed, wadToRay, waitForStacksTransaction, waitForTransactionReceipt, waitUntilIntentExecuted, walletFactoryAbi, wrappedSonicAbi };