@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.cjs CHANGED
@@ -19,6 +19,7 @@ var IconSdkRaw = require('icon-sdk-js');
19
19
  var sdkTs = require('@injectivelabs/sdk-ts');
20
20
  var tx_js = require('cosmjs-types/cosmos/tx/v1beta1/tx.js');
21
21
  var networks = require('@injectivelabs/networks');
22
+ var v4 = require('valibot');
22
23
  var BigNumber5 = require('bignumber.js');
23
24
  var swapSdkCore = require('@pancakeswap/swap-sdk-core');
24
25
  var v3Sdk = require('@pancakeswap/v3-sdk');
@@ -47,6 +48,7 @@ var rlp__namespace = /*#__PURE__*/_interopNamespace(rlp);
47
48
  var ecc__namespace = /*#__PURE__*/_interopNamespace(ecc);
48
49
  var BN__default = /*#__PURE__*/_interopDefault(BN);
49
50
  var IconSdkRaw__namespace = /*#__PURE__*/_interopNamespace(IconSdkRaw);
51
+ var v4__namespace = /*#__PURE__*/_interopNamespace(v4);
50
52
  var BigNumber5__default = /*#__PURE__*/_interopDefault(BigNumber5);
51
53
 
52
54
  // src/errors/SodaxError.ts
@@ -119,8 +121,8 @@ function serializeCause(cause, depth) {
119
121
  }
120
122
  function sanitizeContext(value, depth) {
121
123
  const out = {};
122
- for (const [key, v] of Object.entries(value)) {
123
- out[key] = sanitizeValue(v, depth + 1);
124
+ for (const [key, v6] of Object.entries(value)) {
125
+ out[key] = sanitizeValue(v6, depth + 1);
124
126
  }
125
127
  return out;
126
128
  }
@@ -131,13 +133,13 @@ function sanitizeValue(value, depth) {
131
133
  if (t === "bigint") return value.toString();
132
134
  if (t === "string" || t === "number" || t === "boolean") return value;
133
135
  if (t === "function" || t === "symbol") return safeString(value);
134
- if (Array.isArray(value)) return value.map((v) => sanitizeValue(v, depth + 1));
136
+ if (Array.isArray(value)) return value.map((v6) => sanitizeValue(v6, depth + 1));
135
137
  if (value instanceof Date) return value.toISOString();
136
138
  if (value instanceof Map) {
137
- return Array.from(value.entries()).map(([k, v]) => [sanitizeValue(k, depth + 1), sanitizeValue(v, depth + 1)]);
139
+ return Array.from(value.entries()).map(([k, v6]) => [sanitizeValue(k, depth + 1), sanitizeValue(v6, depth + 1)]);
138
140
  }
139
141
  if (value instanceof Set) {
140
- return Array.from(value.values()).map((v) => sanitizeValue(v, depth + 1));
142
+ return Array.from(value.values()).map((v6) => sanitizeValue(v6, depth + 1));
141
143
  }
142
144
  if (value instanceof Error) return { name: value.name, message: value.message };
143
145
  if (t === "object") {
@@ -149,9 +151,9 @@ function sanitizeValue(value, depth) {
149
151
  }
150
152
  return safeString(value);
151
153
  }
152
- function safeString(v) {
154
+ function safeString(v6) {
153
155
  try {
154
- return String(v);
156
+ return String(v6);
155
157
  } catch {
156
158
  return "[unserializable]";
157
159
  }
@@ -199,6 +201,7 @@ var SODAX_FEATURES = [
199
201
  "dex",
200
202
  "partner",
201
203
  "recovery",
204
+ "backend",
202
205
  "leverageYield"
203
206
  ];
204
207
 
@@ -2640,7 +2643,7 @@ var RelayChainIdMap = {
2640
2643
  [ChainKeys.STACKS_MAINNET]: 60n
2641
2644
  };
2642
2645
  var INTENT_CHAIN_IDS = Object.values(RelayChainIdMap);
2643
- var IntentRelayChainIdToChainKey = Object.fromEntries(Object.entries(RelayChainIdMap).map(([chainKey, chainId]) => [chainId, chainKey]));
2646
+ var IntentRelayChainIdToChainKey = new Map(Object.entries(RelayChainIdMap).map(([chainKey, chainId]) => [chainId, chainKey]));
2644
2647
  var CHAIN_LOGO_BASE_URL = "https://raw.githubusercontent.com/icon-project/sodax-sdks/main/packages/assets/chain";
2645
2648
  var chainLogo = (key) => `${CHAIN_LOGO_BASE_URL}/${key}.png`;
2646
2649
  var baseChainInfo = {
@@ -4246,7 +4249,7 @@ function isValidWalletProviderForChainKey(chainKey, walletProvider) {
4246
4249
  }
4247
4250
 
4248
4251
  // ../types/dist/index.js
4249
- var CONFIG_VERSION = 215;
4252
+ var CONFIG_VERSION = 217;
4250
4253
  function isEvmSpokeChainConfig(value) {
4251
4254
  return typeof value === "object" && value !== null && value.chain.type === "EVM" && value.chain.key !== HUB_CHAIN_KEY;
4252
4255
  }
@@ -4336,26 +4339,6 @@ function isRawDestinationParams(value) {
4336
4339
  if (typeof obj.dstAddress !== "string") return false;
4337
4340
  return typeof obj.dstChainKey === "string" && spokeChainKeysSet.has(obj.dstChainKey);
4338
4341
  }
4339
- function isSubmitSwapTxResponse(value) {
4340
- return typeof value === "object" && value !== null && typeof value.success === "boolean" && typeof value.message === "string";
4341
- }
4342
- function isSubmitSwapTxStatusResponse(value) {
4343
- if (typeof value !== "object" || value === null) return false;
4344
- const obj = value;
4345
- if (typeof obj.success !== "boolean") return false;
4346
- if (typeof obj.data !== "object" || obj.data === null) return false;
4347
- const data = obj.data;
4348
- if (typeof data.txHash !== "string") return false;
4349
- if (typeof data.srcChainKey !== "string") return false;
4350
- if (typeof data.status !== "string") return false;
4351
- if (typeof data.failedAttempts !== "number") return false;
4352
- if (data.result !== void 0) {
4353
- if (typeof data.result !== "object" || data.result === null) return false;
4354
- const result = data.result;
4355
- if (typeof result.dstIntentTxHash !== "string") return false;
4356
- }
4357
- return true;
4358
- }
4359
4342
  function isBitcoinWalletProviderType(wp) {
4360
4343
  return wp.chainType === "BITCOIN";
4361
4344
  }
@@ -4393,11 +4376,11 @@ async function retry(action, retryCount = DEFAULT_MAX_RETRY, delayMs = DEFAULT_R
4393
4376
  throw new Error(`Retry exceeded MAX_RETRY_DEFAULT=${DEFAULT_MAX_RETRY}`);
4394
4377
  }
4395
4378
  function getRandomBytes(length) {
4396
- const array = new Uint8Array(length);
4379
+ const array3 = new Uint8Array(length);
4397
4380
  for (let i = 0; i < length; i++) {
4398
- array[i] = Math.floor(Math.random() * 256);
4381
+ array3[i] = Math.floor(Math.random() * 256);
4399
4382
  }
4400
- return array;
4383
+ return array3;
4401
4384
  }
4402
4385
  function randomUint256() {
4403
4386
  const bytes = getRandomBytes(32);
@@ -10649,6 +10632,144 @@ var ProtocolIntentsAbi = [
10649
10632
  }
10650
10633
  ],
10651
10634
  stateMutability: "view"
10635
+ },
10636
+ {
10637
+ type: "function",
10638
+ name: "cancelIntent",
10639
+ inputs: [
10640
+ {
10641
+ name: "fromToken",
10642
+ type: "address",
10643
+ internalType: "address"
10644
+ },
10645
+ {
10646
+ name: "toToken",
10647
+ type: "address",
10648
+ internalType: "address"
10649
+ }
10650
+ ],
10651
+ outputs: [],
10652
+ stateMutability: "nonpayable"
10653
+ },
10654
+ {
10655
+ type: "function",
10656
+ name: "getUserIntent",
10657
+ inputs: [
10658
+ {
10659
+ name: "user",
10660
+ type: "address",
10661
+ internalType: "address"
10662
+ },
10663
+ {
10664
+ name: "fromToken",
10665
+ type: "address",
10666
+ internalType: "address"
10667
+ },
10668
+ {
10669
+ name: "toToken",
10670
+ type: "address",
10671
+ internalType: "address"
10672
+ }
10673
+ ],
10674
+ outputs: [
10675
+ {
10676
+ name: "intentHash",
10677
+ type: "bytes32",
10678
+ internalType: "bytes32"
10679
+ }
10680
+ ],
10681
+ stateMutability: "view"
10682
+ },
10683
+ {
10684
+ type: "function",
10685
+ name: "getIntentDetails",
10686
+ inputs: [
10687
+ {
10688
+ name: "intentHash",
10689
+ type: "bytes32",
10690
+ internalType: "bytes32"
10691
+ }
10692
+ ],
10693
+ outputs: [
10694
+ {
10695
+ name: "intent",
10696
+ type: "tuple",
10697
+ internalType: "struct Intents.Intent",
10698
+ components: [
10699
+ {
10700
+ name: "intentId",
10701
+ type: "uint256",
10702
+ internalType: "uint256"
10703
+ },
10704
+ {
10705
+ name: "creator",
10706
+ type: "address",
10707
+ internalType: "address"
10708
+ },
10709
+ {
10710
+ name: "inputToken",
10711
+ type: "address",
10712
+ internalType: "address"
10713
+ },
10714
+ {
10715
+ name: "outputToken",
10716
+ type: "address",
10717
+ internalType: "address"
10718
+ },
10719
+ {
10720
+ name: "inputAmount",
10721
+ type: "uint256",
10722
+ internalType: "uint256"
10723
+ },
10724
+ {
10725
+ name: "minOutputAmount",
10726
+ type: "uint256",
10727
+ internalType: "uint256"
10728
+ },
10729
+ {
10730
+ name: "deadline",
10731
+ type: "uint256",
10732
+ internalType: "uint256"
10733
+ },
10734
+ {
10735
+ name: "allowPartialFill",
10736
+ type: "bool",
10737
+ internalType: "bool"
10738
+ },
10739
+ {
10740
+ name: "srcChain",
10741
+ type: "uint256",
10742
+ internalType: "uint256"
10743
+ },
10744
+ {
10745
+ name: "dstChain",
10746
+ type: "uint256",
10747
+ internalType: "uint256"
10748
+ },
10749
+ {
10750
+ name: "srcAddress",
10751
+ type: "bytes",
10752
+ internalType: "bytes"
10753
+ },
10754
+ {
10755
+ name: "dstAddress",
10756
+ type: "bytes",
10757
+ internalType: "bytes"
10758
+ },
10759
+ {
10760
+ name: "solver",
10761
+ type: "address",
10762
+ internalType: "address"
10763
+ },
10764
+ {
10765
+ name: "data",
10766
+ type: "bytes",
10767
+ internalType: "bytes"
10768
+ }
10769
+ ]
10770
+ }
10771
+ ],
10772
+ stateMutability: "view"
10652
10773
  }
10653
10774
  ];
10654
10775
 
@@ -13506,29 +13627,6 @@ var universalRouterAbi = [
13506
13627
  }
13507
13628
  ];
13508
13629
 
13509
- // src/shared/utils/deepMerge.ts
13510
- function deepMerge(target, source) {
13511
- const result = { ...target };
13512
- for (const key of Object.keys(source)) {
13513
- const sourceVal = source[key];
13514
- const targetVal = target[key];
13515
- if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null && !Array.isArray(targetVal)) {
13516
- result[key] = deepMerge(
13517
- targetVal,
13518
- sourceVal
13519
- );
13520
- } else if (sourceVal !== void 0) {
13521
- result[key] = sourceVal;
13522
- }
13523
- }
13524
- return result;
13525
- }
13526
-
13527
- // src/shared/config/mergeSodaxConfig.ts
13528
- function mergeSodaxConfig(base2, override) {
13529
- return deepMerge(base2, override);
13530
- }
13531
-
13532
13630
  // src/shared/logger.ts
13533
13631
  var consoleLogger = {
13534
13632
  debug: (message, data) => data ? console.debug(message, data) : console.debug(message),
@@ -13556,16 +13654,85 @@ function resolveLogger(option) {
13556
13654
  return option;
13557
13655
  }
13558
13656
 
13657
+ // src/shared/analytics.ts
13658
+ var noopAnalytics = {
13659
+ isEnabled: () => false,
13660
+ emit: () => {
13661
+ },
13662
+ trackResult: (_feature, _action, run) => run()
13663
+ };
13664
+ var LEVEL_RANK = { basic: 0, detailed: 1 };
13665
+ function normalizeFeatures(features) {
13666
+ if (features === void 0) return null;
13667
+ const map = /* @__PURE__ */ new Map();
13668
+ if (Array.isArray(features)) {
13669
+ for (const feature of features) map.set(feature, true);
13670
+ } else {
13671
+ for (const [feature, scope] of Object.entries(features)) {
13672
+ if (scope === true) map.set(feature, true);
13673
+ else if (scope) map.set(feature, new Set(scope.actions));
13674
+ }
13675
+ }
13676
+ return map;
13677
+ }
13678
+ function resolveAnalytics(option) {
13679
+ if (!option) return noopAnalytics;
13680
+ const { tracker, level: configuredLevel = "basic", features } = option;
13681
+ const maxRank = LEVEL_RANK[configuredLevel];
13682
+ const allow = normalizeFeatures(features);
13683
+ const isEnabled = (feature, action, level = "basic") => {
13684
+ if (LEVEL_RANK[level] > maxRank) return false;
13685
+ if (allow === null) return true;
13686
+ const scope = allow.get(feature);
13687
+ if (scope === void 0) return false;
13688
+ if (scope === true) return true;
13689
+ if (action === void 0) return scope.size > 0;
13690
+ return scope.has(action);
13691
+ };
13692
+ const emit = (feature, action, phase, build, level = "basic") => {
13693
+ if (!isEnabled(feature, action, level)) return;
13694
+ try {
13695
+ tracker({ feature, action, phase, level, data: build?.() });
13696
+ } catch {
13697
+ }
13698
+ };
13699
+ const trackResult = async (feature, action, run, data) => {
13700
+ emit(feature, action, "start", data?.start);
13701
+ try {
13702
+ const result = await run();
13703
+ if (result.ok) {
13704
+ const build = data?.success;
13705
+ emit(feature, action, "success", build ? () => build(result.value) : void 0);
13706
+ } else {
13707
+ const build = data?.failure;
13708
+ emit(feature, action, "failure", build ? () => build(result.error) : void 0);
13709
+ }
13710
+ return result;
13711
+ } catch (error) {
13712
+ emit(feature, action, "failure", () => ({ error: error instanceof Error ? error.message : String(error) }));
13713
+ throw error;
13714
+ }
13715
+ };
13716
+ return { isEnabled, emit, trackResult };
13717
+ }
13718
+
13559
13719
  // src/shared/config/ConfigService.ts
13560
13720
  var ConfigService = class {
13561
13721
  sodax;
13562
- api;
13563
- userConfig;
13722
+ // TODO(config-v2): restore `api` / `userConfig` when initialize() dynamic fetch is re-enabled.
13723
+ // private readonly api: BackendApiService;
13724
+ // private readonly userConfig?: SodaxOptions;
13564
13725
  /**
13565
13726
  * SDK log sink. Resolved once at construction and kept independent of {@link sodax} so that
13566
13727
  * {@link initialize}'s dynamic-config swap never clobbers it. Read by services via `config.logger`.
13567
13728
  */
13568
13729
  logger;
13730
+ /**
13731
+ * Analytics emitter. Resolved once at construction and kept independent of {@link sodax} so that
13732
+ * {@link initialize}'s dynamic-config swap never clobbers it. Read by services via `config.analytics`;
13733
+ * disabled (no-op) unless the consumer passed an `analytics` config to `new Sodax(...)`.
13734
+ */
13735
+ analytics;
13569
13736
  /**
13570
13737
  * Global partner fee. Resolved once at construction and kept independent of {@link sodax} so that
13571
13738
  * {@link initialize}'s dynamic-config swap never clobbers it. The backend never supplies it — it is
@@ -13583,29 +13750,17 @@ var ConfigService = class {
13583
13750
  stakedATokenAddressesSet;
13584
13751
  chainToSupportedTokenAddressMap;
13585
13752
  hubAssetToXTokenMap;
13586
- constructor({ api, config, userConfig, logger, fee }) {
13587
- this.api = api;
13753
+ // `api` / `userConfig` are accepted but unused while initialize()'s dynamic fetch is disabled
13754
+ // (see TODO(config-v2) below); restore their assignments when re-enabling.
13755
+ constructor({ api, config, userConfig, logger, analytics, fee }) {
13588
13756
  this.sodax = config;
13589
- this.userConfig = userConfig;
13590
13757
  this.logger = logger ?? resolveLogger(void 0);
13758
+ this.analytics = analytics ?? noopAnalytics;
13591
13759
  this.fee = fee;
13592
13760
  this.loadSodaxConfigDataStructures(config);
13593
13761
  }
13594
13762
  async initialize() {
13595
13763
  try {
13596
- const result = await this.api.getAllConfig();
13597
- if (!result.ok) return result;
13598
- const response = result.value;
13599
- if (!response.version || response.version < CONFIG_VERSION) {
13600
- this.logger.warn(
13601
- `Dynamic config version is less than the current version, resorting to the default one. Current version: ${CONFIG_VERSION}, response version: ${response.version}`
13602
- );
13603
- } else {
13604
- const next = this.userConfig ? mergeSodaxConfig(response.config, this.userConfig) : response.config;
13605
- this.loadSodaxConfigDataStructures(next);
13606
- this.sodax = next;
13607
- this.initialized = true;
13608
- }
13609
13764
  return { ok: true, value: void 0 };
13610
13765
  } catch (error) {
13611
13766
  return { ok: false, error };
@@ -13638,6 +13793,16 @@ var ConfigService = class {
13638
13793
  getOriginalAssetAddress(chainId, hubAsset) {
13639
13794
  return this.hubAssetToXTokenMap.get(hubAsset.toLowerCase())?.address;
13640
13795
  }
13796
+ /**
13797
+ * Resolves the {@link XToken} descriptor (hub asset, vault, decimals) for a hub-asset address.
13798
+ *
13799
+ * Useful when a caller holds a hub asset directly on Sonic that has no spoke-token entry under
13800
+ * the hub chain — e.g. a partner BTC fee held as the BTC hub asset, which only exists as a spoke
13801
+ * token on Bitcoin. Returns `undefined` when the address is not a known hub asset.
13802
+ */
13803
+ getXTokenFromHubAsset(hubAsset) {
13804
+ return this.hubAssetToXTokenMap.get(hubAsset.toLowerCase());
13805
+ }
13641
13806
  getSpokeTokenFromOriginalAssetAddress(chainId, originalAssetAddress) {
13642
13807
  return this.supportedTokensPerChain.get(chainId)?.find((token) => token.address.toLowerCase() === originalAssetAddress.toLowerCase());
13643
13808
  }
@@ -13865,9 +14030,32 @@ function parseTokenArrayFromJson(input) {
13865
14030
  }
13866
14031
  return tokens;
13867
14032
  }
14033
+
14034
+ // src/shared/utils/deepMerge.ts
14035
+ function deepMerge(target, source) {
14036
+ const result = { ...target };
14037
+ for (const key of Object.keys(source)) {
14038
+ const sourceVal = source[key];
14039
+ const targetVal = target[key];
14040
+ if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null && !Array.isArray(targetVal)) {
14041
+ result[key] = deepMerge(
14042
+ targetVal,
14043
+ sourceVal
14044
+ );
14045
+ } else if (sourceVal !== void 0) {
14046
+ result[key] = sourceVal;
14047
+ }
14048
+ }
14049
+ return result;
14050
+ }
14051
+
14052
+ // src/shared/config/mergeSodaxConfig.ts
14053
+ function mergeSodaxConfig(base2, override) {
14054
+ return deepMerge(base2, override);
14055
+ }
13868
14056
  function encodeContractCalls(calls) {
13869
14057
  return viem.encodeAbiParameters(viem.parseAbiParameters("(address,uint256,bytes)[]"), [
13870
- calls.map((v) => [v.address, v.value, v.data])
14058
+ calls.map((v6) => [v6.address, v6.value, v6.data])
13871
14059
  ]);
13872
14060
  }
13873
14061
  async function waitForTransactionReceipt(hash, provider) {
@@ -15548,6 +15736,32 @@ var BitcoinSpokeService = class {
15548
15736
  throw error;
15549
15737
  }
15550
15738
  }
15739
+ /**
15740
+ * Sign and submit a TRADING-wallet raw transaction — the Bound-built *unsigned* PSBT returned by
15741
+ * `deposit({ raw: true })` / the Swaps API (`createIntent().tx.data`). Signs it with the wallet
15742
+ * provider's key, then sends it to Bound Exchange to co-sign with the second 2-of-2 key and
15743
+ * broadcast. Returns the broadcast tx id.
15744
+ *
15745
+ * This is the client-side completion of the TRADING deposit flow: the backend builds the PSBT
15746
+ * (it can't broadcast — the user's signature is missing), the client signs here, and Bound
15747
+ * co-signs + broadcasts. `relayData` ({ address, payload }) is the relay identity returned by
15748
+ * `createIntent()`; forwarding it lets Bound auto-resubmit a stuck relay. It is **not** recoverable
15749
+ * from `rawTx` (whose `to` is the asset manager, not the hub wallet), so callers must supply it.
15750
+ *
15751
+ * @throws if the chain is not in `TRADING` wallet mode (raw txs only exist in TRADING mode).
15752
+ */
15753
+ async signAndSubmitRawTransaction(params) {
15754
+ if (this.walletMode !== "TRADING") {
15755
+ throw new Error("signAndSubmitRawTransaction requires TRADING wallet mode.");
15756
+ }
15757
+ const { rawTx, walletProvider, relayData, accessToken = this.radfi.accessToken } = params;
15758
+ if (!rawTx?.data || !rawTx?.from) {
15759
+ throw new Error("signAndSubmitRawTransaction: rawTx.data (PSBT) and rawTx.from are required.");
15760
+ }
15761
+ const signedTx = await walletProvider.signTransaction(rawTx.data, false);
15762
+ const signedBase64Tx = normalizePsbtToBase64(signedTx);
15763
+ return this.radfi.requestRadfiSignature({ userAddress: rawTx.from, signedBase64Tx, relayData }, accessToken);
15764
+ }
15551
15765
  /**
15552
15766
  * Build deposit PSBT with embedded cross-chain data
15553
15767
  */
@@ -15817,7 +16031,7 @@ function hexToBytes2(hex) {
15817
16031
  const al = hl / 2;
15818
16032
  if (hl % 2)
15819
16033
  throw new Error("hex string expected, got unpadded hex of length " + hl);
15820
- const array = new Uint8Array(al);
16034
+ const array3 = new Uint8Array(al);
15821
16035
  for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
15822
16036
  const n1 = asciiToBase16(hex.charCodeAt(hi));
15823
16037
  const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
@@ -15825,9 +16039,9 @@ function hexToBytes2(hex) {
15825
16039
  const char = hex[hi] + hex[hi + 1];
15826
16040
  throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
15827
16041
  }
15828
- array[ai] = n1 * 16 + n2;
16042
+ array3[ai] = n1 * 16 + n2;
15829
16043
  }
15830
- return array;
16044
+ return array3;
15831
16045
  }
15832
16046
  function concatBytes(...arrays) {
15833
16047
  let sum = 0;
@@ -16457,22 +16671,22 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
16457
16671
  const byte0 = Uint8Array.of(0);
16458
16672
  const byte1 = Uint8Array.of(1);
16459
16673
  const _maxDrbgIters = 1e3;
16460
- let v = u8n(hashLen);
16674
+ let v6 = u8n(hashLen);
16461
16675
  let k = u8n(hashLen);
16462
16676
  let i = 0;
16463
16677
  const reset = () => {
16464
- v.fill(1);
16678
+ v6.fill(1);
16465
16679
  k.fill(0);
16466
16680
  i = 0;
16467
16681
  };
16468
- const h = (...msgs) => hmacFn(k, concatBytes(v, ...msgs));
16682
+ const h = (...msgs) => hmacFn(k, concatBytes(v6, ...msgs));
16469
16683
  const reseed = (seed = NULL) => {
16470
16684
  k = h(byte0, seed);
16471
- v = h();
16685
+ v6 = h();
16472
16686
  if (seed.length === 0)
16473
16687
  return;
16474
16688
  k = h(byte1, seed);
16475
- v = h();
16689
+ v6 = h();
16476
16690
  };
16477
16691
  const gen = () => {
16478
16692
  if (i++ >= _maxDrbgIters)
@@ -16480,10 +16694,10 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
16480
16694
  let len = 0;
16481
16695
  const out = [];
16482
16696
  while (len < qByteLen) {
16483
- v = h();
16484
- const sl = v.slice();
16697
+ v6 = h();
16698
+ const sl = v6.slice();
16485
16699
  out.push(sl);
16486
- len += v.length;
16700
+ len += v6.length;
16487
16701
  }
16488
16702
  return concatBytes(...out);
16489
16703
  };
@@ -16498,18 +16712,18 @@ function createHmacDrbg(hashLen, qByteLen, hmacFn) {
16498
16712
  };
16499
16713
  return genUntil;
16500
16714
  }
16501
- function validateObject(object, fields = {}, optFields = {}) {
16502
- if (!object || typeof object !== "object")
16715
+ function validateObject(object4, fields = {}, optFields = {}) {
16716
+ if (!object4 || typeof object4 !== "object")
16503
16717
  throw new Error("expected valid options object");
16504
16718
  function checkField(fieldName, expectedType, isOpt) {
16505
- const val = object[fieldName];
16719
+ const val = object4[fieldName];
16506
16720
  if (isOpt && val === void 0)
16507
16721
  return;
16508
16722
  const current = typeof val;
16509
16723
  if (current !== expectedType || val === null)
16510
16724
  throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
16511
16725
  }
16512
- const iter = (f, isOpt) => Object.entries(f).forEach(([k, v]) => checkField(k, v, isOpt));
16726
+ const iter = (f, isOpt) => Object.entries(f).forEach(([k, v6]) => checkField(k, v6, isOpt));
16513
16727
  iter(fields, false);
16514
16728
  iter(optFields, true);
16515
16729
  }
@@ -16548,12 +16762,12 @@ function pow2(x, power, modulo) {
16548
16762
  }
16549
16763
  return res;
16550
16764
  }
16551
- function invert(number, modulo) {
16552
- if (number === _0n2)
16765
+ function invert(number4, modulo) {
16766
+ if (number4 === _0n2)
16553
16767
  throw new Error("invert: expected non-zero number");
16554
16768
  if (modulo <= _0n2)
16555
16769
  throw new Error("invert: expected positive modulus, got " + modulo);
16556
- let a = mod(number, modulo);
16770
+ let a = mod(number4, modulo);
16557
16771
  let b = modulo;
16558
16772
  let x = _0n2, u = _1n2;
16559
16773
  while (a !== _0n2) {
@@ -16580,9 +16794,9 @@ function sqrt3mod4(Fp, n) {
16580
16794
  function sqrt5mod8(Fp, n) {
16581
16795
  const p5div8 = (Fp.ORDER - _5n) / _8n;
16582
16796
  const n2 = Fp.mul(n, _2n);
16583
- const v = Fp.pow(n2, p5div8);
16584
- const nv = Fp.mul(n, v);
16585
- const i = Fp.mul(Fp.mul(nv, _2n), v);
16797
+ const v6 = Fp.pow(n2, p5div8);
16798
+ const nv = Fp.mul(n, v6);
16799
+ const i = Fp.mul(Fp.mul(nv, _2n), v6);
16586
16800
  const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
16587
16801
  assertIsSquare(Fp, root, n);
16588
16802
  return root;
@@ -17057,27 +17271,27 @@ var wNAF = class {
17057
17271
  assert0(n);
17058
17272
  return acc;
17059
17273
  }
17060
- getPrecomputes(W, point, transform) {
17274
+ getPrecomputes(W, point, transform2) {
17061
17275
  let comp = pointPrecomputes.get(point);
17062
17276
  if (!comp) {
17063
17277
  comp = this.precomputeWindow(point, W);
17064
17278
  if (W !== 1) {
17065
- if (typeof transform === "function")
17066
- comp = transform(comp);
17279
+ if (typeof transform2 === "function")
17280
+ comp = transform2(comp);
17067
17281
  pointPrecomputes.set(point, comp);
17068
17282
  }
17069
17283
  }
17070
17284
  return comp;
17071
17285
  }
17072
- cached(point, scalar, transform) {
17286
+ cached(point, scalar, transform2) {
17073
17287
  const W = getW(point);
17074
- return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
17288
+ return this.wNAF(W, this.getPrecomputes(W, point, transform2), scalar);
17075
17289
  }
17076
- unsafe(point, scalar, transform, prev) {
17290
+ unsafe(point, scalar, transform2, prev) {
17077
17291
  const W = getW(point);
17078
17292
  if (W === 1)
17079
17293
  return this._unsafeLadder(point, scalar, prev);
17080
- return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev);
17294
+ return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform2), scalar, prev);
17081
17295
  }
17082
17296
  // We calculate precomputes for elliptic curve point multiplication
17083
17297
  // using windowed method. This specifies window size and
@@ -17164,9 +17378,9 @@ function edwards(params, extraOpts = {}) {
17164
17378
  validateObject(extraOpts, {}, { uvRatio: "function" });
17165
17379
  const MASK = _2n2 << BigInt(Fn.BYTES * 8) - _1n4;
17166
17380
  const modP = (n) => Fp.create(n);
17167
- const uvRatio2 = extraOpts.uvRatio || ((u, v) => {
17381
+ const uvRatio2 = extraOpts.uvRatio || ((u, v6) => {
17168
17382
  try {
17169
- return { isValid: true, value: Fp.sqrt(Fp.div(u, v)) };
17383
+ return { isValid: true, value: Fp.sqrt(Fp.div(u, v6)) };
17170
17384
  } catch (e) {
17171
17385
  return { isValid: false, value: _0n4 };
17172
17386
  }
@@ -17262,8 +17476,8 @@ function edwards(params, extraOpts = {}) {
17262
17476
  aInRange("point.y", y, _0n4, max);
17263
17477
  const y2 = modP(y * y);
17264
17478
  const u = modP(y2 - _1n4);
17265
- const v = modP(d * y2 - a);
17266
- let { isValid, value: x } = uvRatio2(u, v);
17479
+ const v6 = modP(d * y2 - a);
17480
+ let { isValid, value: x } = uvRatio2(u, v6);
17267
17481
  if (!isValid)
17268
17482
  throw new Error("bad point: invalid y coordinate");
17269
17483
  const isXOdd = (x & _1n4) === _1n4;
@@ -17603,13 +17817,13 @@ function adjustScalarBytes(bytes) {
17603
17817
  return bytes;
17604
17818
  }
17605
17819
  var ED25519_SQRT_M1 = /* @__PURE__ */ BigInt("19681161376707505956807079304988542015446066515923890162744021073123829784752");
17606
- function uvRatio(u, v) {
17820
+ function uvRatio(u, v6) {
17607
17821
  const P = ed25519_CURVE_p;
17608
- const v3 = mod(v * v * v, P);
17609
- const v7 = mod(v3 * v3 * v, P);
17822
+ const v32 = mod(v6 * v6 * v6, P);
17823
+ const v7 = mod(v32 * v32 * v6, P);
17610
17824
  const pow = ed25519_pow_2_252_3(u * v7).pow_p_5_8;
17611
- let x = mod(u * v3 * pow, P);
17612
- const vx2 = mod(v * x * x, P);
17825
+ let x = mod(u * v32 * pow, P);
17826
+ const vx2 = mod(v6 * x * x, P);
17613
17827
  const root1 = x;
17614
17828
  const root2 = mod(x * ED25519_SQRT_M1, P);
17615
17829
  const useRoot1 = vx2 === u;
@@ -17940,7 +18154,7 @@ function findSeatPrice(validators, maxNumberOfSeats, minimumStakeRatio, protocol
17940
18154
  return findSeatPriceForProtocolAfter49(validators, maxNumberOfSeats, minimumStakeRatio);
17941
18155
  }
17942
18156
  function findSeatPriceForProtocolBefore49(validators, numSeats) {
17943
- const stakes = validators.map((v) => BigInt(v.stake)).sort(sortBigIntAsc);
18157
+ const stakes = validators.map((v6) => BigInt(v6.stake)).sort(sortBigIntAsc);
17944
18158
  const num = BigInt(numSeats);
17945
18159
  const stakesSum = stakes.reduce((a, b) => a + b);
17946
18160
  if (stakesSum < num) {
@@ -17969,7 +18183,7 @@ function findSeatPriceForProtocolAfter49(validators, maxNumberOfSeats, minimumSt
17969
18183
  if (minimumStakeRatio.length != 2) {
17970
18184
  throw Error("minimumStakeRatio should have 2 elements");
17971
18185
  }
17972
- const stakes = validators.map((v) => BigInt(v.stake)).sort(sortBigIntAsc);
18186
+ const stakes = validators.map((v6) => BigInt(v6.stake)).sort(sortBigIntAsc);
17973
18187
  const stakesSum = stakes.reduce((a, b) => a + b);
17974
18188
  if (validators.length < maxNumberOfSeats) {
17975
18189
  return stakesSum * BigInt(minimumStakeRatio[0]) / BigInt(minimumStakeRatio[1]);
@@ -18141,10 +18355,10 @@ var DER = {
18141
18355
  if (length < 128)
18142
18356
  throw new E("tlv.decode(long): not minimal encoding");
18143
18357
  }
18144
- const v = data.subarray(pos, pos + length);
18145
- if (v.length !== length)
18358
+ const v6 = data.subarray(pos, pos + length);
18359
+ if (v6.length !== length)
18146
18360
  throw new E("tlv.decode: wrong value length");
18147
- return { v, l: data.subarray(pos + length) };
18361
+ return { v: v6, l: data.subarray(pos + length) };
18148
18362
  }
18149
18363
  },
18150
18364
  // https://crypto.stackexchange.com/a/57734 Leftmost bit of first byte is 'negative' flag,
@@ -18712,9 +18926,9 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
18712
18926
  extraEntropy: false
18713
18927
  };
18714
18928
  const hasLargeCofactor = CURVE_ORDER * _2n4 < Fp.ORDER;
18715
- function isBiggerThanHalfOrder(number) {
18929
+ function isBiggerThanHalfOrder(number4) {
18716
18930
  const HALF = CURVE_ORDER >> _1n6;
18717
- return number > HALF;
18931
+ return number4 > HALF;
18718
18932
  }
18719
18933
  function validateRS(title, num) {
18720
18934
  if (!Fn.isValidNot0(num))
@@ -18891,14 +19105,14 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
18891
19105
  return false;
18892
19106
  const { r, s } = sig;
18893
19107
  const h = bits2int_modN(message);
18894
- const is = Fn.inv(s);
18895
- const u1 = Fn.create(h * is);
18896
- const u2 = Fn.create(r * is);
19108
+ const is2 = Fn.inv(s);
19109
+ const u1 = Fn.create(h * is2);
19110
+ const u2 = Fn.create(r * is2);
18897
19111
  const R = Point.BASE.multiplyUnsafe(u1).add(P.multiplyUnsafe(u2));
18898
19112
  if (R.is0())
18899
19113
  return false;
18900
- const v = Fn.create(R.x);
18901
- return v === r;
19114
+ const v6 = Fn.create(R.x);
19115
+ return v6 === r;
18902
19116
  } catch (e) {
18903
19117
  return false;
18904
19118
  }
@@ -26244,13 +26458,18 @@ var SwapService = class {
26244
26458
  solver;
26245
26459
  partnerFee;
26246
26460
  relayerApiEndpoint;
26247
- constructor({ config, hubProvider, spoke }) {
26461
+ // backend swaps-API client + opt-in 2-step submit-tx flag
26462
+ backendApi;
26463
+ useBackendSubmitTx;
26464
+ constructor({ config, hubProvider, spoke, backendApi, useBackendSubmitTx }) {
26248
26465
  this.solver = config.solver;
26249
26466
  this.partnerFee = config.swapPartnerFee;
26250
26467
  this.relayerApiEndpoint = config.relay.relayerApiEndpoint;
26251
26468
  this.config = config;
26252
26469
  this.hubProvider = hubProvider;
26253
26470
  this.spoke = spoke;
26471
+ this.backendApi = backendApi;
26472
+ this.useBackendSubmitTx = useBackendSubmitTx ?? false;
26254
26473
  }
26255
26474
  /**
26256
26475
  * Estimates the gas cost for a raw (unsigned) transaction on a spoke chain.
@@ -26394,14 +26613,22 @@ var SwapService = class {
26394
26613
  /**
26395
26614
  * Executes a full end-to-end cross-chain swap.
26396
26615
  *
26397
- * Orchestrates the complete swap lifecycle:
26398
- * 1. Calls `createIntent` to submit the intent transaction on the source spoke chain.
26399
- * 2. Verifies the spoke transaction landed on-chain.
26400
- * 3. For non-hub source chains: submits the spoke tx to the relayer and waits for the
26401
- * relay packet to land on the hub (Sonic). Skipped when `srcChainKey` is the hub.
26402
- * 4. Calls `postExecution` to notify the solver, triggering it to fill the intent.
26403
- *
26404
- * @param _params - Swap action params including intent parameters, wallet provider, and optional timeout.
26616
+ * Orchestrates the complete swap lifecycle. `createIntent` first submits the intent transaction on
26617
+ * the source spoke chain; completion then runs via one of two paths, both bounded by a single
26618
+ * shared `timeout` budget:
26619
+ *
26620
+ * - **Client-side (default), {@link fallbackSwapSteps}:** verifies the spoke tx landed on-chain,
26621
+ * relays it to the hub (Sonic) and waits for the packet skipped when `srcChainKey` is the hub,
26622
+ * where the spoke tx already is the hub tx — then calls `postExecution` to notify the solver,
26623
+ * triggering it to fill the intent.
26624
+ * - **Backend 2-step (opt-in via `swapsOptions.useBackendSubmitTx`), {@link submitTx}:** hands the
26625
+ * broadcast tx to the swaps API, which verifies, relays and post-executes server-side, then polls
26626
+ * for completion. On ANY non-success it transparently falls back to the client-side path above —
26627
+ * safe because re-relaying / re-posting an already-processed swap is idempotent (no double-fill).
26628
+ *
26629
+ * @param _params - Swap action params including intent parameters, wallet provider, and an optional
26630
+ * `timeout` — the single shared budget for the whole completion flow (relay/poll plus any
26631
+ * fallback), so total wall-clock never exceeds it.
26405
26632
  * @returns A `Result<SwapResponse, SwapError>`. On success:
26406
26633
  * - `solverExecutionResponse` — solver acknowledgement (`{ answer: 'OK', intent_hash }`).
26407
26634
  * - `intent` — the on-chain intent object that was created.
@@ -26425,63 +26652,179 @@ var SwapService = class {
26425
26652
  const { params } = _params;
26426
26653
  const srcChainKey = params.srcChainKey;
26427
26654
  const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey };
26428
- try {
26429
- const timeout = _params.timeout;
26430
- const createIntentResult = await this.createIntent(_params);
26431
- if (!createIntentResult.ok) {
26432
- return { ok: false, error: createIntentResult.error };
26655
+ return this.config.analytics.trackResult(
26656
+ "swap",
26657
+ "swap",
26658
+ async () => {
26659
+ try {
26660
+ const createIntentResult = await this.createIntent(_params);
26661
+ if (!createIntentResult.ok) {
26662
+ return { ok: false, error: createIntentResult.error };
26663
+ }
26664
+ const created = createIntentResult.value;
26665
+ const deadline = Date.now() + (_params.timeout ?? DEFAULT_RELAY_TX_TIMEOUT);
26666
+ if (this.useBackendSubmitTx) {
26667
+ const submitted = await this.submitTx(_params, created, deadline);
26668
+ if (submitted.ok) return submitted;
26669
+ this.config.logger.warn(
26670
+ "[swap] backend submit-tx did not complete; falling back to the client-side relay",
26671
+ {
26672
+ error: submitted.error
26673
+ }
26674
+ );
26675
+ }
26676
+ return this.fallbackSwapSteps(_params, created, deadline);
26677
+ } catch (error) {
26678
+ if (isSwapError(error)) return { ok: false, error };
26679
+ return {
26680
+ ok: false,
26681
+ error: unknownFailed("swap", error, { ...baseCtx, action: "swap" })
26682
+ };
26683
+ }
26684
+ },
26685
+ {
26686
+ start: () => ({
26687
+ srcChainKey,
26688
+ dstChainKey: params.dstChainKey,
26689
+ srcAddress: params.srcAddress,
26690
+ dstAddress: params.dstAddress
26691
+ }),
26692
+ success: (value) => ({
26693
+ srcChainKey,
26694
+ dstChainKey: params.dstChainKey,
26695
+ srcTxHash: value.intentDeliveryInfo.srcTxHash,
26696
+ dstTxHash: value.intentDeliveryInfo.dstTxHash
26697
+ }),
26698
+ failure: (error) => ({ code: error.code })
26433
26699
  }
26434
- const { tx: spokeTxHash, intent, relayData } = createIntentResult.value;
26435
- const verifyTxHashResult = await this.spoke.verifyTxHash({
26436
- txHash: spokeTxHash,
26437
- chainKey: srcChainKey
26700
+ );
26701
+ }
26702
+ /**
26703
+ * Client-side swap completion (the default path): relay the broadcast intent tx to the hub — or
26704
+ * use it directly when the source IS the hub — then notify the solver via post-execution and
26705
+ * build the {@link SwapResponse}. Extracted verbatim from `swap()` so the opt-in backend 2-step
26706
+ * path ({@link submitTx}) can fall back to it on any non-success.
26707
+ */
26708
+ async fallbackSwapSteps(_params, created, deadline) {
26709
+ const { params } = _params;
26710
+ const srcChainKey = params.srcChainKey;
26711
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey };
26712
+ const { tx: spokeTxHash, intent, relayData } = created;
26713
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
26714
+ txHash: created.tx,
26715
+ chainKey: srcChainKey
26716
+ });
26717
+ if (!verifyTxHashResult.ok) {
26718
+ return { ok: false, error: verifyFailed("swap", verifyTxHashResult.error, { ...baseCtx, action: "swap" }) };
26719
+ }
26720
+ let dstIntentTxHash;
26721
+ if (isHubChainKeyType(srcChainKey)) {
26722
+ dstIntentTxHash = spokeTxHash;
26723
+ } else {
26724
+ const packet = await relayTxAndWaitPacket({
26725
+ srcTxHash: spokeTxHash,
26726
+ data: relayData,
26727
+ chainKey: srcChainKey,
26728
+ relayerApiEndpoint: this.relayerApiEndpoint,
26729
+ // Remaining shared budget: ≈ full `timeout` on the flag-off path (called immediately), or
26730
+ // the reserve `submitTx` left on the backend path. Floor keeps a stalled-backend fallback viable.
26731
+ timeout: Math.max(deadline - Date.now(), 5e3)
26438
26732
  });
26439
- if (!verifyTxHashResult.ok) {
26440
- return { ok: false, error: verifyFailed("swap", verifyTxHashResult.error, { ...baseCtx, action: "swap" }) };
26733
+ if (!packet.ok) {
26734
+ return { ok: false, error: mapRelayFailure(packet.error, { feature: "swap", action: "swap", ...baseCtx }) };
26441
26735
  }
26442
- let dstIntentTxHash;
26443
- if (isHubChainKeyType(srcChainKey)) {
26444
- dstIntentTxHash = spokeTxHash;
26445
- } else {
26446
- const packet = await relayTxAndWaitPacket({
26736
+ dstIntentTxHash = packet.value.dst_tx_hash;
26737
+ }
26738
+ const postExecResult = await this.postExecution({
26739
+ intent_tx_hash: dstIntentTxHash
26740
+ });
26741
+ if (!postExecResult.ok) {
26742
+ return { ok: false, error: postExecResult.error };
26743
+ }
26744
+ return {
26745
+ ok: true,
26746
+ value: {
26747
+ solverExecutionResponse: postExecResult.value,
26748
+ intent,
26749
+ intentDeliveryInfo: {
26750
+ srcChainKey,
26447
26751
  srcTxHash: spokeTxHash,
26448
- data: relayData,
26449
- chainKey: srcChainKey,
26450
- relayerApiEndpoint: this.relayerApiEndpoint,
26451
- timeout
26452
- });
26453
- if (!packet.ok) {
26454
- return { ok: false, error: mapRelayFailure(packet.error, { feature: "swap", action: "swap", ...baseCtx }) };
26752
+ srcAddress: params.srcAddress,
26753
+ dstChainKey: params.dstChainKey,
26754
+ dstTxHash: dstIntentTxHash,
26755
+ dstAddress: params.dstAddress
26455
26756
  }
26456
- dstIntentTxHash = packet.value.dst_tx_hash;
26457
26757
  }
26458
- const postExecResult = await this.postExecution({
26459
- intent_tx_hash: dstIntentTxHash
26758
+ };
26759
+ }
26760
+ /**
26761
+ * Backend 2-step swap path (opt-in via `swapsOptions.useBackendSubmitTx`): hand the broadcast
26762
+ * intent tx to the swaps API (`POST /swaps/submit-tx`); the backend relays + post-executes
26763
+ * server-side. Polls `getSubmitTxStatus` until `executed`, then reconstructs the same
26764
+ * {@link SwapResponse} the client-side path returns (`result.dstIntentTxHash` → delivery info,
26765
+ * `result.intent_hash` → solver response).
26766
+ *
26767
+ * Never throws — returns `{ ok: false }` on any non-success (submit `!ok`, terminal `failed` /
26768
+ * abandoned, or poll timeout) so `swap()` falls back to {@link fallbackSwapSteps}.
26769
+ *
26770
+ * Falling back is safe: re-relaying / re-posting an already-processed swap is idempotent — the
26771
+ * relay dedups and returns the existing `executed` packet, and the solver re-affirms the intent
26772
+ * (no double-fill). Verified live by `e2e-tests/e2e-relay.test.ts`. Polling stops at
26773
+ * `deadline - reserve` so the fallback keeps a guaranteed slice of the shared `swap` budget.
26774
+ */
26775
+ async submitTx(_params, created, deadline) {
26776
+ const { params } = _params;
26777
+ const srcChainKey = params.srcChainKey;
26778
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey };
26779
+ const { tx: spokeTxHash, intent, relayData } = created;
26780
+ const submitTxFailed = (cause) => ({
26781
+ ok: false,
26782
+ error: executionFailed("swap", cause, { ...baseCtx, action: "swap" })
26783
+ });
26784
+ try {
26785
+ const submitted = await this.backendApi.swaps.submitTx({
26786
+ txHash: spokeTxHash,
26787
+ srcChainKey,
26788
+ walletAddress: params.srcAddress,
26789
+ intent,
26790
+ relayData: relayData.payload
26460
26791
  });
26461
- if (!postExecResult.ok) {
26462
- return { ok: false, error: postExecResult.error };
26463
- }
26464
- return {
26465
- ok: true,
26466
- value: {
26467
- solverExecutionResponse: postExecResult.value,
26468
- intent,
26469
- intentDeliveryInfo: {
26470
- srcChainKey,
26471
- srcTxHash: spokeTxHash,
26472
- srcAddress: params.srcAddress,
26473
- dstChainKey: params.dstChainKey,
26474
- dstTxHash: dstIntentTxHash,
26475
- dstAddress: params.dstAddress
26792
+ if (!submitted.ok) return submitTxFailed(submitted.error);
26793
+ const reserveMs = Math.min(Math.ceil((deadline - Date.now()) / 3), 2e4);
26794
+ const pollDeadline = deadline - reserveMs;
26795
+ const pollIntervalMs = 1e3;
26796
+ while (Date.now() < pollDeadline) {
26797
+ const statusResult = await this.backendApi.swaps.getSubmitTxStatus({ txHash: spokeTxHash, srcChainKey });
26798
+ if (statusResult.ok) {
26799
+ const { status, result, failureReason, abandonedAt } = statusResult.value.data;
26800
+ if (status === "executed" && result?.dstIntentTxHash && result.intent_hash) {
26801
+ return {
26802
+ ok: true,
26803
+ value: {
26804
+ // Backend serializes the hex intent_hash as a plain string; brand it at the boundary.
26805
+ solverExecutionResponse: { answer: "OK", intent_hash: result.intent_hash },
26806
+ intent,
26807
+ intentDeliveryInfo: {
26808
+ srcChainKey,
26809
+ srcTxHash: spokeTxHash,
26810
+ srcAddress: params.srcAddress,
26811
+ dstChainKey: params.dstChainKey,
26812
+ dstTxHash: result.dstIntentTxHash,
26813
+ dstAddress: params.dstAddress
26814
+ }
26815
+ }
26816
+ };
26817
+ }
26818
+ if (status === "failed" || abandonedAt) {
26819
+ const reason = failureReason ? `: ${failureReason}` : "";
26820
+ return submitTxFailed(new Error(`backend submit-tx ${status}${reason}`));
26476
26821
  }
26477
26822
  }
26478
- };
26823
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
26824
+ }
26825
+ return submitTxFailed(new Error("backend submit-tx polling timed out before reaching executed"));
26479
26826
  } catch (error) {
26480
- if (isSwapError(error)) return { ok: false, error };
26481
- return {
26482
- ok: false,
26483
- error: unknownFailed("swap", error, { ...baseCtx, action: "swap" })
26484
- };
26827
+ return { ok: false, error: unknownFailed("swap", error, { ...baseCtx, action: "swap" }) };
26485
26828
  }
26486
26829
  }
26487
26830
  /**
@@ -28528,72 +28871,94 @@ var MigrationService = class {
28528
28871
  * }
28529
28872
  */
28530
28873
  async migratebnUSD(_params) {
28531
- const { params, timeout } = _params;
28532
- const baseCtx = {
28533
- srcChainKey: params.srcChainKey,
28534
- dstChainKey: params.dstChainKey,
28535
- action: "migratebnUSD"
28536
- };
28537
- try {
28538
- const intentResult = await this.createMigratebnUSDIntent(_params);
28539
- if (!intentResult.ok) return { ok: false, error: intentResult.error };
28540
- const { tx: spokeTxHash, relayData: extraData } = intentResult.value;
28541
- const verifyTxHashResult = await this.spoke.verifyTxHash({
28542
- txHash: spokeTxHash,
28543
- chainKey: params.srcChainKey
28544
- });
28545
- if (!verifyTxHashResult.ok) {
28546
- return {
28547
- ok: false,
28548
- error: verifyFailed("migration", verifyTxHashResult.error, baseCtx)
28549
- };
28550
- }
28551
- const packetResult = await relayTxAndWaitPacket({
28552
- srcTxHash: spokeTxHash,
28553
- data: extraData,
28554
- chainKey: params.srcChainKey,
28555
- relayerApiEndpoint: this.relayerApiEndpoint,
28556
- timeout
28557
- });
28558
- if (!packetResult.ok) {
28559
- return {
28560
- ok: false,
28561
- error: mapRelayFailure(packetResult.error, {
28562
- feature: "migration",
28563
- action: baseCtx.action,
28564
- srcChainKey: baseCtx.srcChainKey,
28565
- dstChainKey: baseCtx.dstChainKey
28566
- })
28874
+ return this.config.analytics.trackResult(
28875
+ "migration",
28876
+ "migratebnUSD",
28877
+ async () => {
28878
+ const { params, timeout } = _params;
28879
+ const baseCtx = {
28880
+ srcChainKey: params.srcChainKey,
28881
+ dstChainKey: params.dstChainKey,
28882
+ action: "migratebnUSD"
28567
28883
  };
28568
- }
28569
- if (!(params.srcChainKey === ChainKeys.SONIC_MAINNET || params.dstChainKey === ChainKeys.SONIC_MAINNET)) {
28570
- const execResult = await waitUntilIntentExecuted({
28571
- intentRelayChainId: getIntentRelayChainId(ChainKeys.SONIC_MAINNET).toString(),
28572
- srcTxHash: packetResult.value.dst_tx_hash,
28573
- timeout,
28574
- apiUrl: this.relayerApiEndpoint
28575
- });
28576
- if (!execResult.ok) {
28884
+ try {
28885
+ const intentResult = await this.createMigratebnUSDIntent(_params);
28886
+ if (!intentResult.ok) return { ok: false, error: intentResult.error };
28887
+ const { tx: spokeTxHash, relayData: extraData } = intentResult.value;
28888
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
28889
+ txHash: spokeTxHash,
28890
+ chainKey: params.srcChainKey
28891
+ });
28892
+ if (!verifyTxHashResult.ok) {
28893
+ return {
28894
+ ok: false,
28895
+ error: verifyFailed("migration", verifyTxHashResult.error, baseCtx)
28896
+ };
28897
+ }
28898
+ const packetResult = await relayTxAndWaitPacket({
28899
+ srcTxHash: spokeTxHash,
28900
+ data: extraData,
28901
+ chainKey: params.srcChainKey,
28902
+ relayerApiEndpoint: this.relayerApiEndpoint,
28903
+ timeout
28904
+ });
28905
+ if (!packetResult.ok) {
28906
+ return {
28907
+ ok: false,
28908
+ error: mapRelayFailure(packetResult.error, {
28909
+ feature: "migration",
28910
+ action: baseCtx.action,
28911
+ srcChainKey: baseCtx.srcChainKey,
28912
+ dstChainKey: baseCtx.dstChainKey
28913
+ })
28914
+ };
28915
+ }
28916
+ if (!(params.srcChainKey === ChainKeys.SONIC_MAINNET || params.dstChainKey === ChainKeys.SONIC_MAINNET)) {
28917
+ const execResult = await waitUntilIntentExecuted({
28918
+ intentRelayChainId: getIntentRelayChainId(ChainKeys.SONIC_MAINNET).toString(),
28919
+ srcTxHash: packetResult.value.dst_tx_hash,
28920
+ timeout,
28921
+ apiUrl: this.relayerApiEndpoint
28922
+ });
28923
+ if (!execResult.ok) {
28924
+ return {
28925
+ ok: false,
28926
+ error: mapRelayFailure(execResult.error, {
28927
+ feature: "migration",
28928
+ action: baseCtx.action,
28929
+ srcChainKey: baseCtx.srcChainKey,
28930
+ dstChainKey: baseCtx.dstChainKey,
28931
+ phase: "destinationExecution"
28932
+ })
28933
+ };
28934
+ }
28935
+ }
28936
+ return { ok: true, value: { srcChainTxHash: spokeTxHash, dstChainTxHash: packetResult.value.dst_tx_hash } };
28937
+ } catch (error) {
28938
+ if (isMigrateOrchestrationError(error)) return { ok: false, error };
28577
28939
  return {
28578
28940
  ok: false,
28579
- error: mapRelayFailure(execResult.error, {
28580
- feature: "migration",
28581
- action: baseCtx.action,
28582
- srcChainKey: baseCtx.srcChainKey,
28583
- dstChainKey: baseCtx.dstChainKey,
28584
- phase: "destinationExecution"
28585
- })
28941
+ error: executionFailed("migration", error, baseCtx)
28586
28942
  };
28587
28943
  }
28944
+ },
28945
+ {
28946
+ start: () => ({
28947
+ srcChainKey: _params.params.srcChainKey,
28948
+ dstChainKey: _params.params.dstChainKey,
28949
+ srcAddress: _params.params.srcAddress,
28950
+ dstAddress: _params.params.dstAddress,
28951
+ srcbnUSD: _params.params.srcbnUSD,
28952
+ dstbnUSD: _params.params.dstbnUSD,
28953
+ amount: _params.params.amount
28954
+ }),
28955
+ success: (value) => ({
28956
+ srcChainTxHash: value.srcChainTxHash,
28957
+ dstChainTxHash: value.dstChainTxHash
28958
+ }),
28959
+ failure: (error) => ({ code: error.code })
28588
28960
  }
28589
- return { ok: true, value: { srcChainTxHash: spokeTxHash, dstChainTxHash: packetResult.value.dst_tx_hash } };
28590
- } catch (error) {
28591
- if (isMigrateOrchestrationError(error)) return { ok: false, error };
28592
- return {
28593
- ok: false,
28594
- error: executionFailed("migration", error, baseCtx)
28595
- };
28596
- }
28961
+ );
28597
28962
  }
28598
28963
  /**
28599
28964
  * Migrates ICX or wICX tokens from ICON to SODA on the hub chain (Sonic), including relay.
@@ -28608,37 +28973,57 @@ var MigrationService = class {
28608
28973
  * check fails, the deposit reverts, or the relay times out.
28609
28974
  */
28610
28975
  async migrateIcxToSoda(_params) {
28611
- const { timeout } = _params;
28612
- const baseCtx = { srcChainKey: _params.params.srcChainKey, action: "migrateIcxToSoda" };
28613
- try {
28614
- const txResult = await this.createMigrateIcxToSodaIntent(_params);
28615
- if (!txResult.ok) return { ok: false, error: txResult.error };
28616
- const { tx, relayData } = txResult.value;
28617
- const packetResult = await relayTxAndWaitPacket({
28618
- srcTxHash: tx,
28619
- data: relayData,
28620
- chainKey: _params.params.srcChainKey,
28621
- relayerApiEndpoint: this.relayerApiEndpoint,
28622
- timeout
28623
- });
28624
- if (!packetResult.ok) {
28625
- return {
28626
- ok: false,
28627
- error: mapRelayFailure(packetResult.error, {
28628
- feature: "migration",
28629
- action: baseCtx.action,
28630
- srcChainKey: baseCtx.srcChainKey
28631
- })
28632
- };
28976
+ return this.config.analytics.trackResult(
28977
+ "migration",
28978
+ "migrateIcxToSoda",
28979
+ async () => {
28980
+ const { timeout } = _params;
28981
+ const baseCtx = { srcChainKey: _params.params.srcChainKey, action: "migrateIcxToSoda" };
28982
+ try {
28983
+ const txResult = await this.createMigrateIcxToSodaIntent(_params);
28984
+ if (!txResult.ok) return { ok: false, error: txResult.error };
28985
+ const { tx, relayData } = txResult.value;
28986
+ const packetResult = await relayTxAndWaitPacket({
28987
+ srcTxHash: tx,
28988
+ data: relayData,
28989
+ chainKey: _params.params.srcChainKey,
28990
+ relayerApiEndpoint: this.relayerApiEndpoint,
28991
+ timeout
28992
+ });
28993
+ if (!packetResult.ok) {
28994
+ return {
28995
+ ok: false,
28996
+ error: mapRelayFailure(packetResult.error, {
28997
+ feature: "migration",
28998
+ action: baseCtx.action,
28999
+ srcChainKey: baseCtx.srcChainKey
29000
+ })
29001
+ };
29002
+ }
29003
+ return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
29004
+ } catch (error) {
29005
+ if (isMigrateOrchestrationError(error)) return { ok: false, error };
29006
+ return {
29007
+ ok: false,
29008
+ error: executionFailed("migration", error, baseCtx)
29009
+ };
29010
+ }
29011
+ },
29012
+ {
29013
+ start: () => ({
29014
+ srcChainKey: _params.params.srcChainKey,
29015
+ srcAddress: _params.params.srcAddress,
29016
+ dstAddress: _params.params.dstAddress,
29017
+ address: _params.params.address,
29018
+ amount: _params.params.amount
29019
+ }),
29020
+ success: (value) => ({
29021
+ srcChainTxHash: value.srcChainTxHash,
29022
+ dstChainTxHash: value.dstChainTxHash
29023
+ }),
29024
+ failure: (error) => ({ code: error.code })
28633
29025
  }
28634
- return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
28635
- } catch (error) {
28636
- if (isMigrateOrchestrationError(error)) return { ok: false, error };
28637
- return {
28638
- ok: false,
28639
- error: executionFailed("migration", error, baseCtx)
28640
- };
28641
- }
29026
+ );
28642
29027
  }
28643
29028
  /**
28644
29029
  * Reverts a previous ICX→SODA migration by swapping SODA back to wICX on the hub and
@@ -28656,37 +29041,56 @@ var MigrationService = class {
28656
29041
  * Sonic deposit transaction and `dstChainTxHash` is the hub-side packet receipt.
28657
29042
  */
28658
29043
  async revertMigrateSodaToIcx(_params) {
28659
- const { timeout } = _params;
28660
- const baseCtx = { srcChainKey: ChainKeys.SONIC_MAINNET, action: "revertMigrateSodaToIcx" };
28661
- try {
28662
- const txResult = await this.createRevertSodaToIcxMigrationIntent(_params);
28663
- if (!txResult.ok) return { ok: false, error: txResult.error };
28664
- const { tx, relayData } = txResult.value;
28665
- const packetResult = await relayTxAndWaitPacket({
28666
- srcTxHash: tx,
28667
- data: relayData,
28668
- chainKey: ChainKeys.SONIC_MAINNET,
28669
- relayerApiEndpoint: this.relayerApiEndpoint,
28670
- timeout
28671
- });
28672
- if (!packetResult.ok) {
28673
- return {
28674
- ok: false,
28675
- error: mapRelayFailure(packetResult.error, {
28676
- feature: "migration",
28677
- action: baseCtx.action,
28678
- srcChainKey: baseCtx.srcChainKey
28679
- })
28680
- };
29044
+ return this.config.analytics.trackResult(
29045
+ "migration",
29046
+ "revertMigrateSodaToIcx",
29047
+ async () => {
29048
+ const { timeout } = _params;
29049
+ const baseCtx = { srcChainKey: ChainKeys.SONIC_MAINNET, action: "revertMigrateSodaToIcx" };
29050
+ try {
29051
+ const txResult = await this.createRevertSodaToIcxMigrationIntent(_params);
29052
+ if (!txResult.ok) return { ok: false, error: txResult.error };
29053
+ const { tx, relayData } = txResult.value;
29054
+ const packetResult = await relayTxAndWaitPacket({
29055
+ srcTxHash: tx,
29056
+ data: relayData,
29057
+ chainKey: ChainKeys.SONIC_MAINNET,
29058
+ relayerApiEndpoint: this.relayerApiEndpoint,
29059
+ timeout
29060
+ });
29061
+ if (!packetResult.ok) {
29062
+ return {
29063
+ ok: false,
29064
+ error: mapRelayFailure(packetResult.error, {
29065
+ feature: "migration",
29066
+ action: baseCtx.action,
29067
+ srcChainKey: baseCtx.srcChainKey
29068
+ })
29069
+ };
29070
+ }
29071
+ return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
29072
+ } catch (error) {
29073
+ if (isRevertMigrationOrchestrationError(error)) return { ok: false, error };
29074
+ return {
29075
+ ok: false,
29076
+ error: executionFailed("migration", error, baseCtx)
29077
+ };
29078
+ }
29079
+ },
29080
+ {
29081
+ start: () => ({
29082
+ srcChainKey: _params.params.srcChainKey,
29083
+ srcAddress: _params.params.srcAddress,
29084
+ dstAddress: _params.params.dstAddress,
29085
+ amount: _params.params.amount
29086
+ }),
29087
+ success: (value) => ({
29088
+ srcChainTxHash: value.srcChainTxHash,
29089
+ dstChainTxHash: value.dstChainTxHash
29090
+ }),
29091
+ failure: (error) => ({ code: error.code })
28681
29092
  }
28682
- return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
28683
- } catch (error) {
28684
- if (isRevertMigrationOrchestrationError(error)) return { ok: false, error };
28685
- return {
28686
- ok: false,
28687
- error: executionFailed("migration", error, baseCtx)
28688
- };
28689
- }
29093
+ );
28690
29094
  }
28691
29095
  /**
28692
29096
  * Migrates BALN tokens from ICON to SODA on the hub chain (Sonic), including relay.
@@ -28703,37 +29107,58 @@ var MigrationService = class {
28703
29107
  * ICON deposit transaction and `dstChainTxHash` is the hub-side packet receipt.
28704
29108
  */
28705
29109
  async migrateBaln(_params) {
28706
- const { timeout } = _params;
28707
- const baseCtx = { srcChainKey: ChainKeys.ICON_MAINNET, action: "migrateBaln" };
28708
- try {
28709
- const txResult = await this.createMigrateBalnIntent(_params);
28710
- if (!txResult.ok) return { ok: false, error: txResult.error };
28711
- const { tx, relayData } = txResult.value;
28712
- const packetResult = await relayTxAndWaitPacket({
28713
- srcTxHash: tx,
28714
- data: relayData,
28715
- chainKey: ChainKeys.ICON_MAINNET,
28716
- relayerApiEndpoint: this.relayerApiEndpoint,
28717
- timeout
28718
- });
28719
- if (!packetResult.ok) {
28720
- return {
28721
- ok: false,
28722
- error: mapRelayFailure(packetResult.error, {
28723
- feature: "migration",
28724
- action: baseCtx.action,
28725
- srcChainKey: baseCtx.srcChainKey
28726
- })
28727
- };
29110
+ return this.config.analytics.trackResult(
29111
+ "migration",
29112
+ "migrateBaln",
29113
+ async () => {
29114
+ const { timeout } = _params;
29115
+ const baseCtx = { srcChainKey: ChainKeys.ICON_MAINNET, action: "migrateBaln" };
29116
+ try {
29117
+ const txResult = await this.createMigrateBalnIntent(_params);
29118
+ if (!txResult.ok) return { ok: false, error: txResult.error };
29119
+ const { tx, relayData } = txResult.value;
29120
+ const packetResult = await relayTxAndWaitPacket({
29121
+ srcTxHash: tx,
29122
+ data: relayData,
29123
+ chainKey: ChainKeys.ICON_MAINNET,
29124
+ relayerApiEndpoint: this.relayerApiEndpoint,
29125
+ timeout
29126
+ });
29127
+ if (!packetResult.ok) {
29128
+ return {
29129
+ ok: false,
29130
+ error: mapRelayFailure(packetResult.error, {
29131
+ feature: "migration",
29132
+ action: baseCtx.action,
29133
+ srcChainKey: baseCtx.srcChainKey
29134
+ })
29135
+ };
29136
+ }
29137
+ return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
29138
+ } catch (error) {
29139
+ if (isMigrateOrchestrationError(error)) return { ok: false, error };
29140
+ return {
29141
+ ok: false,
29142
+ error: executionFailed("migration", error, baseCtx)
29143
+ };
29144
+ }
29145
+ },
29146
+ {
29147
+ start: () => ({
29148
+ srcChainKey: _params.params.srcChainKey,
29149
+ srcAddress: _params.params.srcAddress,
29150
+ dstAddress: _params.params.dstAddress,
29151
+ amount: _params.params.amount,
29152
+ lockupPeriod: _params.params.lockupPeriod,
29153
+ stake: _params.params.stake
29154
+ }),
29155
+ success: (value) => ({
29156
+ srcChainTxHash: value.srcChainTxHash,
29157
+ dstChainTxHash: value.dstChainTxHash
29158
+ }),
29159
+ failure: (error) => ({ code: error.code })
28728
29160
  }
28729
- return { ok: true, value: { srcChainTxHash: tx, dstChainTxHash: packetResult.value.dst_tx_hash } };
28730
- } catch (error) {
28731
- if (isMigrateOrchestrationError(error)) return { ok: false, error };
28732
- return {
28733
- ok: false,
28734
- error: executionFailed("migration", error, baseCtx)
28735
- };
28736
- }
29161
+ );
28737
29162
  }
28738
29163
  /**
28739
29164
  * Builds and submits the spoke-side deposit for a BALN→SODA migration without relaying.
@@ -29124,71 +29549,883 @@ var MigrationService = class {
29124
29549
  }
29125
29550
  };
29126
29551
 
29127
- // src/backendApi/BackendApiService.ts
29128
- var BackendApiService = class {
29552
+ // src/backendApi/api-utils.ts
29553
+ var toJsonBody = (value) => JSON.stringify(value, (_key, val) => typeof val === "bigint" ? val.toString() : val);
29554
+ async function makeRequest(params) {
29555
+ const { endpoint, config, overrideConfig = {}, logger, serviceLabel } = params;
29556
+ const baseURL = overrideConfig.baseURL || config.baseURL || "";
29557
+ const url = `${baseURL}${endpoint}`;
29558
+ const headers = { ...config.headers, ...overrideConfig.headers };
29559
+ const controller = new AbortController();
29560
+ const timeout = overrideConfig.timeout ?? config.timeout ?? DEFAULT_BACKEND_API_TIMEOUT;
29561
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
29562
+ try {
29563
+ const response = await fetch(url, {
29564
+ method: config.method,
29565
+ headers,
29566
+ body: config.body,
29567
+ signal: controller.signal
29568
+ });
29569
+ clearTimeout(timeoutId);
29570
+ if (!response.ok) {
29571
+ const errorText = await response.text();
29572
+ throw new Error("HTTP_REQUEST_FAILED", { cause: new Error(`HTTP ${response.status}: ${errorText}`) });
29573
+ }
29574
+ const data = await response.json();
29575
+ return data;
29576
+ } catch (error) {
29577
+ clearTimeout(timeoutId);
29578
+ if (error instanceof Error) {
29579
+ if (error.name === "AbortError") {
29580
+ throw new Error("REQUEST_TIMEOUT", { cause: new Error(`Request timeout after ${timeout}ms`) });
29581
+ }
29582
+ logger.error(`[${serviceLabel}] Request error`, error);
29583
+ throw error;
29584
+ }
29585
+ logger.error(`[${serviceLabel}] Unknown error`, error);
29586
+ throw new Error("UNKNOWN_REQUEST_ERROR", { cause: error });
29587
+ }
29588
+ }
29589
+ var SwapTokenSchema = v4__namespace.object({
29590
+ symbol: v4__namespace.string(),
29591
+ name: v4__namespace.string(),
29592
+ decimals: v4__namespace.number(),
29593
+ address: v4__namespace.string(),
29594
+ chainKey: v4__namespace.string(),
29595
+ hubAsset: v4__namespace.string(),
29596
+ vault: v4__namespace.string()
29597
+ });
29598
+ var IntentResponseSchema = v4__namespace.object({
29599
+ intentId: v4__namespace.string(),
29600
+ creator: v4__namespace.string(),
29601
+ inputToken: v4__namespace.string(),
29602
+ outputToken: v4__namespace.string(),
29603
+ inputAmount: v4__namespace.string(),
29604
+ minOutputAmount: v4__namespace.string(),
29605
+ deadline: v4__namespace.string(),
29606
+ allowPartialFill: v4__namespace.boolean(),
29607
+ srcChain: v4__namespace.string(),
29608
+ dstChain: v4__namespace.string(),
29609
+ srcAddress: v4__namespace.string(),
29610
+ dstAddress: v4__namespace.string(),
29611
+ solver: v4__namespace.string(),
29612
+ data: v4__namespace.string()
29613
+ });
29614
+ var RelayExtraDataResponseSchema = v4__namespace.object({
29615
+ address: v4__namespace.string(),
29616
+ payload: v4__namespace.string()
29617
+ });
29618
+ var makeCreateIntentResponseSchema = (txSchema) => v4__namespace.object({
29619
+ tx: txSchema,
29620
+ intent: IntentResponseSchema,
29621
+ relayData: RelayExtraDataResponseSchema
29622
+ });
29623
+ var GetSwapTokensResponseSchema = v4__namespace.record(v4__namespace.string(), v4__namespace.array(SwapTokenSchema));
29624
+ var GetSwapTokensByChainResponseSchema = v4__namespace.array(SwapTokenSchema);
29625
+ var makeQuoteResponseSchema = (txSchema) => v4__namespace.object({
29626
+ quotedAmount: v4__namespace.string(),
29627
+ txData: v4__namespace.optional(makeCreateIntentResponseSchema(txSchema))
29628
+ });
29629
+ var DeadlineResponseSchema = v4__namespace.object({
29630
+ deadline: v4__namespace.string()
29631
+ });
29632
+ var AllowanceCheckResponseSchema = v4__namespace.object({
29633
+ valid: v4__namespace.boolean()
29634
+ });
29635
+ var makeApproveResponseSchema = (txSchema) => v4__namespace.object({ tx: txSchema });
29636
+ var SubmitIntentResponseSchema = v4__namespace.object({
29637
+ result: v4__namespace.unknown()
29638
+ });
29639
+ var StatusResponseSchema = v4__namespace.object({
29640
+ status: v4__namespace.picklist([-1, 1, 2, 3, 4]),
29641
+ fillTxHash: v4__namespace.optional(v4__namespace.string())
29642
+ });
29643
+ var makeCancelIntentResponseSchema = (txSchema) => v4__namespace.object({ tx: txSchema });
29644
+ var IntentHashResponseSchema = v4__namespace.object({
29645
+ hash: v4__namespace.string()
29646
+ });
29647
+ var IntentPacketResponseSchema = v4__namespace.object({
29648
+ srcChainId: v4__namespace.number(),
29649
+ srcTxHash: v4__namespace.string(),
29650
+ srcAddress: v4__namespace.string(),
29651
+ status: v4__namespace.string(),
29652
+ dstChainId: v4__namespace.number(),
29653
+ connSn: v4__namespace.number(),
29654
+ dstAddress: v4__namespace.string(),
29655
+ dstTxHash: v4__namespace.string(),
29656
+ signatures: v4__namespace.array(v4__namespace.string()),
29657
+ payload: v4__namespace.string()
29658
+ });
29659
+ var IntentStateResponseSchema = v4__namespace.object({
29660
+ exists: v4__namespace.boolean(),
29661
+ remainingInput: v4__namespace.string(),
29662
+ receivedOutput: v4__namespace.string(),
29663
+ pendingPayment: v4__namespace.boolean()
29664
+ });
29665
+ var GasEstimateResponseSchema = v4__namespace.object({
29666
+ gas: v4__namespace.unknown()
29667
+ });
29668
+ var FeeResponseSchema = v4__namespace.object({
29669
+ fee: v4__namespace.string()
29670
+ });
29671
+ var SubmitTxResponseSchema = v4__namespace.object({
29672
+ success: v4__namespace.boolean(),
29673
+ data: v4__namespace.object({
29674
+ status: v4__namespace.picklist(["inserted", "duplicate"]),
29675
+ message: v4__namespace.string()
29676
+ })
29677
+ });
29678
+ var SubmitSwapTxStatusSchema = v4__namespace.picklist([
29679
+ "pending",
29680
+ "relaying",
29681
+ "relayed",
29682
+ "posting_execution",
29683
+ "executed",
29684
+ "failed"
29685
+ ]);
29686
+ var PacketDataSchema = v4__namespace.object({
29687
+ src_chain_id: v4__namespace.number(),
29688
+ src_tx_hash: v4__namespace.string(),
29689
+ src_address: v4__namespace.string(),
29690
+ status: v4__namespace.picklist(["pending", "validating", "executing", "executed"]),
29691
+ dst_chain_id: v4__namespace.number(),
29692
+ conn_sn: v4__namespace.number(),
29693
+ dst_address: v4__namespace.string(),
29694
+ dst_tx_hash: v4__namespace.string(),
29695
+ signatures: v4__namespace.array(v4__namespace.string()),
29696
+ payload: v4__namespace.string()
29697
+ });
29698
+ var SubmitTxStatusResultSchema = v4__namespace.object({
29699
+ dstIntentTxHash: v4__namespace.string(),
29700
+ packetData: v4__namespace.optional(PacketDataSchema),
29701
+ intent_hash: v4__namespace.optional(v4__namespace.string())
29702
+ });
29703
+ var SubmitTxStatusDataSchema = v4__namespace.object({
29704
+ txHash: v4__namespace.string(),
29705
+ srcChainKey: v4__namespace.string(),
29706
+ status: SubmitSwapTxStatusSchema,
29707
+ failedAtStep: v4__namespace.optional(SubmitSwapTxStatusSchema),
29708
+ failureReason: v4__namespace.optional(v4__namespace.string()),
29709
+ processingAttempts: v4__namespace.number(),
29710
+ abandonedAt: v4__namespace.optional(v4__namespace.string()),
29711
+ result: v4__namespace.optional(SubmitTxStatusResultSchema),
29712
+ userMessage: v4__namespace.optional(v4__namespace.string()),
29713
+ intentCancelled: v4__namespace.optional(v4__namespace.boolean())
29714
+ });
29715
+ var SubmitTxStatusResponseSchema = v4__namespace.object({
29716
+ success: v4__namespace.boolean(),
29717
+ data: SubmitTxStatusDataSchema
29718
+ });
29719
+ var AddressSchema = v4__namespace.custom((input) => typeof input === "string");
29720
+ var HexSchema = v4__namespace.custom((input) => typeof input === "string");
29721
+ var BigintFromString = v4__namespace.pipe(v4__namespace.string(), v4__namespace.toBigint());
29722
+ var BytesFromIndexRecord = v4__namespace.pipe(
29723
+ v4__namespace.record(v4__namespace.string(), v4__namespace.number()),
29724
+ v4__namespace.transform((indexed) => Uint8Array.from(Object.values(indexed)))
29725
+ );
29726
+ var EvmRawTxSchema = v4__namespace.object({
29727
+ from: AddressSchema,
29728
+ to: AddressSchema,
29729
+ value: BigintFromString,
29730
+ data: HexSchema
29731
+ });
29732
+ var SolanaRawTxSchema = v4__namespace.object({
29733
+ from: v4__namespace.string(),
29734
+ to: v4__namespace.string(),
29735
+ value: BigintFromString,
29736
+ data: v4__namespace.string()
29737
+ });
29738
+ var SuiRawTxSchema = v4__namespace.object({
29739
+ from: HexSchema,
29740
+ to: v4__namespace.string(),
29741
+ value: BigintFromString,
29742
+ data: v4__namespace.string()
29743
+ });
29744
+ var StellarRawTxSchema = v4__namespace.object({
29745
+ from: v4__namespace.string(),
29746
+ to: v4__namespace.string(),
29747
+ value: BigintFromString,
29748
+ data: v4__namespace.string()
29749
+ });
29750
+ var InjectiveRawTxSchema = v4__namespace.object({
29751
+ from: HexSchema,
29752
+ to: HexSchema,
29753
+ signedDoc: v4__namespace.object({
29754
+ bodyBytes: BytesFromIndexRecord,
29755
+ authInfoBytes: BytesFromIndexRecord,
29756
+ chainId: v4__namespace.string(),
29757
+ accountNumber: BigintFromString
29758
+ })
29759
+ });
29760
+ var NearRawTxSchema = v4__namespace.object({
29761
+ signerId: v4__namespace.string(),
29762
+ params: v4__namespace.object({
29763
+ contractId: v4__namespace.string(),
29764
+ method: v4__namespace.string(),
29765
+ args: v4__namespace.custom((input) => typeof input === "object" && input !== null),
29766
+ gas: v4__namespace.optional(BigintFromString),
29767
+ deposit: v4__namespace.optional(BigintFromString)
29768
+ })
29769
+ });
29770
+ var IconRawTxSchema = v4__namespace.record(
29771
+ v4__namespace.string(),
29772
+ v4__namespace.union([v4__namespace.string(), v4__namespace.custom((input) => typeof input === "object" && input !== null)])
29773
+ );
29774
+ var StacksRawTxSchema = v4__namespace.object({
29775
+ payload: v4__namespace.string(),
29776
+ estimatedLength: v4__namespace.optional(v4__namespace.number())
29777
+ });
29778
+ var AnyRawTxSchema = v4__namespace.custom((input) => typeof input === "object" && input !== null);
29779
+ function rawTxSchemaForChainKey(chainKey) {
29780
+ let chainType;
29781
+ try {
29782
+ chainType = getChainType(chainKey);
29783
+ } catch {
29784
+ chainType = void 0;
29785
+ }
29786
+ switch (chainType) {
29787
+ case "EVM":
29788
+ return EvmRawTxSchema;
29789
+ case "SOLANA":
29790
+ return SolanaRawTxSchema;
29791
+ case "SUI":
29792
+ return SuiRawTxSchema;
29793
+ case "STELLAR":
29794
+ return StellarRawTxSchema;
29795
+ case "INJECTIVE":
29796
+ return InjectiveRawTxSchema;
29797
+ case "ICON":
29798
+ return IconRawTxSchema;
29799
+ case "STACKS":
29800
+ return StacksRawTxSchema;
29801
+ case "NEAR":
29802
+ return NearRawTxSchema;
29803
+ default:
29804
+ return AnyRawTxSchema;
29805
+ }
29806
+ }
29807
+
29808
+ // src/backendApi/SwapsApiService.ts
29809
+ var SwapsApiService = class {
29810
+ // Fully-resolved swaps-API config supplied by the caller (BackendApiService resolves the
29811
+ // `ApiConfig` union via `resolveSwapsApiConfig`); this service does not resolve the union.
29812
+ config;
29813
+ headers;
29814
+ logger;
29129
29815
  constructor(config, logger = consoleLogger) {
29130
29816
  this.config = config;
29131
29817
  this.headers = { ...config.headers };
29132
29818
  this.logger = logger;
29133
29819
  }
29134
- headers;
29135
- logger;
29136
29820
  /**
29137
- * Execute a single HTTP request and return the parsed JSON body.
29138
- *
29139
- * Applies an `AbortController`-backed timeout (falls back to `this.config.timeout`
29140
- * when `config.timeout` is absent). Throws on non-2xx status codes or when the
29141
- * request exceeds the timeout, so callers should use {@link request} instead of
29142
- * calling this directly.
29143
- *
29144
- * @throws `Error('HTTP_REQUEST_FAILED')` on non-2xx responses.
29145
- * @throws `Error('REQUEST_TIMEOUT')` when the request exceeds the timeout.
29146
- * @throws `Error('UNKNOWN_REQUEST_ERROR')` for any other unexpected failure.
29821
+ * Issues a single HTTP request, validates the JSON body against `schema`, and
29822
+ * wraps the result in `Result<T>`. The service-level `baseURL`/`timeout`/
29823
+ * `headers` are merged with the optional per-call `overrideConfig` (which takes
29824
+ * precedence) inside {@link makeRequest}. Every public method delegates here.
29147
29825
  */
29148
- async makeRequest(endpoint, config) {
29149
- const url = config.baseURL ? `${config.baseURL}${endpoint}` : `${this.config.baseURL}${endpoint}`;
29150
- const headers = { ...this.headers, ...config.headers };
29151
- const controller = new AbortController();
29152
- const timeout = config.timeout ?? this.config.timeout;
29153
- const timeoutId = setTimeout(() => controller.abort(), timeout);
29826
+ async request(endpoint, config, schema, overrideConfig) {
29154
29827
  try {
29155
- const response = await fetch(url, {
29156
- method: config.method,
29157
- headers,
29158
- body: config.body,
29159
- signal: controller.signal
29828
+ const raw = await makeRequest({
29829
+ endpoint,
29830
+ config: { baseURL: this.config.baseURL, timeout: this.config.timeout, headers: this.headers, ...config },
29831
+ overrideConfig,
29832
+ logger: this.logger,
29833
+ serviceLabel: "SwapsApiService"
29160
29834
  });
29161
- clearTimeout(timeoutId);
29162
- if (!response.ok) {
29163
- const errorText = await response.text();
29164
- throw new Error("HTTP_REQUEST_FAILED", { cause: new Error(`HTTP ${response.status}: ${errorText}`) });
29835
+ const parsed = v4__namespace.safeParse(schema, raw);
29836
+ if (!parsed.success) {
29837
+ return {
29838
+ ok: false,
29839
+ error: new SodaxError("EXTERNAL_API_ERROR", `Invalid response shape from swaps API for ${endpoint}`, {
29840
+ feature: "backend",
29841
+ context: { api: "swaps", endpoint, reason: "invalid_response_shape", issues: v4__namespace.flatten(parsed.issues) }
29842
+ })
29843
+ };
29165
29844
  }
29166
- const data = await response.json();
29167
- return data;
29845
+ return { ok: true, value: parsed.output };
29168
29846
  } catch (error) {
29169
- clearTimeout(timeoutId);
29170
- if (error instanceof Error) {
29171
- if (error.name === "AbortError") {
29172
- throw new Error("REQUEST_TIMEOUT", { cause: new Error(`Request timeout after ${timeout}ms`) });
29173
- }
29174
- this.logger.error("[BackendApiService] Request error", error);
29175
- throw error;
29176
- }
29177
- this.logger.error("[BackendApiService] Unknown error", error);
29178
- throw new Error("UNKNOWN_REQUEST_ERROR", { cause: error });
29847
+ return {
29848
+ ok: false,
29849
+ error: new SodaxError(
29850
+ "EXTERNAL_API_ERROR",
29851
+ error instanceof Error ? error.message : `Request to ${endpoint} failed`,
29852
+ {
29853
+ feature: "backend",
29854
+ cause: error,
29855
+ context: { api: "swaps", endpoint }
29856
+ }
29857
+ )
29858
+ };
29859
+ }
29860
+ }
29861
+ // ──────────────────────────────────────────────────────────────────────
29862
+ // Tokens
29863
+ // ──────────────────────────────────────────────────────────────────────
29864
+ /**
29865
+ * Fetch all supported swap tokens grouped by SpokeChainKey.
29866
+ *
29867
+ * @returns `Result<GetSwapTokensResponseV2>` — map of chain key → token list.
29868
+ */
29869
+ async getTokens(config) {
29870
+ return this.request("/swaps/tokens", { method: "GET" }, GetSwapTokensResponseSchema, config);
29871
+ }
29872
+ /**
29873
+ * Fetch supported swap tokens for a single SpokeChainKey.
29874
+ *
29875
+ * @param chainKey - SODAX SpokeChainKey (e.g. `0xa4b1.arbitrum`, `solana`).
29876
+ * @returns `Result<GetSwapTokensByChainResponseV2>` — token list for the chain.
29877
+ */
29878
+ async getTokensByChain(chainKey, config) {
29879
+ return this.request(
29880
+ `/swaps/tokens/${chainKey}`,
29881
+ { method: "GET" },
29882
+ GetSwapTokensByChainResponseSchema,
29883
+ config
29884
+ );
29885
+ }
29886
+ // ──────────────────────────────────────────────────────────────────────
29887
+ // Quote · deadline
29888
+ // ──────────────────────────────────────────────────────────────────────
29889
+ /**
29890
+ * Get a solver quote for a cross-chain swap.
29891
+ *
29892
+ * Pass `query.includeTxData = true` to also build an unsigned create-intent
29893
+ * transaction (`txData`) using the quoted amount as `minOutputAmount`; in that
29894
+ * case `srcAddress`/`dstAddress` are required in the body.
29895
+ *
29896
+ * @returns `Result<QuoteResponseV2>` — `quotedAmount` (decimal string) and optional `txData`.
29897
+ */
29898
+ async getQuote(body, query, config) {
29899
+ const endpoint = query?.includeTxData ? "/swaps/quote?includeTxData=true" : "/swaps/quote";
29900
+ const txSchema = rawTxSchemaForChainKey(body.tokenSrcChainKey);
29901
+ return this.request(
29902
+ endpoint,
29903
+ { method: "POST", body: toJsonBody(body) },
29904
+ makeQuoteResponseSchema(txSchema),
29905
+ config
29906
+ );
29907
+ }
29908
+ /**
29909
+ * Compute a swap deadline (hub timestamp + `offsetSeconds`, default 300s).
29910
+ *
29911
+ * @returns `Result<DeadlineResponseV2>` — unix-seconds deadline (decimal string).
29912
+ */
29913
+ async getDeadline(query, config) {
29914
+ const queryParams = new URLSearchParams();
29915
+ if (query?.offsetSeconds !== void 0) queryParams.append("offsetSeconds", String(query.offsetSeconds));
29916
+ const queryString = queryParams.toString();
29917
+ const endpoint = queryString.length > 0 ? `/swaps/deadline?${queryString}` : "/swaps/deadline";
29918
+ return this.request(endpoint, { method: "GET" }, DeadlineResponseSchema, config);
29919
+ }
29920
+ // ──────────────────────────────────────────────────────────────────────
29921
+ // Allowance · approve · create intent
29922
+ // ──────────────────────────────────────────────────────────────────────
29923
+ /**
29924
+ * Check whether the source token allowance is already sufficient for the intent.
29925
+ *
29926
+ * @returns `Result<AllowanceCheckResponseV2>` — `{ valid }`.
29927
+ */
29928
+ async checkAllowance(body, config) {
29929
+ return this.request(
29930
+ "/swaps/allowance/check",
29931
+ { method: "POST", body: toJsonBody(body) },
29932
+ AllowanceCheckResponseSchema,
29933
+ config
29934
+ );
29935
+ }
29936
+ /**
29937
+ * Build an unsigned token-approval transaction for the source token.
29938
+ *
29939
+ * @returns `Result<ApproveResponseV2>` — `{ tx }` (chain-specific unsigned tx).
29940
+ */
29941
+ async approve(body, config) {
29942
+ const txSchema = rawTxSchemaForChainKey(body.srcChainKey);
29943
+ return this.request(
29944
+ "/swaps/approve",
29945
+ { method: "POST", body: toJsonBody(body) },
29946
+ makeApproveResponseSchema(txSchema),
29947
+ config
29948
+ );
29949
+ }
29950
+ /**
29951
+ * Build an unsigned create-intent transaction.
29952
+ *
29953
+ * @returns `Result<CreateIntentResponseV2>` — `{ tx, intent, relayData }`.
29954
+ */
29955
+ async createIntent(body, config) {
29956
+ const txSchema = rawTxSchemaForChainKey(body.srcChainKey);
29957
+ return this.request(
29958
+ "/swaps/intents",
29959
+ { method: "POST", body: toJsonBody(body) },
29960
+ makeCreateIntentResponseSchema(txSchema),
29961
+ config
29962
+ );
29963
+ }
29964
+ // ──────────────────────────────────────────────────────────────────────
29965
+ // Intent lifecycle: submit · status · cancel · hash · packet · extra-data
29966
+ // ──────────────────────────────────────────────────────────────────────
29967
+ /**
29968
+ * Submit the broadcast intent tx to the relay.
29969
+ *
29970
+ * @returns `Result<SubmitIntentResponseV2>` — `{ result }` (opaque relay response).
29971
+ */
29972
+ async submitIntent(body, config) {
29973
+ return this.request(
29974
+ "/swaps/intents/submit",
29975
+ { method: "POST", body: toJsonBody(body) },
29976
+ SubmitIntentResponseSchema,
29977
+ config
29978
+ );
29979
+ }
29980
+ /**
29981
+ * Poll the solver for intent execution status.
29982
+ *
29983
+ * @returns `Result<StatusResponseV2>` — `{ status, fillTxHash? }` (`fillTxHash` set when `status === 3`).
29984
+ */
29985
+ async getStatus(body, config) {
29986
+ return this.request(
29987
+ "/swaps/intents/status",
29988
+ { method: "POST", body: toJsonBody(body) },
29989
+ StatusResponseSchema,
29990
+ config
29991
+ );
29992
+ }
29993
+ /**
29994
+ * Build an unsigned cancel-intent transaction. The `intent` field carries
29995
+ * `bigint` numerics — {@link toJsonBody} serializes them to decimal strings.
29996
+ *
29997
+ * @returns `Result<CancelIntentResponseV2>` — `{ tx }`.
29998
+ */
29999
+ async cancelIntent(body, config) {
30000
+ const txSchema = rawTxSchemaForChainKey(body.srcChainKey);
30001
+ return this.request(
30002
+ "/swaps/intents/cancel",
30003
+ { method: "POST", body: toJsonBody(body) },
30004
+ makeCancelIntentResponseSchema(txSchema),
30005
+ config
30006
+ );
30007
+ }
30008
+ /**
30009
+ * Compute the keccak256 hash of an Intent struct. The `intent` field carries
30010
+ * `bigint` numerics — {@link toJsonBody} serializes them to decimal strings.
30011
+ *
30012
+ * @returns `Result<IntentHashResponseV2>` — `{ hash }`.
30013
+ */
30014
+ async getIntentHash(body, config) {
30015
+ return this.request(
30016
+ "/swaps/intents/hash",
30017
+ { method: "POST", body: toJsonBody(body) },
30018
+ IntentHashResponseSchema,
30019
+ config
30020
+ );
30021
+ }
30022
+ /**
30023
+ * Long-poll the relayer until the fill packet lands on the destination chain.
30024
+ *
30025
+ * @returns `Result<IntentPacketResponseV2>` — delivered packet data.
30026
+ */
30027
+ async getSolvedIntentPacket(body, config) {
30028
+ return this.request(
30029
+ "/swaps/intents/packet",
30030
+ { method: "POST", body: toJsonBody(body) },
30031
+ IntentPacketResponseSchema,
30032
+ config
30033
+ );
30034
+ }
30035
+ /**
30036
+ * Recover the relay extra data needed by `/swaps/intents/submit`. Provide
30037
+ * EITHER `txHash` OR `intent` (whose `bigint` numerics {@link toJsonBody} serializes).
30038
+ *
30039
+ * @returns `Result<IntentExtraDataResponseV2>` — `{ address, payload }`.
30040
+ */
30041
+ async getIntentSubmitTxExtraData(body, config) {
30042
+ return this.request(
30043
+ "/swaps/intents/extra-data",
30044
+ { method: "POST", body: toJsonBody(body) },
30045
+ RelayExtraDataResponseSchema,
30046
+ config
30047
+ );
30048
+ }
30049
+ /**
30050
+ * Get the on-chain fill state for an intent by its hub-chain tx hash.
30051
+ *
30052
+ * @returns `Result<IntentStateV2>` — `{ exists, remainingInput, receivedOutput, pendingPayment }`.
30053
+ */
30054
+ async getFilledIntent(txHash, config) {
30055
+ return this.request(`/swaps/intents/${txHash}/fill`, { method: "GET" }, IntentStateResponseSchema, config);
30056
+ }
30057
+ /**
30058
+ * Look up an Intent struct by its hub-chain creation tx hash.
30059
+ *
30060
+ * @returns `Result<GetIntentResponseV2>` — the decoded intent (bigint fields as decimal strings).
30061
+ */
30062
+ async getIntent(txHash, config) {
30063
+ return this.request(`/swaps/intents/${txHash}`, { method: "GET" }, IntentResponseSchema, config);
30064
+ }
30065
+ // ──────────────────────────────────────────────────────────────────────
30066
+ // Limit orders · gas · fees
30067
+ // ──────────────────────────────────────────────────────────────────────
30068
+ /**
30069
+ * Build an unsigned create-limit-order-intent transaction (same as create-intent
30070
+ * but `deadline` is optional).
30071
+ *
30072
+ * @returns `Result<CreateLimitOrderResponseV2>` — `{ tx, intent, relayData }`.
30073
+ */
30074
+ async createLimitOrderIntent(body, config) {
30075
+ const txSchema = rawTxSchemaForChainKey(body.srcChainKey);
30076
+ return this.request(
30077
+ "/swaps/limit-orders",
30078
+ { method: "POST", body: toJsonBody(body) },
30079
+ makeCreateIntentResponseSchema(txSchema),
30080
+ config
30081
+ );
30082
+ }
30083
+ /**
30084
+ * Estimate gas for a raw transaction on a spoke chain.
30085
+ *
30086
+ * @returns `Result<GasEstimateResponseV2>` — `{ gas }` (chain-specific shape).
30087
+ */
30088
+ async estimateGas(body, config) {
30089
+ return this.request(
30090
+ "/swaps/gas/estimate",
30091
+ { method: "POST", body: toJsonBody(body) },
30092
+ GasEstimateResponseSchema,
30093
+ config
30094
+ );
30095
+ }
30096
+ /**
30097
+ * Compute the partner fee for a given input amount.
30098
+ *
30099
+ * @returns `Result<FeeResponseV2>` — `{ fee }` (decimal string).
30100
+ */
30101
+ async getPartnerFee(query, config) {
30102
+ const queryParams = new URLSearchParams({ amount: query.amount });
30103
+ return this.request(
30104
+ `/swaps/fees/partner?${queryParams.toString()}`,
30105
+ { method: "GET" },
30106
+ FeeResponseSchema,
30107
+ config
30108
+ );
30109
+ }
30110
+ /**
30111
+ * Compute the protocol (solver) fee for a given input amount.
30112
+ *
30113
+ * @returns `Result<FeeResponseV2>` — `{ fee }` (decimal string).
30114
+ */
30115
+ async getSolverFee(query, config) {
30116
+ const queryParams = new URLSearchParams({ amount: query.amount });
30117
+ return this.request(
30118
+ `/swaps/fees/solver?${queryParams.toString()}`,
30119
+ { method: "GET" },
30120
+ FeeResponseSchema,
30121
+ config
30122
+ );
30123
+ }
30124
+ // ──────────────────────────────────────────────────────────────────────
30125
+ // Submit-tx state machine
30126
+ // ──────────────────────────────────────────────────────────────────────
30127
+ /**
30128
+ * Submit a swap transaction to be processed (relay, post-execution, etc.). The
30129
+ * `intent` field carries `bigint` numerics — {@link toJsonBody} serializes them.
30130
+ * Idempotent on `(txHash, srcChainKey)`.
30131
+ *
30132
+ * @returns `Result<SubmitTxResponseV2>` — `{ success, data: { status, message } }`.
30133
+ */
30134
+ async submitTx(body, config) {
30135
+ return this.request(
30136
+ "/swaps/submit-tx",
30137
+ { method: "POST", body: toJsonBody(body) },
30138
+ SubmitTxResponseSchema,
30139
+ config
30140
+ );
30141
+ }
30142
+ /**
30143
+ * Get the processing status of a submitted swap transaction by `(txHash, srcChainKey)`.
30144
+ *
30145
+ * @returns `Result<SubmitTxStatusResponseV2>` — `{ success, data }` (processing state).
30146
+ */
30147
+ async getSubmitTxStatus(query, config) {
30148
+ const queryParams = new URLSearchParams({ txHash: query.txHash, srcChainKey: query.srcChainKey });
30149
+ return this.request(
30150
+ `/swaps/submit-tx/status?${queryParams.toString()}`,
30151
+ { method: "GET" },
30152
+ SubmitTxStatusResponseSchema,
30153
+ config
30154
+ );
30155
+ }
30156
+ // ──────────────────────────────────────────────────────────────────────
30157
+ // Utilities (parity with BackendApiService)
30158
+ // ──────────────────────────────────────────────────────────────────────
30159
+ /**
30160
+ * Merge additional headers into the service's default header set. Existing
30161
+ * keys are overwritten; keys absent from `headers` are preserved.
30162
+ */
30163
+ setHeaders(headers) {
30164
+ Object.entries(headers).forEach(([key, value]) => {
30165
+ this.headers[key] = value;
30166
+ });
30167
+ }
30168
+ /** Return the base URL the service is currently pointing at. */
30169
+ getBaseURL() {
30170
+ return this.config.baseURL;
30171
+ }
30172
+ };
30173
+
30174
+ // src/backendApi/apiConfig.ts
30175
+ function isCustomApiConfig(config) {
30176
+ return "baseApiConfig" in config || "swapsApiConfig" in config;
30177
+ }
30178
+ function layerConfigs(...slices) {
30179
+ return slices.reduce(
30180
+ (acc, slice) => slice ? {
30181
+ baseURL: slice.baseURL ?? acc.baseURL,
30182
+ timeout: slice.timeout ?? acc.timeout,
30183
+ headers: { ...acc.headers, ...slice.headers }
30184
+ } : acc,
30185
+ {
30186
+ baseURL: DEFAULT_BACKEND_API_ENDPOINT,
30187
+ timeout: DEFAULT_BACKEND_API_TIMEOUT,
30188
+ headers: { ...DEFAULT_BACKEND_API_HEADERS }
29179
30189
  }
30190
+ );
30191
+ }
30192
+ function resolveBaseApiConfig(config) {
30193
+ if (isCustomApiConfig(config)) {
30194
+ return layerConfigs(config.baseApiConfig);
30195
+ }
30196
+ return layerConfigs(config);
30197
+ }
30198
+ function resolveSwapsApiConfig(config) {
30199
+ if (isCustomApiConfig(config)) {
30200
+ return layerConfigs(config.baseApiConfig, config.swapsApiConfig);
30201
+ }
30202
+ return layerConfigs(config);
30203
+ }
30204
+ var ChainKeySchema = v4__namespace.custom((input) => typeof input === "string");
30205
+ var SpokeChainKeySchema = v4__namespace.custom((input) => typeof input === "string");
30206
+ var AddressSchema2 = v4__namespace.custom((input) => typeof input === "string");
30207
+ var XTokenSchema = v4__namespace.object({
30208
+ symbol: v4__namespace.string(),
30209
+ name: v4__namespace.string(),
30210
+ decimals: v4__namespace.number(),
30211
+ address: v4__namespace.string(),
30212
+ chainKey: ChainKeySchema,
30213
+ hubAsset: AddressSchema2,
30214
+ vault: AddressSchema2,
30215
+ access: v4__namespace.optional(v4__namespace.picklist(["withdrawOnly", "depositOnly"]))
30216
+ });
30217
+ var IntentStructSchema = v4__namespace.object({
30218
+ intentId: v4__namespace.string(),
30219
+ creator: v4__namespace.string(),
30220
+ inputToken: v4__namespace.string(),
30221
+ outputToken: v4__namespace.string(),
30222
+ inputAmount: v4__namespace.string(),
30223
+ minOutputAmount: v4__namespace.string(),
30224
+ deadline: v4__namespace.string(),
30225
+ allowPartialFill: v4__namespace.boolean(),
30226
+ srcChain: v4__namespace.number(),
30227
+ dstChain: v4__namespace.number(),
30228
+ srcAddress: v4__namespace.string(),
30229
+ dstAddress: v4__namespace.string(),
30230
+ solver: v4__namespace.string(),
30231
+ data: v4__namespace.string()
30232
+ });
30233
+ var IntentResponseSchema2 = v4__namespace.object({
30234
+ intentHash: v4__namespace.string(),
30235
+ txHash: v4__namespace.string(),
30236
+ logIndex: v4__namespace.number(),
30237
+ chainId: v4__namespace.number(),
30238
+ blockNumber: v4__namespace.number(),
30239
+ open: v4__namespace.boolean(),
30240
+ intent: IntentStructSchema,
30241
+ events: v4__namespace.array(v4__namespace.unknown())
30242
+ });
30243
+ var UserIntentsResponseSchema = v4__namespace.object({
30244
+ total: v4__namespace.number(),
30245
+ offset: v4__namespace.number(),
30246
+ limit: v4__namespace.number(),
30247
+ items: v4__namespace.array(IntentResponseSchema2)
30248
+ });
30249
+ var OrderbookResponseSchema = v4__namespace.object({
30250
+ total: v4__namespace.number(),
30251
+ data: v4__namespace.array(
30252
+ v4__namespace.object({
30253
+ intentState: v4__namespace.object({
30254
+ exists: v4__namespace.boolean(),
30255
+ remainingInput: v4__namespace.string(),
30256
+ receivedOutput: v4__namespace.string(),
30257
+ pendingPayment: v4__namespace.boolean()
30258
+ }),
30259
+ intentData: v4__namespace.object({
30260
+ intentId: v4__namespace.string(),
30261
+ creator: v4__namespace.string(),
30262
+ inputToken: v4__namespace.string(),
30263
+ outputToken: v4__namespace.string(),
30264
+ inputAmount: v4__namespace.string(),
30265
+ minOutputAmount: v4__namespace.string(),
30266
+ deadline: v4__namespace.string(),
30267
+ allowPartialFill: v4__namespace.boolean(),
30268
+ srcChain: v4__namespace.number(),
30269
+ dstChain: v4__namespace.number(),
30270
+ srcAddress: v4__namespace.string(),
30271
+ dstAddress: v4__namespace.string(),
30272
+ solver: v4__namespace.string(),
30273
+ data: v4__namespace.string(),
30274
+ intentHash: v4__namespace.string(),
30275
+ txHash: v4__namespace.string(),
30276
+ blockNumber: v4__namespace.number()
30277
+ })
30278
+ })
30279
+ )
30280
+ });
30281
+ var MoneyMarketPositionSchema = v4__namespace.object({
30282
+ userAddress: v4__namespace.string(),
30283
+ positions: v4__namespace.array(
30284
+ v4__namespace.object({
30285
+ reserveAddress: v4__namespace.string(),
30286
+ aTokenAddress: v4__namespace.string(),
30287
+ variableDebtTokenAddress: v4__namespace.string(),
30288
+ aTokenBalance: v4__namespace.string(),
30289
+ variableDebtTokenBalance: v4__namespace.string(),
30290
+ blockNumber: v4__namespace.number()
30291
+ })
30292
+ )
30293
+ });
30294
+ var MoneyMarketAssetSchema = v4__namespace.object({
30295
+ reserveAddress: v4__namespace.string(),
30296
+ aTokenAddress: v4__namespace.string(),
30297
+ totalATokenBalance: v4__namespace.string(),
30298
+ variableDebtTokenAddress: v4__namespace.string(),
30299
+ totalVariableDebtTokenBalance: v4__namespace.string(),
30300
+ liquidityRate: v4__namespace.string(),
30301
+ symbol: v4__namespace.string(),
30302
+ totalSuppliers: v4__namespace.number(),
30303
+ totalBorrowers: v4__namespace.number(),
30304
+ variableBorrowRate: v4__namespace.string(),
30305
+ stableBorrowRate: v4__namespace.string(),
30306
+ liquidityIndex: v4__namespace.string(),
30307
+ variableBorrowIndex: v4__namespace.string(),
30308
+ blockNumber: v4__namespace.number()
30309
+ });
30310
+ var MoneyMarketAssetsSchema = v4__namespace.array(MoneyMarketAssetSchema);
30311
+ var MoneyMarketAssetBorrowersSchema = v4__namespace.object({
30312
+ borrowers: v4__namespace.array(v4__namespace.string()),
30313
+ total: v4__namespace.number(),
30314
+ offset: v4__namespace.number(),
30315
+ limit: v4__namespace.number()
30316
+ });
30317
+ var MoneyMarketAssetSuppliersSchema = v4__namespace.object({
30318
+ suppliers: v4__namespace.array(v4__namespace.string()),
30319
+ total: v4__namespace.number(),
30320
+ offset: v4__namespace.number(),
30321
+ limit: v4__namespace.number()
30322
+ });
30323
+ var MoneyMarketBorrowersSchema = v4__namespace.object({
30324
+ borrowers: v4__namespace.array(v4__namespace.string()),
30325
+ total: v4__namespace.number(),
30326
+ offset: v4__namespace.number(),
30327
+ limit: v4__namespace.number()
30328
+ });
30329
+ var GetChainsResponseSchema = v4__namespace.array(SpokeChainKeySchema);
30330
+ var TokensByChainMapSchema = v4__namespace.custom(
30331
+ (input) => typeof input === "object" && input !== null && Object.values(input).every((tokens) => Array.isArray(tokens) && tokens.every((t) => v4__namespace.is(XTokenSchema, t)))
30332
+ );
30333
+ var TokensListSchema = v4__namespace.array(XTokenSchema);
30334
+ var ReserveAssetsSchema = v4__namespace.array(AddressSchema2);
30335
+
30336
+ // src/backendApi/BackendApiService.ts
30337
+ var BackendApiService = class {
30338
+ // sub-services exposing domain-specific APIs
30339
+ swaps;
30340
+ // resolved base-API slice of the ApiConfig union (flat config, or its `baseApiConfig`)
30341
+ config;
30342
+ headers;
30343
+ logger;
30344
+ constructor(config, logger = consoleLogger) {
30345
+ this.config = resolveBaseApiConfig(config);
30346
+ this.headers = { ...this.config.headers };
30347
+ this.logger = logger;
30348
+ this.swaps = new SwapsApiService(resolveSwapsApiConfig(config), this.logger);
29180
30349
  }
29181
30350
  /**
29182
30351
  * Wraps {@link makeRequest} in a `Result<T>` so all errors are captured rather
29183
30352
  * than propagated as thrown exceptions. Every public endpoint method delegates
29184
30353
  * here instead of calling `makeRequest` directly.
29185
30354
  */
29186
- async request(endpoint, config) {
30355
+ /**
30356
+ * Fold any per-call override (carried on `config` alongside `method`/`body`) over the service
30357
+ * defaults: baseURL truthy-fallback (an empty-string override defers to the default), timeout
30358
+ * nullish-fallback, headers merged (override wins per key). Mirrors the original makeRequest merge.
30359
+ */
30360
+ resolveRequestConfig(config) {
30361
+ const { baseURL, timeout, headers, ...rest } = config;
30362
+ return {
30363
+ ...rest,
30364
+ baseURL: baseURL || this.config.baseURL,
30365
+ timeout: timeout ?? this.config.timeout,
30366
+ headers: { ...this.headers, ...headers }
30367
+ };
30368
+ }
30369
+ /**
30370
+ * Wrap a thrown transport failure (HTTP_REQUEST_FAILED / REQUEST_TIMEOUT / network error) as the
30371
+ * canonical backend `SodaxError` — identical shape to SwapsApiService; the underlying failure is
30372
+ * preserved on `error.cause`.
30373
+ */
30374
+ toExternalApiError(endpoint, error) {
30375
+ return new SodaxError(
30376
+ "EXTERNAL_API_ERROR",
30377
+ error instanceof Error ? error.message : `Backend API request to ${endpoint} failed`,
30378
+ { feature: "backend", cause: error, context: { api: "backend", endpoint } }
30379
+ );
30380
+ }
30381
+ /**
30382
+ * Issue a request and validate the JSON body against `schema`, wrapping the result in `Result<T>`.
30383
+ * Mirrors {@link SwapsApiService}: a 2xx body that fails the schema is surfaced as
30384
+ * `EXTERNAL_API_ERROR` (`context.reason: 'invalid_response_shape'`) rather than returned untyped.
30385
+ * Every data/token/money-market method delegates here; the config/relay reads use
30386
+ * {@link requestUnvalidated} instead.
30387
+ */
30388
+ async request(endpoint, config, schema) {
30389
+ try {
30390
+ const raw = await makeRequest({
30391
+ endpoint,
30392
+ config: this.resolveRequestConfig(config),
30393
+ logger: this.logger,
30394
+ serviceLabel: "BackendApiService"
30395
+ });
30396
+ const parsed = v4__namespace.safeParse(schema, raw);
30397
+ if (!parsed.success) {
30398
+ return {
30399
+ ok: false,
30400
+ error: new SodaxError("EXTERNAL_API_ERROR", `Invalid response shape from backend API for ${endpoint}`, {
30401
+ feature: "backend",
30402
+ context: { api: "backend", endpoint, reason: "invalid_response_shape", issues: v4__namespace.flatten(parsed.issues) }
30403
+ })
30404
+ };
30405
+ }
30406
+ return { ok: true, value: parsed.output };
30407
+ } catch (error) {
30408
+ return { ok: false, error: this.toExternalApiError(endpoint, error) };
30409
+ }
30410
+ }
30411
+ /**
30412
+ * Issue a request WITHOUT response-shape validation (passthrough typing). Reserved for the
30413
+ * config/relay reads whose shapes are too large/brittle to schema-validate (`SodaxConfig` via
30414
+ * `getAllConfig`, `SpokeChainConfigMap` via `getSpokeChainConfig`) or carry `bigint` values that
30415
+ * cannot survive JSON validation (`getRelayChainIdMap`). `ConfigService` version-gates and falls
30416
+ * back to packaged defaults, so it relies on no response-shape guarantee from these endpoints.
30417
+ */
30418
+ async requestUnvalidated(endpoint, config) {
29187
30419
  try {
29188
- const value = await this.makeRequest(endpoint, config);
30420
+ const value = await makeRequest({
30421
+ endpoint,
30422
+ config: this.resolveRequestConfig(config),
30423
+ logger: this.logger,
30424
+ serviceLabel: "BackendApiService"
30425
+ });
29189
30426
  return { ok: true, value };
29190
30427
  } catch (error) {
29191
- return { ok: false, error };
30428
+ return { ok: false, error: this.toExternalApiError(endpoint, error) };
29192
30429
  }
29193
30430
  }
29194
30431
  // Intent endpoints
@@ -29203,7 +30440,7 @@ var BackendApiService = class {
29203
30440
  * open/closed state, token amounts, and any fill events.
29204
30441
  */
29205
30442
  async getIntentByTxHash(txHash, config) {
29206
- return this.request(`/intent/tx/${txHash}`, { ...config, method: "GET" });
30443
+ return this.request(`/intent/tx/${txHash}`, { ...config, method: "GET" }, IntentResponseSchema2);
29207
30444
  }
29208
30445
  /**
29209
30446
  * Fetch a swap intent by its canonical intent hash.
@@ -29212,58 +30449,7 @@ var BackendApiService = class {
29212
30449
  * @returns `Result<IntentResponse>` — on success, the full intent details.
29213
30450
  */
29214
30451
  async getIntentByHash(intentHash, config) {
29215
- return this.request(`/intent/${intentHash}`, { ...config, method: "GET" });
29216
- }
29217
- // Swap submit-tx endpoints
29218
- /**
29219
- * Submit a signed spoke-chain swap transaction to the backend for processing.
29220
- *
29221
- * The backend relays the transaction to the hub chain, posts execution data
29222
- * to the solver, and advances the intent through its lifecycle. The response
29223
- * shape is validated at runtime via a type guard; an invalid shape is
29224
- * returned as `{ ok: false }`.
29225
- *
29226
- * @param params - The signed transaction hash, source chain key, sender wallet
29227
- * address, intent data, and relay data required to process the swap.
29228
- * @returns `Result<SubmitSwapTxResponse>` — on success, a confirmation object
29229
- * with `success: true` and a human-readable `message`.
29230
- */
29231
- async submitSwapTx(params, config) {
29232
- const result = await this.request("/swaps/submit-tx", {
29233
- ...config,
29234
- method: "POST",
29235
- body: JSON.stringify(params)
29236
- });
29237
- if (!result.ok) return result;
29238
- if (!isSubmitSwapTxResponse(result.value)) {
29239
- return { ok: false, error: new Error("Invalid submitSwapTx response: unexpected response shape") };
29240
- }
29241
- return { ok: true, value: result.value };
29242
- }
29243
- /**
29244
- * Poll the backend relay pipeline for the current status of a previously
29245
- * submitted swap transaction.
29246
- *
29247
- * Status progresses through: `pending` → `verifying` → `verified` →
29248
- * `relaying` → `relayed` → `posting_execution` → `executed` (or `failed`).
29249
- *
29250
- * @param params - Object containing the source-chain transaction hash and,
29251
- * optionally, the source chain key to disambiguate cross-chain hashes.
29252
- * @returns `Result<SubmitSwapTxStatusResponse>` — on success, includes the
29253
- * current `status`, any `failureReason`, and (once executed) the
29254
- * `dstIntentTxHash` on the hub chain.
29255
- */
29256
- async getSubmitSwapTxStatus(params, config) {
29257
- const queryParams = new URLSearchParams();
29258
- queryParams.append("txHash", params.txHash);
29259
- if (params.srcChainKey) queryParams.append("srcChainKey", params.srcChainKey);
29260
- const endpoint = `/swaps/submit-tx/status?${queryParams.toString()}`;
29261
- const result = await this.request(endpoint, { ...config, method: "GET" });
29262
- if (!result.ok) return result;
29263
- if (!isSubmitSwapTxStatusResponse(result.value)) {
29264
- return { ok: false, error: new Error("Invalid submitSwapTxStatus response: unexpected response shape") };
29265
- }
29266
- return { ok: true, value: result.value };
30452
+ return this.request(`/intent/${intentHash}`, { ...config, method: "GET" }, IntentResponseSchema2);
29267
30453
  }
29268
30454
  // Solver endpoints
29269
30455
  /**
@@ -29280,7 +30466,7 @@ var BackendApiService = class {
29280
30466
  queryParams.append("limit", params.limit);
29281
30467
  const queryString = queryParams.toString();
29282
30468
  const endpoint = `/solver/orderbook?${queryString}`;
29283
- return this.request(endpoint, { ...config, method: "GET" });
30469
+ return this.request(endpoint, { ...config, method: "GET" }, OrderbookResponseSchema);
29284
30470
  }
29285
30471
  /**
29286
30472
  * Fetch all swap intents created by a specific wallet address, with optional
@@ -29306,7 +30492,7 @@ var BackendApiService = class {
29306
30492
  if (offset) queryParams.append("offset", offset);
29307
30493
  const queryString = queryParams.toString();
29308
30494
  const endpoint = queryString.length > 0 ? `/intent/user/${userAddress}?${queryString}` : `/intent/user/${userAddress}`;
29309
- return this.request(endpoint, { ...config, method: "GET" });
30495
+ return this.request(endpoint, { ...config, method: "GET" }, UserIntentsResponseSchema);
29310
30496
  }
29311
30497
  // Money Market endpoints
29312
30498
  /**
@@ -29321,7 +30507,11 @@ var BackendApiService = class {
29321
30507
  * position across all active reserves.
29322
30508
  */
29323
30509
  async getMoneyMarketPosition(userAddress, config) {
29324
- return this.request(`/moneymarket/position/${userAddress}`, { ...config, method: "GET" });
30510
+ return this.request(
30511
+ `/moneymarket/position/${userAddress}`,
30512
+ { ...config, method: "GET" },
30513
+ MoneyMarketPositionSchema
30514
+ );
29325
30515
  }
29326
30516
  /**
29327
30517
  * Fetch the on-chain state for every active money market reserve asset.
@@ -29330,7 +30520,7 @@ var BackendApiService = class {
29330
30520
  * snapshots including interest rates, liquidity indices, and participant counts.
29331
30521
  */
29332
30522
  async getAllMoneyMarketAssets(config) {
29333
- return this.request("/moneymarket/asset/all", { ...config, method: "GET" });
30523
+ return this.request("/moneymarket/asset/all", { ...config, method: "GET" }, MoneyMarketAssetsSchema);
29334
30524
  }
29335
30525
  /**
29336
30526
  * Fetch the on-chain state for a single money market reserve asset.
@@ -29340,7 +30530,11 @@ var BackendApiService = class {
29340
30530
  * including interest rates, total balances, and liquidity indices.
29341
30531
  */
29342
30532
  async getMoneyMarketAsset(reserveAddress, config) {
29343
- return this.request(`/moneymarket/asset/${reserveAddress}`, { ...config, method: "GET" });
30533
+ return this.request(
30534
+ `/moneymarket/asset/${reserveAddress}`,
30535
+ { ...config, method: "GET" },
30536
+ MoneyMarketAssetSchema
30537
+ );
29344
30538
  }
29345
30539
  /**
29346
30540
  * Fetch a paginated list of wallets that currently have an outstanding borrow
@@ -29357,7 +30551,7 @@ var BackendApiService = class {
29357
30551
  queryParams.append("limit", params.limit);
29358
30552
  const queryString = queryParams.toString();
29359
30553
  const endpoint = `/moneymarket/asset/${reserveAddress}/borrowers?${queryString}`;
29360
- return this.request(endpoint, { ...config, method: "GET" });
30554
+ return this.request(endpoint, { ...config, method: "GET" }, MoneyMarketAssetBorrowersSchema);
29361
30555
  }
29362
30556
  /**
29363
30557
  * Fetch a paginated list of wallets that currently have an active supply
@@ -29374,7 +30568,7 @@ var BackendApiService = class {
29374
30568
  queryParams.append("limit", params.limit);
29375
30569
  const queryString = queryParams.toString();
29376
30570
  const endpoint = `/moneymarket/asset/${reserveAddress}/suppliers?${queryString}`;
29377
- return this.request(endpoint, { ...config, method: "GET" });
30571
+ return this.request(endpoint, { ...config, method: "GET" }, MoneyMarketAssetSuppliersSchema);
29378
30572
  }
29379
30573
  /**
29380
30574
  * Fetch a paginated list of all wallet addresses that hold an active borrow
@@ -29390,7 +30584,7 @@ var BackendApiService = class {
29390
30584
  queryParams.append("limit", params.limit);
29391
30585
  const queryString = queryParams.toString();
29392
30586
  const endpoint = `/moneymarket/borrowers?${queryString}`;
29393
- return this.request(endpoint, { ...config, method: "GET" });
30587
+ return this.request(endpoint, { ...config, method: "GET" }, MoneyMarketBorrowersSchema);
29394
30588
  }
29395
30589
  /**
29396
30590
  * Fetch the complete SODAX runtime configuration in a single request.
@@ -29403,7 +30597,7 @@ var BackendApiService = class {
29403
30597
  * `config` is the current `SodaxConfig` used by all SDK services.
29404
30598
  */
29405
30599
  async getAllConfig(config) {
29406
- return this.request("/config/all", { ...config, method: "GET" });
30600
+ return this.requestUnvalidated("/config/all", { ...config, method: "GET" });
29407
30601
  }
29408
30602
  /**
29409
30603
  * Fetch the list of spoke chain keys that are currently supported by the
@@ -29416,7 +30610,7 @@ var BackendApiService = class {
29416
30610
  * `SpokeChainKey` strings (e.g. `["ethereum", "arbitrum", "solana", …]`).
29417
30611
  */
29418
30612
  async getChains(config) {
29419
- return this.request("/config/spoke/chains", { ...config, method: "GET" });
30613
+ return this.request("/config/spoke/chains", { ...config, method: "GET" }, GetChainsResponseSchema);
29420
30614
  }
29421
30615
  /**
29422
30616
  * Fetch the full map of tokens available for swapping, keyed by spoke chain.
@@ -29428,7 +30622,7 @@ var BackendApiService = class {
29428
30622
  * supported spoke chain key to its list of swappable `XToken` definitions.
29429
30623
  */
29430
30624
  async getSwapTokens(config) {
29431
- return this.request("/config/swap/tokens", { ...config, method: "GET" });
30625
+ return this.request("/config/swap/tokens", { ...config, method: "GET" }, TokensByChainMapSchema);
29432
30626
  }
29433
30627
  /**
29434
30628
  * Fetch the list of tokens available for swapping on a specific spoke chain.
@@ -29440,10 +30634,7 @@ var BackendApiService = class {
29440
30634
  * array of `XToken` definitions supported for swapping on that chain.
29441
30635
  */
29442
30636
  async getSwapTokensByChainId(chainId, config) {
29443
- return this.request(`/config/swap/${chainId}/tokens`, {
29444
- ...config,
29445
- method: "GET"
29446
- });
30637
+ return this.request(`/config/swap/${chainId}/tokens`, { ...config, method: "GET" }, TokensListSchema);
29447
30638
  }
29448
30639
  /**
29449
30640
  * Fetch the full map of tokens available in the money market (lending/borrowing),
@@ -29455,10 +30646,7 @@ var BackendApiService = class {
29455
30646
  * each supported spoke chain key to its list of money-market `XToken` definitions.
29456
30647
  */
29457
30648
  async getMoneyMarketTokens(config) {
29458
- return this.request("/config/money-market/tokens", {
29459
- ...config,
29460
- method: "GET"
29461
- });
30649
+ return this.request("/config/money-market/tokens", { ...config, method: "GET" }, TokensByChainMapSchema);
29462
30650
  }
29463
30651
  /**
29464
30652
  * Fetch the list of hub-chain reserve asset addresses registered in the
@@ -29472,10 +30660,11 @@ var BackendApiService = class {
29472
30660
  * readonly array of reserve `Address` strings.
29473
30661
  */
29474
30662
  async getMoneyMarketReserveAssets(config) {
29475
- return this.request("/config/money-market/reserve-assets", {
29476
- ...config,
29477
- method: "GET"
29478
- });
30663
+ return this.request(
30664
+ "/config/money-market/reserve-assets",
30665
+ { ...config, method: "GET" },
30666
+ ReserveAssetsSchema
30667
+ );
29479
30668
  }
29480
30669
  /**
29481
30670
  * Fetch the list of tokens available for lending/borrowing on a specific
@@ -29488,10 +30677,11 @@ var BackendApiService = class {
29488
30677
  * readonly array of `XToken` definitions supported in the money market on that chain.
29489
30678
  */
29490
30679
  async getMoneyMarketTokensByChainId(chainId, config) {
29491
- return this.request(`/config/money-market/${chainId}/tokens`, {
29492
- ...config,
29493
- method: "GET"
29494
- });
30680
+ return this.request(
30681
+ `/config/money-market/${chainId}/tokens`,
30682
+ { ...config, method: "GET" },
30683
+ TokensListSchema
30684
+ );
29495
30685
  }
29496
30686
  /**
29497
30687
  * Fetch the mapping from spoke chain keys to the numeric chain IDs used by
@@ -29505,7 +30695,7 @@ var BackendApiService = class {
29505
30695
  * `IntentRelayChainIdMap` record mapping each spoke chain key to its relay chain ID.
29506
30696
  */
29507
30697
  async getRelayChainIdMap(config) {
29508
- return this.request("/config/relay/chain-id-map", {
30698
+ return this.requestUnvalidated("/config/relay/chain-id-map", {
29509
30699
  ...config,
29510
30700
  method: "GET"
29511
30701
  });
@@ -29522,7 +30712,7 @@ var BackendApiService = class {
29522
30712
  * `SpokeChainConfigMap` for all currently enabled spoke chains.
29523
30713
  */
29524
30714
  async getSpokeChainConfig(config) {
29525
- return this.request("/config/spoke/all-chains-configs", {
30715
+ return this.requestUnvalidated("/config/spoke/all-chains-configs", {
29526
30716
  ...config,
29527
30717
  method: "GET"
29528
30718
  });
@@ -29534,12 +30724,17 @@ var BackendApiService = class {
29534
30724
  * without constructing a new service instance. Existing header keys are
29535
30725
  * overwritten; keys absent from `headers` are preserved.
29536
30726
  *
30727
+ * The headers are also fanned out to the sub-services (`swaps`), which hold
30728
+ * their own header copies — so a token set here applies to every request made
30729
+ * through this client, including `swaps.*`.
30730
+ *
29537
30731
  * @param headers - Key-value pairs to add or overwrite in the default headers.
29538
30732
  */
29539
30733
  setHeaders(headers) {
29540
30734
  Object.entries(headers).forEach(([key, value]) => {
29541
30735
  this.headers[key] = value;
29542
30736
  });
30737
+ this.swaps.setHeaders(headers);
29543
30738
  }
29544
30739
  /**
29545
30740
  * Return the base URL the service is currently pointing at.
@@ -29787,49 +30982,71 @@ var BridgeService = class {
29787
30982
  * }
29788
30983
  */
29789
30984
  async bridge(_params) {
29790
- const { params, timeout } = _params;
29791
- const baseCtx = { srcChainKey: params.srcChainKey, dstChainKey: params.dstChainKey };
29792
- try {
29793
- const txResult = await this.createBridgeIntent(_params);
29794
- if (!txResult.ok) return { ok: false, error: txResult.error };
29795
- const verifyTxHashResult = await this.spoke.verifyTxHash({
29796
- txHash: txResult.value.tx,
29797
- chainKey: params.srcChainKey
29798
- });
29799
- if (!verifyTxHashResult.ok) {
29800
- return {
29801
- ok: false,
29802
- error: verifyFailed("bridge", verifyTxHashResult.error, baseCtx)
29803
- };
30985
+ return this.config.analytics.trackResult(
30986
+ "bridge",
30987
+ "bridge",
30988
+ async () => {
30989
+ const { params, timeout } = _params;
30990
+ const baseCtx = { srcChainKey: params.srcChainKey, dstChainKey: params.dstChainKey };
30991
+ try {
30992
+ const txResult = await this.createBridgeIntent(_params);
30993
+ if (!txResult.ok) return { ok: false, error: txResult.error };
30994
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
30995
+ txHash: txResult.value.tx,
30996
+ chainKey: params.srcChainKey
30997
+ });
30998
+ if (!verifyTxHashResult.ok) {
30999
+ return {
31000
+ ok: false,
31001
+ error: verifyFailed("bridge", verifyTxHashResult.error, baseCtx)
31002
+ };
31003
+ }
31004
+ const packetResult = await relayTxAndWaitPacket({
31005
+ srcTxHash: txResult.value.tx,
31006
+ data: txResult.value.relayData,
31007
+ chainKey: params.srcChainKey,
31008
+ relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
31009
+ timeout
31010
+ });
31011
+ if (!packetResult.ok)
31012
+ return {
31013
+ ok: false,
31014
+ error: mapRelayFailure(packetResult.error, {
31015
+ feature: "bridge",
31016
+ action: "bridge",
31017
+ srcChainKey: baseCtx.srcChainKey,
31018
+ dstChainKey: baseCtx.dstChainKey
31019
+ })
31020
+ };
31021
+ return {
31022
+ ok: true,
31023
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packetResult.value.dst_tx_hash }
31024
+ };
31025
+ } catch (error) {
31026
+ if (isBridgeOrchestrationError(error)) return { ok: false, error };
31027
+ return {
31028
+ ok: false,
31029
+ error: executionFailed("bridge", error, baseCtx)
31030
+ };
31031
+ }
31032
+ },
31033
+ {
31034
+ start: () => ({
31035
+ srcChainKey: _params.params.srcChainKey,
31036
+ dstChainKey: _params.params.dstChainKey,
31037
+ srcToken: _params.params.srcToken,
31038
+ dstToken: _params.params.dstToken,
31039
+ amount: _params.params.amount,
31040
+ srcAddress: _params.params.srcAddress,
31041
+ recipient: _params.params.recipient
31042
+ }),
31043
+ success: (value) => ({
31044
+ srcChainTxHash: value.srcChainTxHash,
31045
+ dstChainTxHash: value.dstChainTxHash
31046
+ }),
31047
+ failure: (error) => ({ code: error.code })
29804
31048
  }
29805
- const packetResult = await relayTxAndWaitPacket({
29806
- srcTxHash: txResult.value.tx,
29807
- data: txResult.value.relayData,
29808
- chainKey: params.srcChainKey,
29809
- relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
29810
- timeout
29811
- });
29812
- if (!packetResult.ok)
29813
- return {
29814
- ok: false,
29815
- error: mapRelayFailure(packetResult.error, {
29816
- feature: "bridge",
29817
- action: "bridge",
29818
- srcChainKey: baseCtx.srcChainKey,
29819
- dstChainKey: baseCtx.dstChainKey
29820
- })
29821
- };
29822
- return {
29823
- ok: true,
29824
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packetResult.value.dst_tx_hash }
29825
- };
29826
- } catch (error) {
29827
- if (isBridgeOrchestrationError(error)) return { ok: false, error };
29828
- return {
29829
- ok: false,
29830
- error: executionFailed("bridge", error, baseCtx)
29831
- };
29832
- }
31049
+ );
29833
31050
  }
29834
31051
  /**
29835
31052
  * Submits the spoke-side deposit transaction that initiates a bridge transfer,
@@ -29858,8 +31075,8 @@ var BridgeService = class {
29858
31075
  const baseCtx = { srcChainKey: params.srcChainKey, dstChainKey: params.dstChainKey };
29859
31076
  try {
29860
31077
  bridgeInvariant(params.amount > 0n, "Amount must be greater than 0", { ...baseCtx, field: "amount" });
29861
- const srcToken = this.config.getSpokeTokenFromOriginalAssetAddress(params.srcChainKey, params.srcToken);
29862
- const dstToken = this.config.getSpokeTokenFromOriginalAssetAddress(params.dstChainKey, params.dstToken);
31078
+ const srcToken = this.resolveBridgeEndpointToken(params.srcChainKey, params.srcToken);
31079
+ const dstToken = this.resolveBridgeEndpointToken(params.dstChainKey, params.dstToken);
29863
31080
  bridgeInvariant(srcToken, `Unsupported spoke chain (${params.srcChainKey}) token: ${params.srcToken}`, {
29864
31081
  ...baseCtx,
29865
31082
  field: "srcToken"
@@ -29927,6 +31144,20 @@ var BridgeService = class {
29927
31144
  };
29928
31145
  }
29929
31146
  }
31147
+ /**
31148
+ * Resolves a bridge endpoint's {@link XToken} descriptor from a chain key + token address.
31149
+ *
31150
+ * Spoke endpoints (and hub-native tokens such as USDC/WETH/S whose on-chain address equals their
31151
+ * hub asset) resolve by original asset address. On the hub a caller may instead hold a hub asset
31152
+ * that has no spoke-token entry under the hub chain — e.g. a partner BTC fee held as the BTC hub
31153
+ * asset, whose only spoke-token entry lives on Bitcoin. Resolve those by hub-asset address so the
31154
+ * Sonic-sourced "withdraw directly" bridge can find the matching vault/decimals.
31155
+ */
31156
+ resolveBridgeEndpointToken(chainKey, token) {
31157
+ const spokeToken = this.config.getSpokeTokenFromOriginalAssetAddress(chainKey, token);
31158
+ if (spokeToken) return spokeToken;
31159
+ return isHubChainKeyType(chainKey) ? this.config.getXTokenFromHubAsset(token) : void 0;
31160
+ }
29930
31161
  /**
29931
31162
  * Encodes the hub-side execution payload for a bridge operation.
29932
31163
  *
@@ -30809,52 +32040,71 @@ var StakingService = class {
30809
32040
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
30810
32041
  */
30811
32042
  async stake(_params) {
30812
- const { params, timeout } = _params;
30813
- const baseCtx = { srcChainKey: params.srcChainKey, action: "stake" };
30814
- try {
30815
- const txResult = await this.createStakeIntent(_params);
30816
- if (!txResult.ok) return { ok: false, error: txResult.error };
30817
- const verifyTxHashResult = await this.spoke.verifyTxHash({
30818
- txHash: txResult.value.tx,
30819
- chainKey: params.srcChainKey
30820
- });
30821
- if (!verifyTxHashResult.ok) {
30822
- return {
30823
- ok: false,
30824
- error: verifyFailed("staking", verifyTxHashResult.error, baseCtx)
30825
- };
30826
- }
30827
- let hubTxHash;
30828
- if (!isHubChainKeyType(params.srcChainKey)) {
30829
- const packetResult = await relayTxAndWaitPacket({
30830
- srcTxHash: txResult.value.tx,
30831
- data: txResult.value.relayData,
30832
- chainKey: params.srcChainKey,
30833
- relayerApiEndpoint: this.relayerApiEndpoint,
30834
- timeout
30835
- });
30836
- if (!packetResult.ok) {
32043
+ return this.config.analytics.trackResult(
32044
+ "staking",
32045
+ "stake",
32046
+ async () => {
32047
+ const { params, timeout } = _params;
32048
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "stake" };
32049
+ try {
32050
+ const txResult = await this.createStakeIntent(_params);
32051
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32052
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
32053
+ txHash: txResult.value.tx,
32054
+ chainKey: params.srcChainKey
32055
+ });
32056
+ if (!verifyTxHashResult.ok) {
32057
+ return {
32058
+ ok: false,
32059
+ error: verifyFailed("staking", verifyTxHashResult.error, baseCtx)
32060
+ };
32061
+ }
32062
+ let hubTxHash;
32063
+ if (!isHubChainKeyType(params.srcChainKey)) {
32064
+ const packetResult = await relayTxAndWaitPacket({
32065
+ srcTxHash: txResult.value.tx,
32066
+ data: txResult.value.relayData,
32067
+ chainKey: params.srcChainKey,
32068
+ relayerApiEndpoint: this.relayerApiEndpoint,
32069
+ timeout
32070
+ });
32071
+ if (!packetResult.ok) {
32072
+ return {
32073
+ ok: false,
32074
+ error: mapRelayFailure(packetResult.error, {
32075
+ feature: "staking",
32076
+ action: baseCtx.action,
32077
+ srcChainKey: baseCtx.srcChainKey
32078
+ })
32079
+ };
32080
+ }
32081
+ hubTxHash = packetResult.value.dst_tx_hash;
32082
+ } else {
32083
+ hubTxHash = txResult.value.tx;
32084
+ }
32085
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32086
+ } catch (error) {
32087
+ if (isStakeOrchestrationError(error)) return { ok: false, error };
30837
32088
  return {
30838
32089
  ok: false,
30839
- error: mapRelayFailure(packetResult.error, {
30840
- feature: "staking",
30841
- action: baseCtx.action,
30842
- srcChainKey: baseCtx.srcChainKey
30843
- })
32090
+ error: executionFailed("staking", error, baseCtx)
30844
32091
  };
30845
32092
  }
30846
- hubTxHash = packetResult.value.dst_tx_hash;
30847
- } else {
30848
- hubTxHash = txResult.value.tx;
32093
+ },
32094
+ {
32095
+ start: () => ({
32096
+ srcChainKey: _params.params.srcChainKey,
32097
+ srcAddress: _params.params.srcAddress,
32098
+ amount: _params.params.amount,
32099
+ minReceive: _params.params.minReceive
32100
+ }),
32101
+ success: (value) => ({
32102
+ srcChainTxHash: value.srcChainTxHash,
32103
+ dstChainTxHash: value.dstChainTxHash
32104
+ }),
32105
+ failure: (error) => ({ code: error.code })
30849
32106
  }
30850
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
30851
- } catch (error) {
30852
- if (isStakeOrchestrationError(error)) return { ok: false, error };
30853
- return {
30854
- ok: false,
30855
- error: executionFailed("staking", error, baseCtx)
30856
- };
30857
- }
32107
+ );
30858
32108
  }
30859
32109
  /**
30860
32110
  * Submits the stake transaction on the spoke chain without relaying to the hub.
@@ -30959,42 +32209,60 @@ var StakingService = class {
30959
32209
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
30960
32210
  */
30961
32211
  async unstake(_params) {
30962
- const { params, timeout } = _params;
30963
- const baseCtx = { srcChainKey: params.srcChainKey, action: "unstake" };
30964
- try {
30965
- const txResult = await this.createUnstakeIntent(_params);
30966
- if (!txResult.ok) return { ok: false, error: txResult.error };
30967
- let hubTxHash;
30968
- if (!isHubChainKeyType(params.srcChainKey)) {
30969
- const packetResult = await relayTxAndWaitPacket({
30970
- srcTxHash: txResult.value.tx,
30971
- data: txResult.value.relayData,
30972
- chainKey: params.srcChainKey,
30973
- relayerApiEndpoint: this.relayerApiEndpoint,
30974
- timeout
30975
- });
30976
- if (!packetResult.ok) {
32212
+ return this.config.analytics.trackResult(
32213
+ "staking",
32214
+ "unstake",
32215
+ async () => {
32216
+ const { params, timeout } = _params;
32217
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "unstake" };
32218
+ try {
32219
+ const txResult = await this.createUnstakeIntent(_params);
32220
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32221
+ let hubTxHash;
32222
+ if (!isHubChainKeyType(params.srcChainKey)) {
32223
+ const packetResult = await relayTxAndWaitPacket({
32224
+ srcTxHash: txResult.value.tx,
32225
+ data: txResult.value.relayData,
32226
+ chainKey: params.srcChainKey,
32227
+ relayerApiEndpoint: this.relayerApiEndpoint,
32228
+ timeout
32229
+ });
32230
+ if (!packetResult.ok) {
32231
+ return {
32232
+ ok: false,
32233
+ error: mapRelayFailure(packetResult.error, {
32234
+ feature: "staking",
32235
+ action: baseCtx.action,
32236
+ srcChainKey: baseCtx.srcChainKey
32237
+ })
32238
+ };
32239
+ }
32240
+ hubTxHash = packetResult.value.dst_tx_hash;
32241
+ } else {
32242
+ hubTxHash = txResult.value.tx;
32243
+ }
32244
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32245
+ } catch (error) {
32246
+ if (isStakingOrchestrationError(error)) return { ok: false, error };
30977
32247
  return {
30978
32248
  ok: false,
30979
- error: mapRelayFailure(packetResult.error, {
30980
- feature: "staking",
30981
- action: baseCtx.action,
30982
- srcChainKey: baseCtx.srcChainKey
30983
- })
32249
+ error: executionFailed("staking", error, baseCtx)
30984
32250
  };
30985
32251
  }
30986
- hubTxHash = packetResult.value.dst_tx_hash;
30987
- } else {
30988
- hubTxHash = txResult.value.tx;
32252
+ },
32253
+ {
32254
+ start: () => ({
32255
+ srcChainKey: _params.params.srcChainKey,
32256
+ srcAddress: _params.params.srcAddress,
32257
+ amount: _params.params.amount
32258
+ }),
32259
+ success: (value) => ({
32260
+ srcChainTxHash: value.srcChainTxHash,
32261
+ dstChainTxHash: value.dstChainTxHash
32262
+ }),
32263
+ failure: (error) => ({ code: error.code })
30989
32264
  }
30990
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
30991
- } catch (error) {
30992
- if (isStakingOrchestrationError(error)) return { ok: false, error };
30993
- return {
30994
- ok: false,
30995
- error: executionFailed("staking", error, baseCtx)
30996
- };
30997
- }
32265
+ );
30998
32266
  }
30999
32267
  /**
31000
32268
  * Submits the unstake transaction on the spoke chain without relaying to the hub.
@@ -31090,42 +32358,61 @@ var StakingService = class {
31090
32358
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
31091
32359
  */
31092
32360
  async instantUnstake(_params) {
31093
- const { params, timeout } = _params;
31094
- const baseCtx = { srcChainKey: params.srcChainKey, action: "instantUnstake" };
31095
- try {
31096
- const txResult = await this.createInstantUnstakeIntent(_params);
31097
- if (!txResult.ok) return { ok: false, error: txResult.error };
31098
- let hubTxHash;
31099
- if (!isHubChainKeyType(params.srcChainKey)) {
31100
- const packetResult = await relayTxAndWaitPacket({
31101
- srcTxHash: txResult.value.tx,
31102
- data: txResult.value.relayData,
31103
- chainKey: params.srcChainKey,
31104
- relayerApiEndpoint: this.relayerApiEndpoint,
31105
- timeout
31106
- });
31107
- if (!packetResult.ok) {
32361
+ return this.config.analytics.trackResult(
32362
+ "staking",
32363
+ "instantUnstake",
32364
+ async () => {
32365
+ const { params, timeout } = _params;
32366
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "instantUnstake" };
32367
+ try {
32368
+ const txResult = await this.createInstantUnstakeIntent(_params);
32369
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32370
+ let hubTxHash;
32371
+ if (!isHubChainKeyType(params.srcChainKey)) {
32372
+ const packetResult = await relayTxAndWaitPacket({
32373
+ srcTxHash: txResult.value.tx,
32374
+ data: txResult.value.relayData,
32375
+ chainKey: params.srcChainKey,
32376
+ relayerApiEndpoint: this.relayerApiEndpoint,
32377
+ timeout
32378
+ });
32379
+ if (!packetResult.ok) {
32380
+ return {
32381
+ ok: false,
32382
+ error: mapRelayFailure(packetResult.error, {
32383
+ feature: "staking",
32384
+ action: baseCtx.action,
32385
+ srcChainKey: baseCtx.srcChainKey
32386
+ })
32387
+ };
32388
+ }
32389
+ hubTxHash = packetResult.value.dst_tx_hash;
32390
+ } else {
32391
+ hubTxHash = txResult.value.tx;
32392
+ }
32393
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32394
+ } catch (error) {
32395
+ if (isStakingOrchestrationError(error)) return { ok: false, error };
31108
32396
  return {
31109
32397
  ok: false,
31110
- error: mapRelayFailure(packetResult.error, {
31111
- feature: "staking",
31112
- action: baseCtx.action,
31113
- srcChainKey: baseCtx.srcChainKey
31114
- })
32398
+ error: executionFailed("staking", error, baseCtx)
31115
32399
  };
31116
32400
  }
31117
- hubTxHash = packetResult.value.dst_tx_hash;
31118
- } else {
31119
- hubTxHash = txResult.value.tx;
32401
+ },
32402
+ {
32403
+ start: () => ({
32404
+ srcChainKey: _params.params.srcChainKey,
32405
+ srcAddress: _params.params.srcAddress,
32406
+ amount: _params.params.amount,
32407
+ minAmount: _params.params.minAmount
32408
+ }),
32409
+ success: (value) => ({
32410
+ srcChainTxHash: value.srcChainTxHash,
32411
+ dstChainTxHash: value.dstChainTxHash
32412
+ }),
32413
+ failure: (error) => ({ code: error.code })
31120
32414
  }
31121
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
31122
- } catch (error) {
31123
- if (isStakingOrchestrationError(error)) return { ok: false, error };
31124
- return {
31125
- ok: false,
31126
- error: executionFailed("staking", error, baseCtx)
31127
- };
31128
- }
32415
+ );
31129
32416
  }
31130
32417
  /**
31131
32418
  * Submits the instant-unstake transaction on the spoke chain without relaying to the hub.
@@ -31233,42 +32520,61 @@ var StakingService = class {
31233
32520
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
31234
32521
  */
31235
32522
  async claim(_params) {
31236
- const { params, timeout } = _params;
31237
- const baseCtx = { srcChainKey: params.srcChainKey, action: "claim" };
31238
- try {
31239
- const txResult = await this.createClaimIntent(_params);
31240
- if (!txResult.ok) return { ok: false, error: txResult.error };
31241
- let hubTxHash;
31242
- if (!isHubChainKeyType(params.srcChainKey)) {
31243
- const packetResult = await relayTxAndWaitPacket({
31244
- srcTxHash: txResult.value.tx,
31245
- data: txResult.value.relayData,
31246
- chainKey: params.srcChainKey,
31247
- relayerApiEndpoint: this.relayerApiEndpoint,
31248
- timeout
31249
- });
31250
- if (!packetResult.ok) {
32523
+ return this.config.analytics.trackResult(
32524
+ "staking",
32525
+ "claim",
32526
+ async () => {
32527
+ const { params, timeout } = _params;
32528
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "claim" };
32529
+ try {
32530
+ const txResult = await this.createClaimIntent(_params);
32531
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32532
+ let hubTxHash;
32533
+ if (!isHubChainKeyType(params.srcChainKey)) {
32534
+ const packetResult = await relayTxAndWaitPacket({
32535
+ srcTxHash: txResult.value.tx,
32536
+ data: txResult.value.relayData,
32537
+ chainKey: params.srcChainKey,
32538
+ relayerApiEndpoint: this.relayerApiEndpoint,
32539
+ timeout
32540
+ });
32541
+ if (!packetResult.ok) {
32542
+ return {
32543
+ ok: false,
32544
+ error: mapRelayFailure(packetResult.error, {
32545
+ feature: "staking",
32546
+ action: baseCtx.action,
32547
+ srcChainKey: baseCtx.srcChainKey
32548
+ })
32549
+ };
32550
+ }
32551
+ hubTxHash = packetResult.value.dst_tx_hash;
32552
+ } else {
32553
+ hubTxHash = txResult.value.tx;
32554
+ }
32555
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32556
+ } catch (error) {
32557
+ if (isStakingOrchestrationError(error)) return { ok: false, error };
31251
32558
  return {
31252
32559
  ok: false,
31253
- error: mapRelayFailure(packetResult.error, {
31254
- feature: "staking",
31255
- action: baseCtx.action,
31256
- srcChainKey: baseCtx.srcChainKey
31257
- })
32560
+ error: executionFailed("staking", error, baseCtx)
31258
32561
  };
31259
32562
  }
31260
- hubTxHash = packetResult.value.dst_tx_hash;
31261
- } else {
31262
- hubTxHash = txResult.value.tx;
32563
+ },
32564
+ {
32565
+ start: () => ({
32566
+ srcChainKey: _params.params.srcChainKey,
32567
+ srcAddress: _params.params.srcAddress,
32568
+ requestId: _params.params.requestId,
32569
+ amount: _params.params.amount
32570
+ }),
32571
+ success: (value) => ({
32572
+ srcChainTxHash: value.srcChainTxHash,
32573
+ dstChainTxHash: value.dstChainTxHash
32574
+ }),
32575
+ failure: (error) => ({ code: error.code })
31263
32576
  }
31264
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
31265
- } catch (error) {
31266
- if (isStakingOrchestrationError(error)) return { ok: false, error };
31267
- return {
31268
- ok: false,
31269
- error: executionFailed("staking", error, baseCtx)
31270
- };
31271
- }
32577
+ );
31272
32578
  }
31273
32579
  /**
31274
32580
  * Submits the claim transaction on the spoke chain without relaying to the hub.
@@ -31382,42 +32688,60 @@ var StakingService = class {
31382
32688
  * @returns `{ ok: true, value: { srcChainTxHash, dstChainTxHash } }` on success.
31383
32689
  */
31384
32690
  async cancelUnstake(_params) {
31385
- const { params, timeout } = _params;
31386
- const baseCtx = { srcChainKey: params.srcChainKey, action: "cancelUnstake" };
31387
- try {
31388
- const txResult = await this.createCancelUnstakeIntent(_params);
31389
- if (!txResult.ok) return { ok: false, error: txResult.error };
31390
- let hubTxHash;
31391
- if (!isHubChainKeyType(params.srcChainKey)) {
31392
- const packetResult = await relayTxAndWaitPacket({
31393
- srcTxHash: txResult.value.tx,
31394
- data: txResult.value.relayData,
31395
- chainKey: params.srcChainKey,
31396
- relayerApiEndpoint: this.relayerApiEndpoint,
31397
- timeout
31398
- });
31399
- if (!packetResult.ok) {
32691
+ return this.config.analytics.trackResult(
32692
+ "staking",
32693
+ "cancelUnstake",
32694
+ async () => {
32695
+ const { params, timeout } = _params;
32696
+ const baseCtx = { srcChainKey: params.srcChainKey, action: "cancelUnstake" };
32697
+ try {
32698
+ const txResult = await this.createCancelUnstakeIntent(_params);
32699
+ if (!txResult.ok) return { ok: false, error: txResult.error };
32700
+ let hubTxHash;
32701
+ if (!isHubChainKeyType(params.srcChainKey)) {
32702
+ const packetResult = await relayTxAndWaitPacket({
32703
+ srcTxHash: txResult.value.tx,
32704
+ data: txResult.value.relayData,
32705
+ chainKey: params.srcChainKey,
32706
+ relayerApiEndpoint: this.relayerApiEndpoint,
32707
+ timeout
32708
+ });
32709
+ if (!packetResult.ok) {
32710
+ return {
32711
+ ok: false,
32712
+ error: mapRelayFailure(packetResult.error, {
32713
+ feature: "staking",
32714
+ action: baseCtx.action,
32715
+ srcChainKey: baseCtx.srcChainKey
32716
+ })
32717
+ };
32718
+ }
32719
+ hubTxHash = packetResult.value.dst_tx_hash;
32720
+ } else {
32721
+ hubTxHash = txResult.value.tx;
32722
+ }
32723
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32724
+ } catch (error) {
32725
+ if (isStakingOrchestrationError(error)) return { ok: false, error };
31400
32726
  return {
31401
32727
  ok: false,
31402
- error: mapRelayFailure(packetResult.error, {
31403
- feature: "staking",
31404
- action: baseCtx.action,
31405
- srcChainKey: baseCtx.srcChainKey
31406
- })
32728
+ error: executionFailed("staking", error, baseCtx)
31407
32729
  };
31408
32730
  }
31409
- hubTxHash = packetResult.value.dst_tx_hash;
31410
- } else {
31411
- hubTxHash = txResult.value.tx;
32731
+ },
32732
+ {
32733
+ start: () => ({
32734
+ srcChainKey: _params.params.srcChainKey,
32735
+ srcAddress: _params.params.srcAddress,
32736
+ requestId: _params.params.requestId
32737
+ }),
32738
+ success: (value) => ({
32739
+ srcChainTxHash: value.srcChainTxHash,
32740
+ dstChainTxHash: value.dstChainTxHash
32741
+ }),
32742
+ failure: (error) => ({ code: error.code })
31412
32743
  }
31413
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
31414
- } catch (error) {
31415
- if (isStakingOrchestrationError(error)) return { ok: false, error };
31416
- return {
31417
- ok: false,
31418
- error: executionFailed("staking", error, baseCtx)
31419
- };
31420
- }
32744
+ );
31421
32745
  }
31422
32746
  /**
31423
32747
  * Submits the cancel-unstake transaction on the spoke chain without relaying to the hub.
@@ -32791,34 +34115,56 @@ var ClService = class _ClService {
32791
34115
  * `dstChainTxHash` (hub) once the packet has been confirmed.
32792
34116
  */
32793
34117
  async supplyLiquidity(_params) {
32794
- const { params, timeout } = _params;
32795
- try {
32796
- const txResult = await this.executeSupplyLiquidity(_params);
32797
- if (!txResult.ok) {
32798
- return txResult;
32799
- }
32800
- let hubTxHash;
32801
- if (!isHubChainKeyType(params.srcChainKey)) {
32802
- const packetResult = await relayTxAndWaitPacket({
32803
- srcTxHash: txResult.value.tx,
32804
- data: txResult.value.relayData,
32805
- chainKey: params.srcChainKey,
32806
- relayerApiEndpoint: this.relayerApiEndpoint,
32807
- timeout
32808
- });
32809
- if (!packetResult.ok) return packetResult;
32810
- hubTxHash = packetResult.value.dst_tx_hash;
32811
- } else {
32812
- hubTxHash = txResult.value.tx;
34118
+ return this.config.analytics.trackResult(
34119
+ "dex",
34120
+ "supplyLiquidity",
34121
+ async () => {
34122
+ const { params, timeout } = _params;
34123
+ try {
34124
+ const txResult = await this.executeSupplyLiquidity(_params);
34125
+ if (!txResult.ok) {
34126
+ return txResult;
34127
+ }
34128
+ let hubTxHash;
34129
+ if (!isHubChainKeyType(params.srcChainKey)) {
34130
+ const packetResult = await relayTxAndWaitPacket({
34131
+ srcTxHash: txResult.value.tx,
34132
+ data: txResult.value.relayData,
34133
+ chainKey: params.srcChainKey,
34134
+ relayerApiEndpoint: this.relayerApiEndpoint,
34135
+ timeout
34136
+ });
34137
+ if (!packetResult.ok) return packetResult;
34138
+ hubTxHash = packetResult.value.dst_tx_hash;
34139
+ } else {
34140
+ hubTxHash = txResult.value.tx;
34141
+ }
34142
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
34143
+ } catch (error) {
34144
+ this.config.logger.error("supplyLiquidity error", error);
34145
+ return {
34146
+ ok: false,
34147
+ error
34148
+ };
34149
+ }
34150
+ },
34151
+ {
34152
+ start: () => ({
34153
+ srcChainKey: _params.params.srcChainKey,
34154
+ srcAddress: _params.params.srcAddress,
34155
+ currency0: _params.params.poolKey.currency0,
34156
+ currency1: _params.params.poolKey.currency1,
34157
+ fee: _params.params.poolKey.fee,
34158
+ tickLower: _params.params.tickLower,
34159
+ tickUpper: _params.params.tickUpper,
34160
+ liquidity: _params.params.liquidity,
34161
+ amount0Max: _params.params.amount0Max,
34162
+ amount1Max: _params.params.amount1Max
34163
+ }),
34164
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
34165
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
32813
34166
  }
32814
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32815
- } catch (error) {
32816
- this.config.logger.error("supplyLiquidity error", error);
32817
- return {
32818
- ok: false,
32819
- error
32820
- };
32821
- }
34167
+ );
32822
34168
  }
32823
34169
  /**
32824
34170
  * Add liquidity to an existing position and wait for the cross-chain relay to complete.
@@ -32833,33 +34179,56 @@ var ClService = class _ClService {
32833
34179
  * `dstChainTxHash` (hub) once the packet has been confirmed.
32834
34180
  */
32835
34181
  async increaseLiquidity(_params) {
32836
- const { params, timeout } = _params;
32837
- try {
32838
- const txResult = await this.executeIncreaseLiquidity(_params);
32839
- if (!txResult.ok) {
32840
- return txResult;
32841
- }
32842
- let hubTxHash;
32843
- if (!isHubChainKeyType(params.srcChainKey)) {
32844
- const packetResult = await relayTxAndWaitPacket({
32845
- srcTxHash: txResult.value.tx,
32846
- data: txResult.value.relayData,
32847
- chainKey: params.srcChainKey,
32848
- relayerApiEndpoint: this.relayerApiEndpoint,
32849
- timeout
32850
- });
32851
- if (!packetResult.ok) return packetResult;
32852
- hubTxHash = packetResult.value.dst_tx_hash;
32853
- } else {
32854
- hubTxHash = txResult.value.tx;
34182
+ return this.config.analytics.trackResult(
34183
+ "dex",
34184
+ "increaseLiquidity",
34185
+ async () => {
34186
+ const { params, timeout } = _params;
34187
+ try {
34188
+ const txResult = await this.executeIncreaseLiquidity(_params);
34189
+ if (!txResult.ok) {
34190
+ return txResult;
34191
+ }
34192
+ let hubTxHash;
34193
+ if (!isHubChainKeyType(params.srcChainKey)) {
34194
+ const packetResult = await relayTxAndWaitPacket({
34195
+ srcTxHash: txResult.value.tx,
34196
+ data: txResult.value.relayData,
34197
+ chainKey: params.srcChainKey,
34198
+ relayerApiEndpoint: this.relayerApiEndpoint,
34199
+ timeout
34200
+ });
34201
+ if (!packetResult.ok) return packetResult;
34202
+ hubTxHash = packetResult.value.dst_tx_hash;
34203
+ } else {
34204
+ hubTxHash = txResult.value.tx;
34205
+ }
34206
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
34207
+ } catch (error) {
34208
+ return {
34209
+ ok: false,
34210
+ error
34211
+ };
34212
+ }
34213
+ },
34214
+ {
34215
+ start: () => ({
34216
+ srcChainKey: _params.params.srcChainKey,
34217
+ srcAddress: _params.params.srcAddress,
34218
+ currency0: _params.params.poolKey.currency0,
34219
+ currency1: _params.params.poolKey.currency1,
34220
+ fee: _params.params.poolKey.fee,
34221
+ tokenId: _params.params.tokenId,
34222
+ tickLower: _params.params.tickLower,
34223
+ tickUpper: _params.params.tickUpper,
34224
+ liquidity: _params.params.liquidity,
34225
+ amount0Max: _params.params.amount0Max,
34226
+ amount1Max: _params.params.amount1Max
34227
+ }),
34228
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
34229
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
32855
34230
  }
32856
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32857
- } catch (error) {
32858
- return {
32859
- ok: false,
32860
- error
32861
- };
32862
- }
34231
+ );
32863
34232
  }
32864
34233
  /**
32865
34234
  * Remove liquidity from an existing position and wait for the cross-chain relay to complete.
@@ -32874,33 +34243,54 @@ var ClService = class _ClService {
32874
34243
  * `dstChainTxHash` (hub) once the packet has been confirmed.
32875
34244
  */
32876
34245
  async decreaseLiquidity(_params) {
32877
- const { params, timeout } = _params;
32878
- try {
32879
- const txResult = await this.executeDecreaseLiquidity(_params);
32880
- if (!txResult.ok) {
32881
- return txResult;
32882
- }
32883
- let hubTxHash;
32884
- if (!isHubChainKeyType(params.srcChainKey)) {
32885
- const packetResult = await relayTxAndWaitPacket({
32886
- srcTxHash: txResult.value.tx,
32887
- data: txResult.value.relayData,
32888
- chainKey: params.srcChainKey,
32889
- relayerApiEndpoint: this.relayerApiEndpoint,
32890
- timeout
32891
- });
32892
- if (!packetResult.ok) return packetResult;
32893
- hubTxHash = packetResult.value.dst_tx_hash;
32894
- } else {
32895
- hubTxHash = txResult.value.tx;
34246
+ return this.config.analytics.trackResult(
34247
+ "dex",
34248
+ "decreaseLiquidity",
34249
+ async () => {
34250
+ const { params, timeout } = _params;
34251
+ try {
34252
+ const txResult = await this.executeDecreaseLiquidity(_params);
34253
+ if (!txResult.ok) {
34254
+ return txResult;
34255
+ }
34256
+ let hubTxHash;
34257
+ if (!isHubChainKeyType(params.srcChainKey)) {
34258
+ const packetResult = await relayTxAndWaitPacket({
34259
+ srcTxHash: txResult.value.tx,
34260
+ data: txResult.value.relayData,
34261
+ chainKey: params.srcChainKey,
34262
+ relayerApiEndpoint: this.relayerApiEndpoint,
34263
+ timeout
34264
+ });
34265
+ if (!packetResult.ok) return packetResult;
34266
+ hubTxHash = packetResult.value.dst_tx_hash;
34267
+ } else {
34268
+ hubTxHash = txResult.value.tx;
34269
+ }
34270
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
34271
+ } catch (error) {
34272
+ return {
34273
+ ok: false,
34274
+ error
34275
+ };
34276
+ }
34277
+ },
34278
+ {
34279
+ start: () => ({
34280
+ srcChainKey: _params.params.srcChainKey,
34281
+ srcAddress: _params.params.srcAddress,
34282
+ currency0: _params.params.poolKey.currency0,
34283
+ currency1: _params.params.poolKey.currency1,
34284
+ fee: _params.params.poolKey.fee,
34285
+ tokenId: _params.params.tokenId,
34286
+ liquidity: _params.params.liquidity,
34287
+ amount0Min: _params.params.amount0Min,
34288
+ amount1Min: _params.params.amount1Min
34289
+ }),
34290
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
34291
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
32896
34292
  }
32897
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32898
- } catch (error) {
32899
- return {
32900
- ok: false,
32901
- error
32902
- };
32903
- }
34293
+ );
32904
34294
  }
32905
34295
  /**
32906
34296
  * Fetch the reward configuration stored in the pool's hook contract.
@@ -33052,34 +34442,54 @@ var ClService = class _ClService {
33052
34442
  * `dstChainTxHash` (hub) once the packet has been confirmed.
33053
34443
  */
33054
34444
  async claimRewards(_params) {
33055
- const { params, timeout } = _params;
33056
- try {
33057
- const txResult = await this.executeClaimRewards(_params);
33058
- if (!txResult.ok) {
33059
- return txResult;
33060
- }
33061
- let hubTxHash;
33062
- if (!isHubChainKeyType(params.srcChainKey)) {
33063
- const packetResult = await relayTxAndWaitPacket({
33064
- srcTxHash: txResult.value.tx,
33065
- data: txResult.value.relayData,
33066
- chainKey: params.srcChainKey,
33067
- relayerApiEndpoint: this.relayerApiEndpoint,
33068
- timeout
33069
- });
33070
- if (!packetResult.ok) return packetResult;
33071
- hubTxHash = packetResult.value.dst_tx_hash;
33072
- } else {
33073
- hubTxHash = txResult.value.tx;
34445
+ return this.config.analytics.trackResult(
34446
+ "dex",
34447
+ "claimRewards",
34448
+ async () => {
34449
+ const { params, timeout } = _params;
34450
+ try {
34451
+ const txResult = await this.executeClaimRewards(_params);
34452
+ if (!txResult.ok) {
34453
+ return txResult;
34454
+ }
34455
+ let hubTxHash;
34456
+ if (!isHubChainKeyType(params.srcChainKey)) {
34457
+ const packetResult = await relayTxAndWaitPacket({
34458
+ srcTxHash: txResult.value.tx,
34459
+ data: txResult.value.relayData,
34460
+ chainKey: params.srcChainKey,
34461
+ relayerApiEndpoint: this.relayerApiEndpoint,
34462
+ timeout
34463
+ });
34464
+ if (!packetResult.ok) return packetResult;
34465
+ hubTxHash = packetResult.value.dst_tx_hash;
34466
+ } else {
34467
+ hubTxHash = txResult.value.tx;
34468
+ }
34469
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
34470
+ } catch (error) {
34471
+ this.config.logger.error("claimRewards error", error);
34472
+ return {
34473
+ ok: false,
34474
+ error
34475
+ };
34476
+ }
34477
+ },
34478
+ {
34479
+ start: () => ({
34480
+ srcChainKey: _params.params.srcChainKey,
34481
+ srcAddress: _params.params.srcAddress,
34482
+ currency0: _params.params.poolKey.currency0,
34483
+ currency1: _params.params.poolKey.currency1,
34484
+ fee: _params.params.poolKey.fee,
34485
+ tokenId: _params.params.tokenId,
34486
+ tickLower: _params.params.tickLower,
34487
+ tickUpper: _params.params.tickUpper
34488
+ }),
34489
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
34490
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
33074
34491
  }
33075
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
33076
- } catch (error) {
33077
- this.config.logger.error("claimRewards error", error);
33078
- return {
33079
- ok: false,
33080
- error
33081
- };
33082
- }
34492
+ );
33083
34493
  }
33084
34494
  /**
33085
34495
  * Return the list of configured concentrated-liquidity pool keys for the SODAX DEX.
@@ -36002,50 +37412,68 @@ var MoneyMarketService = class _MoneyMarketService {
36002
37412
  * @returns A pair of transaction hashes — `srcChainTxHash` (spoke) and `dstChainTxHash` (hub).
36003
37413
  */
36004
37414
  async supply(_params) {
36005
- const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
36006
- const srcChainKey = params.srcChainKey;
36007
- const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "supply" };
36008
- try {
36009
- const txResult = await this.createSupplyIntent(_params);
36010
- if (!txResult.ok) return { ok: false, error: txResult.error };
36011
- const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
36012
- if (!verify.ok) {
36013
- return {
36014
- ok: false,
36015
- error: verifyFailed("moneyMarket", verify.error, baseCtx)
36016
- };
36017
- }
36018
- if (isHubChainKeyType(srcChainKey)) {
36019
- return {
36020
- ok: true,
36021
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
36022
- };
37415
+ return this.config.analytics.trackResult(
37416
+ "moneyMarket",
37417
+ "supply",
37418
+ async () => {
37419
+ const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
37420
+ const srcChainKey = params.srcChainKey;
37421
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "supply" };
37422
+ try {
37423
+ const txResult = await this.createSupplyIntent(_params);
37424
+ if (!txResult.ok) return { ok: false, error: txResult.error };
37425
+ const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
37426
+ if (!verify.ok) {
37427
+ return {
37428
+ ok: false,
37429
+ error: verifyFailed("moneyMarket", verify.error, baseCtx)
37430
+ };
37431
+ }
37432
+ if (isHubChainKeyType(srcChainKey)) {
37433
+ return {
37434
+ ok: true,
37435
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
37436
+ };
37437
+ }
37438
+ const packet = await relayTxAndWaitPacket({
37439
+ srcTxHash: txResult.value.tx,
37440
+ data: txResult.value.relayData,
37441
+ chainKey: srcChainKey,
37442
+ relayerApiEndpoint: this.relayerApiEndpoint,
37443
+ timeout
37444
+ });
37445
+ if (!packet.ok)
37446
+ return {
37447
+ ok: false,
37448
+ error: mapRelayFailure(packet.error, {
37449
+ feature: "moneyMarket",
37450
+ action: baseCtx.action,
37451
+ srcChainKey: baseCtx.srcChainKey,
37452
+ dstChainKey: baseCtx.dstChainKey
37453
+ })
37454
+ };
37455
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
37456
+ } catch (error) {
37457
+ if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
37458
+ return {
37459
+ ok: false,
37460
+ error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
37461
+ };
37462
+ }
37463
+ },
37464
+ {
37465
+ start: () => ({
37466
+ srcChainKey: _params.params.srcChainKey,
37467
+ srcAddress: _params.params.srcAddress,
37468
+ dstChainKey: _params.params.dstChainKey,
37469
+ dstAddress: _params.params.dstAddress,
37470
+ token: _params.params.token,
37471
+ amount: _params.params.amount
37472
+ }),
37473
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
37474
+ failure: (error) => ({ code: error.code })
36023
37475
  }
36024
- const packet = await relayTxAndWaitPacket({
36025
- srcTxHash: txResult.value.tx,
36026
- data: txResult.value.relayData,
36027
- chainKey: srcChainKey,
36028
- relayerApiEndpoint: this.relayerApiEndpoint,
36029
- timeout
36030
- });
36031
- if (!packet.ok)
36032
- return {
36033
- ok: false,
36034
- error: mapRelayFailure(packet.error, {
36035
- feature: "moneyMarket",
36036
- action: baseCtx.action,
36037
- srcChainKey: baseCtx.srcChainKey,
36038
- dstChainKey: baseCtx.dstChainKey
36039
- })
36040
- };
36041
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
36042
- } catch (error) {
36043
- if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
36044
- return {
36045
- ok: false,
36046
- error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
36047
- };
36048
- }
37476
+ );
36049
37477
  }
36050
37478
  /**
36051
37479
  * Build and optionally broadcast the spoke-side supply transaction without waiting for the
@@ -36162,58 +37590,76 @@ var MoneyMarketService = class _MoneyMarketService {
36162
37590
  * `dstChainTxHash` (hub delivery or relay destination).
36163
37591
  */
36164
37592
  async borrow(_params) {
36165
- const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
36166
- const srcChainKey = params.srcChainKey;
36167
- const hubChainId = this.hubProvider.chainConfig.chain.key;
36168
- const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "borrow" };
36169
- try {
36170
- const txResult = await this.createBorrowIntent(_params);
36171
- if (!txResult.ok) return { ok: false, error: txResult.error };
36172
- const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
36173
- if (!verify.ok) {
36174
- return {
36175
- ok: false,
36176
- error: verifyFailed("moneyMarket", verify.error, baseCtx)
36177
- };
36178
- }
36179
- const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId;
36180
- if (!needsRelay) {
36181
- return {
36182
- ok: true,
36183
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
36184
- };
36185
- }
36186
- const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
36187
- const packet = await relayTxAndWaitPacket({
36188
- ...relayIdentity,
36189
- chainKey: srcChainKey,
36190
- relayerApiEndpoint: this.relayerApiEndpoint,
36191
- timeout
36192
- });
36193
- if (!packet.ok)
36194
- return {
36195
- ok: false,
36196
- error: mapRelayFailure(packet.error, {
36197
- feature: "moneyMarket",
36198
- action: baseCtx.action,
36199
- srcChainKey: baseCtx.srcChainKey,
36200
- dstChainKey: baseCtx.dstChainKey
36201
- })
36202
- };
36203
- return {
36204
- ok: true,
36205
- value: {
36206
- srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
36207
- dstChainTxHash: packet.value.dst_tx_hash
37593
+ return this.config.analytics.trackResult(
37594
+ "moneyMarket",
37595
+ "borrow",
37596
+ async () => {
37597
+ const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
37598
+ const srcChainKey = params.srcChainKey;
37599
+ const hubChainId = this.hubProvider.chainConfig.chain.key;
37600
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "borrow" };
37601
+ try {
37602
+ const txResult = await this.createBorrowIntent(_params);
37603
+ if (!txResult.ok) return { ok: false, error: txResult.error };
37604
+ const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
37605
+ if (!verify.ok) {
37606
+ return {
37607
+ ok: false,
37608
+ error: verifyFailed("moneyMarket", verify.error, baseCtx)
37609
+ };
37610
+ }
37611
+ const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId;
37612
+ if (!needsRelay) {
37613
+ return {
37614
+ ok: true,
37615
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
37616
+ };
37617
+ }
37618
+ const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
37619
+ const packet = await relayTxAndWaitPacket({
37620
+ ...relayIdentity,
37621
+ chainKey: srcChainKey,
37622
+ relayerApiEndpoint: this.relayerApiEndpoint,
37623
+ timeout
37624
+ });
37625
+ if (!packet.ok)
37626
+ return {
37627
+ ok: false,
37628
+ error: mapRelayFailure(packet.error, {
37629
+ feature: "moneyMarket",
37630
+ action: baseCtx.action,
37631
+ srcChainKey: baseCtx.srcChainKey,
37632
+ dstChainKey: baseCtx.dstChainKey
37633
+ })
37634
+ };
37635
+ return {
37636
+ ok: true,
37637
+ value: {
37638
+ srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
37639
+ dstChainTxHash: packet.value.dst_tx_hash
37640
+ }
37641
+ };
37642
+ } catch (error) {
37643
+ if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
37644
+ return {
37645
+ ok: false,
37646
+ error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
37647
+ };
36208
37648
  }
36209
- };
36210
- } catch (error) {
36211
- if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
36212
- return {
36213
- ok: false,
36214
- error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
36215
- };
36216
- }
37649
+ },
37650
+ {
37651
+ start: () => ({
37652
+ srcChainKey: _params.params.srcChainKey,
37653
+ srcAddress: _params.params.srcAddress,
37654
+ dstChainKey: _params.params.dstChainKey,
37655
+ dstAddress: _params.params.dstAddress,
37656
+ token: _params.params.token,
37657
+ amount: _params.params.amount
37658
+ }),
37659
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
37660
+ failure: (error) => ({ code: error.code })
37661
+ }
37662
+ );
36217
37663
  }
36218
37664
  /**
36219
37665
  * Build and optionally broadcast the spoke-side borrow message without waiting for the
@@ -36311,59 +37757,77 @@ var MoneyMarketService = class _MoneyMarketService {
36311
37757
  * `dstChainTxHash` (hub or relay destination).
36312
37758
  */
36313
37759
  async withdraw(_params) {
36314
- const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
36315
- const srcChainKey = params.srcChainKey;
36316
- const hubChainId = this.hubProvider.chainConfig.chain.key;
36317
- const walletRouter = this.hubProvider.chainConfig.addresses.walletRouter;
36318
- const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "withdraw" };
36319
- try {
36320
- const txResult = await this.createWithdrawIntent(_params);
36321
- if (!txResult.ok) return { ok: false, error: txResult.error };
36322
- const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
36323
- if (!verify.ok) {
36324
- return {
36325
- ok: false,
36326
- error: verifyFailed("moneyMarket", verify.error, baseCtx)
36327
- };
36328
- }
36329
- const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId && params.dstAddress !== walletRouter;
36330
- if (!needsRelay) {
36331
- return {
36332
- ok: true,
36333
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
36334
- };
36335
- }
36336
- const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
36337
- const packet = await relayTxAndWaitPacket({
36338
- ...relayIdentity,
36339
- chainKey: srcChainKey,
36340
- relayerApiEndpoint: this.relayerApiEndpoint,
36341
- timeout
36342
- });
36343
- if (!packet.ok)
36344
- return {
36345
- ok: false,
36346
- error: mapRelayFailure(packet.error, {
36347
- feature: "moneyMarket",
36348
- action: baseCtx.action,
36349
- srcChainKey: baseCtx.srcChainKey,
36350
- dstChainKey: baseCtx.dstChainKey
36351
- })
36352
- };
36353
- return {
36354
- ok: true,
36355
- value: {
36356
- srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
36357
- dstChainTxHash: packet.value.dst_tx_hash
37760
+ return this.config.analytics.trackResult(
37761
+ "moneyMarket",
37762
+ "withdraw",
37763
+ async () => {
37764
+ const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
37765
+ const srcChainKey = params.srcChainKey;
37766
+ const hubChainId = this.hubProvider.chainConfig.chain.key;
37767
+ const walletRouter = this.hubProvider.chainConfig.addresses.walletRouter;
37768
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "withdraw" };
37769
+ try {
37770
+ const txResult = await this.createWithdrawIntent(_params);
37771
+ if (!txResult.ok) return { ok: false, error: txResult.error };
37772
+ const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
37773
+ if (!verify.ok) {
37774
+ return {
37775
+ ok: false,
37776
+ error: verifyFailed("moneyMarket", verify.error, baseCtx)
37777
+ };
37778
+ }
37779
+ const needsRelay = srcChainKey !== hubChainId || params.dstChainKey != null && params.dstAddress != null && params.dstChainKey !== hubChainId && params.dstAddress !== walletRouter;
37780
+ if (!needsRelay) {
37781
+ return {
37782
+ ok: true,
37783
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
37784
+ };
37785
+ }
37786
+ const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
37787
+ const packet = await relayTxAndWaitPacket({
37788
+ ...relayIdentity,
37789
+ chainKey: srcChainKey,
37790
+ relayerApiEndpoint: this.relayerApiEndpoint,
37791
+ timeout
37792
+ });
37793
+ if (!packet.ok)
37794
+ return {
37795
+ ok: false,
37796
+ error: mapRelayFailure(packet.error, {
37797
+ feature: "moneyMarket",
37798
+ action: baseCtx.action,
37799
+ srcChainKey: baseCtx.srcChainKey,
37800
+ dstChainKey: baseCtx.dstChainKey
37801
+ })
37802
+ };
37803
+ return {
37804
+ ok: true,
37805
+ value: {
37806
+ srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
37807
+ dstChainTxHash: packet.value.dst_tx_hash
37808
+ }
37809
+ };
37810
+ } catch (error) {
37811
+ if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
37812
+ return {
37813
+ ok: false,
37814
+ error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
37815
+ };
36358
37816
  }
36359
- };
36360
- } catch (error) {
36361
- if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
36362
- return {
36363
- ok: false,
36364
- error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
36365
- };
36366
- }
37817
+ },
37818
+ {
37819
+ start: () => ({
37820
+ srcChainKey: _params.params.srcChainKey,
37821
+ srcAddress: _params.params.srcAddress,
37822
+ dstChainKey: _params.params.dstChainKey,
37823
+ dstAddress: _params.params.dstAddress,
37824
+ token: _params.params.token,
37825
+ amount: _params.params.amount
37826
+ }),
37827
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
37828
+ failure: (error) => ({ code: error.code })
37829
+ }
37830
+ );
36367
37831
  }
36368
37832
  /**
36369
37833
  * Build and optionally broadcast the spoke-side withdraw message without waiting for the
@@ -36459,50 +37923,68 @@ var MoneyMarketService = class _MoneyMarketService {
36459
37923
  * @returns A pair of transaction hashes — `srcChainTxHash` (spoke) and `dstChainTxHash` (hub).
36460
37924
  */
36461
37925
  async repay(_params) {
36462
- const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
36463
- const srcChainKey = params.srcChainKey;
36464
- const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "repay" };
36465
- try {
36466
- const txResult = await this.createRepayIntent(_params);
36467
- if (!txResult.ok) return { ok: false, error: txResult.error };
36468
- const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
36469
- if (!verify.ok) {
36470
- return {
36471
- ok: false,
36472
- error: verifyFailed("moneyMarket", verify.error, baseCtx)
36473
- };
36474
- }
36475
- if (isHubChainKeyType(srcChainKey)) {
36476
- return {
36477
- ok: true,
36478
- value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
36479
- };
37926
+ return this.config.analytics.trackResult(
37927
+ "moneyMarket",
37928
+ "repay",
37929
+ async () => {
37930
+ const { params, timeout = DEFAULT_RELAY_TX_TIMEOUT } = _params;
37931
+ const srcChainKey = params.srcChainKey;
37932
+ const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "repay" };
37933
+ try {
37934
+ const txResult = await this.createRepayIntent(_params);
37935
+ if (!txResult.ok) return { ok: false, error: txResult.error };
37936
+ const verify = await this.spoke.verifyTxHash({ txHash: txResult.value.tx, chainKey: srcChainKey });
37937
+ if (!verify.ok) {
37938
+ return {
37939
+ ok: false,
37940
+ error: verifyFailed("moneyMarket", verify.error, baseCtx)
37941
+ };
37942
+ }
37943
+ if (isHubChainKeyType(srcChainKey)) {
37944
+ return {
37945
+ ok: true,
37946
+ value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
37947
+ };
37948
+ }
37949
+ const packet = await relayTxAndWaitPacket({
37950
+ srcTxHash: txResult.value.tx,
37951
+ data: txResult.value.relayData,
37952
+ chainKey: srcChainKey,
37953
+ relayerApiEndpoint: this.relayerApiEndpoint,
37954
+ timeout
37955
+ });
37956
+ if (!packet.ok)
37957
+ return {
37958
+ ok: false,
37959
+ error: mapRelayFailure(packet.error, {
37960
+ feature: "moneyMarket",
37961
+ action: baseCtx.action,
37962
+ srcChainKey: baseCtx.srcChainKey,
37963
+ dstChainKey: baseCtx.dstChainKey
37964
+ })
37965
+ };
37966
+ return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
37967
+ } catch (error) {
37968
+ if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
37969
+ return {
37970
+ ok: false,
37971
+ error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
37972
+ };
37973
+ }
37974
+ },
37975
+ {
37976
+ start: () => ({
37977
+ srcChainKey: _params.params.srcChainKey,
37978
+ srcAddress: _params.params.srcAddress,
37979
+ dstChainKey: _params.params.dstChainKey,
37980
+ dstAddress: _params.params.dstAddress,
37981
+ token: _params.params.token,
37982
+ amount: _params.params.amount
37983
+ }),
37984
+ success: (value) => ({ srcChainTxHash: value.srcChainTxHash, dstChainTxHash: value.dstChainTxHash }),
37985
+ failure: (error) => ({ code: error.code })
36480
37986
  }
36481
- const packet = await relayTxAndWaitPacket({
36482
- srcTxHash: txResult.value.tx,
36483
- data: txResult.value.relayData,
36484
- chainKey: srcChainKey,
36485
- relayerApiEndpoint: this.relayerApiEndpoint,
36486
- timeout
36487
- });
36488
- if (!packet.ok)
36489
- return {
36490
- ok: false,
36491
- error: mapRelayFailure(packet.error, {
36492
- feature: "moneyMarket",
36493
- action: baseCtx.action,
36494
- srcChainKey: baseCtx.srcChainKey,
36495
- dstChainKey: baseCtx.dstChainKey
36496
- })
36497
- };
36498
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
36499
- } catch (error) {
36500
- if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
36501
- return {
36502
- ok: false,
36503
- error: executionFailed("moneyMarket", error, { ...baseCtx, phase: "intentCreation" })
36504
- };
36505
- }
37987
+ );
36506
37988
  }
36507
37989
  /**
36508
37990
  * Build and optionally broadcast the spoke-side repay transaction without waiting for the
@@ -37203,50 +38685,67 @@ var PartnerFeeClaimService = class {
37203
38685
  * unsigned raw transaction (`raw: true`). Returns an error on failure.
37204
38686
  */
37205
38687
  async setSwapPreference(_params) {
37206
- const { params, walletProvider, raw } = _params;
37207
- try {
37208
- invariant(isHubChainKeyType(params.srcChainKey), "PartnerFeeClaimService only supports Sonic spoke provider");
37209
- invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
37210
- const outputToken = params.dstChainKey !== this.hubProvider.chainConfig.chain.key ? this.hubProvider.config.getSpokeTokenFromOriginalAssetAddress(params.dstChainKey, params.outputToken)?.hubAsset : params.outputToken;
37211
- invariant(
37212
- outputToken,
37213
- `hub asset not found for spoke chain token (params.outputToken): ${params.outputToken} with chain key: ${params.dstChainKey}`
37214
- );
37215
- const rawTx = {
37216
- from: params.srcAddress,
37217
- to: this.protocolIntentsContract,
37218
- value: 0n,
37219
- data: viem.encodeFunctionData({
37220
- abi: ProtocolIntentsAbi,
37221
- functionName: "setAutoSwapPreferences",
37222
- args: [
38688
+ return this.config.analytics.trackResult(
38689
+ "partner",
38690
+ "setSwapPreference",
38691
+ async () => {
38692
+ const { params, walletProvider, raw } = _params;
38693
+ try {
38694
+ invariant(isHubChainKeyType(params.srcChainKey), "PartnerFeeClaimService only supports Sonic spoke provider");
38695
+ invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
38696
+ const outputToken = params.dstChainKey !== this.hubProvider.chainConfig.chain.key ? this.hubProvider.config.getSpokeTokenFromOriginalAssetAddress(params.dstChainKey, params.outputToken)?.hubAsset : params.outputToken;
38697
+ invariant(
37223
38698
  outputToken,
37224
- BigInt(getIntentRelayChainId(params.dstChainKey)),
37225
- encodeAddress(params.dstChainKey, params.dstAddress)
37226
- ]
37227
- })
37228
- };
37229
- if (raw) {
37230
- return {
37231
- ok: true,
37232
- value: rawTx
37233
- };
38699
+ `hub asset not found for spoke chain token (params.outputToken): ${params.outputToken} with chain key: ${params.dstChainKey}`
38700
+ );
38701
+ const rawTx = {
38702
+ from: params.srcAddress,
38703
+ to: this.protocolIntentsContract,
38704
+ value: 0n,
38705
+ data: viem.encodeFunctionData({
38706
+ abi: ProtocolIntentsAbi,
38707
+ functionName: "setAutoSwapPreferences",
38708
+ args: [
38709
+ outputToken,
38710
+ BigInt(getIntentRelayChainId(params.dstChainKey)),
38711
+ encodeAddress(params.dstChainKey, params.dstAddress)
38712
+ ]
38713
+ })
38714
+ };
38715
+ if (raw) {
38716
+ return {
38717
+ ok: true,
38718
+ value: rawTx
38719
+ };
38720
+ }
38721
+ invariant(
38722
+ isEvmWalletProviderType(walletProvider),
38723
+ "PartnerFeeClaimService only supports Evm (sonic) wallet provider"
38724
+ );
38725
+ const txHash = await walletProvider.sendTransaction(rawTx);
38726
+ return {
38727
+ ok: true,
38728
+ value: txHash
38729
+ };
38730
+ } catch (error) {
38731
+ return {
38732
+ ok: false,
38733
+ error
38734
+ };
38735
+ }
38736
+ },
38737
+ {
38738
+ start: () => ({
38739
+ srcChainKey: _params.params.srcChainKey,
38740
+ srcAddress: _params.params.srcAddress,
38741
+ outputToken: _params.params.outputToken,
38742
+ dstChainKey: _params.params.dstChainKey,
38743
+ dstAddress: _params.params.dstAddress
38744
+ }),
38745
+ success: (value) => ({ txHash: value }),
38746
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
37234
38747
  }
37235
- invariant(
37236
- isEvmWalletProviderType(walletProvider),
37237
- "PartnerFeeClaimService only supports Evm (sonic) wallet provider"
37238
- );
37239
- const txHash = await walletProvider.sendTransaction(rawTx);
37240
- return {
37241
- ok: true,
37242
- value: txHash
37243
- };
37244
- } catch (error) {
37245
- return {
37246
- ok: false,
37247
- error
37248
- };
37249
- }
38748
+ );
37250
38749
  }
37251
38750
  /**
37252
38751
  * Checks whether a hub-chain ERC-20 token is already approved for the ProtocolIntents contract.
@@ -37358,6 +38857,11 @@ var PartnerFeeClaimService = class {
37358
38857
  * This is the low-level building block. Use `swap` for the full flow that also waits for
37359
38858
  * the solver to execute the intent.
37360
38859
  *
38860
+ * Guards against same-token intents: if the configured output token equals `fromToken` the solver
38861
+ * cannot fill the swap, so the call is rejected with `VALIDATION_FAILED` before any transaction is
38862
+ * built. The guard fails closed — if the preference lookup fails the call returns that error rather
38863
+ * than submitting an intent that may become unfillable.
38864
+ *
37361
38865
  * @param _params - Action descriptor containing:
37362
38866
  * - `params.srcChainKey` — must be the hub chain key (Sonic).
37363
38867
  * - `params.srcAddress` — partner's EVM address; also used as the intent creator.
@@ -37381,6 +38885,25 @@ var PartnerFeeClaimService = class {
37381
38885
  this.config.solver.protocolIntentsContract,
37382
38886
  "protocolIntentsContract is not configured in solver config"
37383
38887
  );
38888
+ const prefs = await this.getAutoSwapPreferences(params.srcAddress);
38889
+ if (!prefs.ok) return prefs;
38890
+ if (prefs.value.outputToken.toLowerCase() === params.fromToken.toLowerCase()) {
38891
+ return {
38892
+ ok: false,
38893
+ error: new SodaxError(
38894
+ "VALIDATION_FAILED",
38895
+ "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.",
38896
+ {
38897
+ feature: "partner",
38898
+ context: {
38899
+ action: "createIntentAutoSwap",
38900
+ fromToken: params.fromToken,
38901
+ outputToken: prefs.value.outputToken
38902
+ }
38903
+ }
38904
+ )
38905
+ };
38906
+ }
37384
38907
  const minOutputAmount = 0n;
37385
38908
  const rawTx = {
37386
38909
  from: params.srcAddress,
@@ -37428,47 +38951,218 @@ var PartnerFeeClaimService = class {
37428
38951
  *
37429
38952
  * On failure the `error` is tagged:
37430
38953
  * - `WAIT_INTENT_AUTO_SWAP_FAILED` — transaction was submitted but receipt polling failed.
37431
- * - Error from `createIntentAutoSwap` — if the initial submission failed.
38954
+ * - Error from `createIntentAutoSwap` — if the initial submission failed, including
38955
+ * `VALIDATION_FAILED` when the output token equals the fee token (same-token guard) or the
38956
+ * preference lookup that backs the guard fails.
37432
38957
  * - Error from `SolverApiService.postExecution` — if the solver notification failed.
37433
38958
  */
37434
38959
  async swap(_params) {
37435
- try {
37436
- const txHash = await this.createIntentAutoSwap(_params);
37437
- if (!txHash.ok) {
37438
- return txHash;
38960
+ return this.config.analytics.trackResult(
38961
+ "partner",
38962
+ "swap",
38963
+ async () => {
38964
+ try {
38965
+ const txHash = await this.createIntentAutoSwap(_params);
38966
+ if (!txHash.ok) {
38967
+ return txHash;
38968
+ }
38969
+ let intentTxHash;
38970
+ try {
38971
+ const receipt = await this.hubProvider.publicClient.waitForTransactionReceipt({ hash: txHash.value });
38972
+ intentTxHash = receipt.transactionHash;
38973
+ } catch (error) {
38974
+ return {
38975
+ ok: false,
38976
+ error: new SodaxError(
38977
+ "EXECUTION_FAILED",
38978
+ error instanceof Error ? error.message : "waitIntentAutoSwap failed",
38979
+ { feature: "partner", cause: error, context: { action: "waitAutoSwap", phase: "execution" } }
38980
+ )
38981
+ };
38982
+ }
38983
+ const solverExecutionResponse = await SolverApiService.postExecution(
38984
+ { intent_tx_hash: intentTxHash },
38985
+ this.config.solver,
38986
+ this.config.logger
38987
+ );
38988
+ if (!solverExecutionResponse.ok) {
38989
+ return solverExecutionResponse;
38990
+ }
38991
+ return {
38992
+ ok: true,
38993
+ value: {
38994
+ srcTxHash: txHash.value,
38995
+ solverExecutionResponse: solverExecutionResponse.value,
38996
+ intentTxHash
38997
+ }
38998
+ };
38999
+ } catch (error) {
39000
+ return { ok: false, error };
39001
+ }
39002
+ },
39003
+ {
39004
+ start: () => ({
39005
+ srcChainKey: _params.params.srcChainKey,
39006
+ srcAddress: _params.params.srcAddress,
39007
+ fromToken: _params.params.fromToken,
39008
+ amount: _params.params.amount
39009
+ }),
39010
+ success: (value) => ({
39011
+ srcTxHash: value.srcTxHash,
39012
+ intentTxHash: value.intentTxHash
39013
+ }),
39014
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
37439
39015
  }
37440
- let intentTxHash;
37441
- try {
37442
- const receipt = await this.hubProvider.publicClient.waitForTransactionReceipt({ hash: txHash.value });
37443
- intentTxHash = receipt.transactionHash;
37444
- } catch (error) {
39016
+ );
39017
+ }
39018
+ /**
39019
+ * Cancels a partner fee-claim auto-swap intent and refunds the input token to the partner.
39020
+ *
39021
+ * This targets the ProtocolIntents contract's own `cancelIntent(fromToken, toToken)`, which is
39022
+ * the only authorized cancel path for partner auto-swap intents: the intent's `creator` is the
39023
+ * ProtocolIntents contract (not the partner), so the generic `SwapService.cancelIntent` reverts
39024
+ * with `Unauthorized()`. ProtocolIntents looks up the caller's intent for the token pair, cancels
39025
+ * it in the main intents contract (as the creator), and transfers the locked `inputAmount` back
39026
+ * to the partner (`msg.sender`).
39027
+ *
39028
+ * Use this to recover funds stuck in an unfillable intent — most commonly a same-token claim
39029
+ * (`fromToken === toToken`) the solver refused to fill (see the guard in {@link swap}).
39030
+ *
39031
+ * @param _params - Action descriptor containing:
39032
+ * - `params.srcChainKey` — must be the hub chain key (Sonic).
39033
+ * - `params.srcAddress` — the partner's EVM address; must match the intent's owner.
39034
+ * - `params.fromToken` — the stuck intent's input (fee) token, hub-chain address.
39035
+ * - `params.toToken` — the stuck intent's output token, hub-chain address.
39036
+ * - `raw` — when `true`, returns the unsigned transaction object instead of submitting it.
39037
+ * - `walletProvider` — required when `raw` is `false`; must be an EVM wallet provider.
39038
+ * @returns A `Result` containing the submitted transaction hash (`raw: false`) or the unsigned
39039
+ * raw transaction (`raw: true`).
39040
+ */
39041
+ async cancelIntent(_params) {
39042
+ return this.config.analytics.trackResult(
39043
+ "partner",
39044
+ "cancelIntent",
39045
+ async () => {
39046
+ const { params } = _params;
39047
+ try {
39048
+ invariant(isHubChainKeyType(params.srcChainKey), "PartnerFeeClaimService only supports Hub srcChainKey");
39049
+ invariant(
39050
+ isOptionalEvmWalletProviderType(_params.walletProvider),
39051
+ "PartnerFeeClaimService only supports Evm wallet provider"
39052
+ );
39053
+ invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
39054
+ const rawTx = {
39055
+ from: params.srcAddress,
39056
+ to: this.protocolIntentsContract,
39057
+ value: 0n,
39058
+ data: viem.encodeFunctionData({
39059
+ abi: ProtocolIntentsAbi,
39060
+ functionName: "cancelIntent",
39061
+ args: [params.fromToken, params.toToken]
39062
+ })
39063
+ };
39064
+ if (_params.raw) {
39065
+ return {
39066
+ ok: true,
39067
+ value: rawTx
39068
+ };
39069
+ }
39070
+ const txHash = await _params.walletProvider.sendTransaction(rawTx);
39071
+ return {
39072
+ ok: true,
39073
+ value: txHash
39074
+ };
39075
+ } catch (error) {
39076
+ return { ok: false, error };
39077
+ }
39078
+ },
39079
+ {
39080
+ start: () => ({
39081
+ srcChainKey: _params.params.srcChainKey,
39082
+ srcAddress: _params.params.srcAddress,
39083
+ fromToken: _params.params.fromToken,
39084
+ toToken: _params.params.toToken
39085
+ }),
39086
+ success: (value) => ({ txHash: value }),
39087
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
39088
+ }
39089
+ );
39090
+ }
39091
+ /**
39092
+ * Returns the stored intent hash for a partner's `(user, fromToken, toToken)` token pair.
39093
+ *
39094
+ * A non-zero hash means an auto-swap intent currently exists for that pair (e.g. an unfilled
39095
+ * same-token claim) and can be cancelled via {@link cancelIntent}. A zero hash
39096
+ * (`0x000…0`) means there is no open intent for the pair.
39097
+ *
39098
+ * @param params.user - The partner's EVM address on Sonic.
39099
+ * @param params.fromToken - Input (fee) token, hub-chain address.
39100
+ * @param params.toToken - Output token, hub-chain address.
39101
+ * @returns A `Result` containing the intent hash, or an `Error` tagged `LOOKUP_FAILED`.
39102
+ */
39103
+ async getUserIntent({ user, fromToken, toToken }) {
39104
+ try {
39105
+ invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
39106
+ const intentHash = await this.hubProvider.publicClient.readContract({
39107
+ address: this.protocolIntentsContract,
39108
+ abi: ProtocolIntentsAbi,
39109
+ functionName: "getUserIntent",
39110
+ args: [user, fromToken, toToken]
39111
+ });
39112
+ return { ok: true, value: intentHash };
39113
+ } catch (error) {
39114
+ return { ok: false, error: lookupFailed("partner", "getUserIntent", error) };
39115
+ }
39116
+ }
39117
+ /**
39118
+ * Reads the full {@link Intent} details for a stored intent hash from the ProtocolIntents contract.
39119
+ *
39120
+ * Useful for displaying a stuck intent before cancelling it — e.g. the locked `inputAmount` and
39121
+ * `inputToken`. Pair with {@link getUserIntent} to resolve the hash from a token pair.
39122
+ *
39123
+ * @param intentHash - The intent hash (from {@link getUserIntent}).
39124
+ * @returns A `Result` containing the `Intent`, or an `Error` tagged `LOOKUP_FAILED`.
39125
+ */
39126
+ async getIntentDetails(intentHash) {
39127
+ try {
39128
+ invariant(this.protocolIntentsContract, "protocolIntentsContract is not configured in solver config");
39129
+ const intent = await this.hubProvider.publicClient.readContract({
39130
+ address: this.protocolIntentsContract,
39131
+ abi: ProtocolIntentsAbi,
39132
+ functionName: "getIntentDetails",
39133
+ args: [intentHash]
39134
+ });
39135
+ if (!this.config.isValidIntentRelayChainId(intent.srcChain) || !this.config.isValidIntentRelayChainId(intent.dstChain)) {
37445
39136
  return {
37446
39137
  ok: false,
37447
- error: new SodaxError(
37448
- "EXECUTION_FAILED",
37449
- error instanceof Error ? error.message : "waitIntentAutoSwap failed",
37450
- { feature: "partner", cause: error, context: { action: "waitAutoSwap", phase: "execution" } }
39138
+ error: lookupFailed(
39139
+ "partner",
39140
+ "getIntentDetails",
39141
+ new Error(`Invalid intent relay chain id: ${intent.srcChain} or ${intent.dstChain}`)
37451
39142
  )
37452
39143
  };
37453
39144
  }
37454
- const solverExecutionResponse = await SolverApiService.postExecution(
37455
- { intent_tx_hash: intentTxHash },
37456
- this.config.solver,
37457
- this.config.logger
37458
- );
37459
- if (!solverExecutionResponse.ok) {
37460
- return solverExecutionResponse;
37461
- }
37462
39145
  return {
37463
39146
  ok: true,
37464
39147
  value: {
37465
- srcTxHash: txHash.value,
37466
- solverExecutionResponse: solverExecutionResponse.value,
37467
- intentTxHash
39148
+ intentId: intent.intentId,
39149
+ creator: intent.creator,
39150
+ inputToken: intent.inputToken,
39151
+ outputToken: intent.outputToken,
39152
+ inputAmount: intent.inputAmount,
39153
+ minOutputAmount: intent.minOutputAmount,
39154
+ deadline: intent.deadline,
39155
+ allowPartialFill: intent.allowPartialFill,
39156
+ srcChain: intent.srcChain,
39157
+ dstChain: intent.dstChain,
39158
+ srcAddress: intent.srcAddress,
39159
+ dstAddress: intent.dstAddress,
39160
+ solver: intent.solver,
39161
+ data: intent.data
37468
39162
  }
37469
39163
  };
37470
39164
  } catch (error) {
37471
- return { ok: false, error };
39165
+ return { ok: false, error: lookupFailed("partner", "getIntentDetails", error) };
37472
39166
  }
37473
39167
  }
37474
39168
  };
@@ -37603,42 +39297,62 @@ var RecoveryService = class {
37603
39297
  * transaction object when `raw: true`, or the broadcast transaction hash when `raw: false`.
37604
39298
  */
37605
39299
  async withdrawHubAsset(_params) {
37606
- const { params } = _params;
37607
- try {
37608
- const hubWallet = await this.hubProvider.getUserHubWalletAddress(params.srcAddress, params.srcChainKey);
37609
- const payload = EvmAssetManagerService.withdrawAssetData(
37610
- {
37611
- token: params.token,
37612
- to: encodeAddress(params.srcChainKey, params.srcAddress),
37613
- amount: params.amount
37614
- },
37615
- this.hubProvider,
37616
- params.srcChainKey
37617
- );
37618
- const coreParams = {
37619
- srcChainKey: params.srcChainKey,
37620
- srcAddress: params.srcAddress,
37621
- dstChainKey: this.hubProvider.chainConfig.chain.key,
37622
- dstAddress: hubWallet,
37623
- payload
37624
- };
37625
- const sendMessageParams = _params.raw ? { ...coreParams, raw: true } : { ...coreParams, raw: false, walletProvider: _params.walletProvider };
37626
- const txResult = await this.spoke.sendMessage(sendMessageParams);
37627
- if (!txResult.ok) return txResult;
37628
- return {
37629
- ok: true,
37630
- value: txResult.value
37631
- };
37632
- } catch (error) {
37633
- return {
37634
- ok: false,
37635
- error: new SodaxError("EXECUTION_FAILED", error instanceof Error ? error.message : "withdrawHubAsset failed", {
37636
- feature: "recovery",
37637
- cause: error,
37638
- context: { action: "withdrawHubAsset", phase: "execution" }
37639
- })
37640
- };
37641
- }
39300
+ return this.config.analytics.trackResult(
39301
+ "recovery",
39302
+ "withdrawHubAsset",
39303
+ async () => {
39304
+ const { params } = _params;
39305
+ try {
39306
+ const hubWallet = await this.hubProvider.getUserHubWalletAddress(params.srcAddress, params.srcChainKey);
39307
+ const payload = EvmAssetManagerService.withdrawAssetData(
39308
+ {
39309
+ token: params.token,
39310
+ to: encodeAddress(params.srcChainKey, params.srcAddress),
39311
+ amount: params.amount
39312
+ },
39313
+ this.hubProvider,
39314
+ params.srcChainKey
39315
+ );
39316
+ const coreParams = {
39317
+ srcChainKey: params.srcChainKey,
39318
+ srcAddress: params.srcAddress,
39319
+ dstChainKey: this.hubProvider.chainConfig.chain.key,
39320
+ dstAddress: hubWallet,
39321
+ payload
39322
+ };
39323
+ const sendMessageParams = _params.raw ? { ...coreParams, raw: true } : {
39324
+ ...coreParams,
39325
+ raw: false,
39326
+ walletProvider: _params.walletProvider
39327
+ };
39328
+ const txResult = await this.spoke.sendMessage(sendMessageParams);
39329
+ if (!txResult.ok) return txResult;
39330
+ return {
39331
+ ok: true,
39332
+ value: txResult.value
39333
+ };
39334
+ } catch (error) {
39335
+ return {
39336
+ ok: false,
39337
+ error: new SodaxError(
39338
+ "EXECUTION_FAILED",
39339
+ error instanceof Error ? error.message : "withdrawHubAsset failed",
39340
+ { feature: "recovery", cause: error, context: { action: "withdrawHubAsset", phase: "execution" } }
39341
+ )
39342
+ };
39343
+ }
39344
+ },
39345
+ {
39346
+ start: () => ({
39347
+ srcChainKey: _params.params.srcChainKey,
39348
+ srcAddress: _params.params.srcAddress,
39349
+ token: _params.params.token,
39350
+ amount: _params.params.amount
39351
+ }),
39352
+ success: (value) => ({ txHash: typeof value === "string" ? value : void 0 }),
39353
+ failure: (error) => ({ code: isSodaxError(error) ? error.code : void 0 })
39354
+ }
39355
+ );
37642
39356
  }
37643
39357
  };
37644
39358
 
@@ -37740,7 +39454,7 @@ var LeverageYieldService = class {
37740
39454
  }
37741
39455
  /** Looks up a vault by its `name` field. Returns `undefined` when not registered. */
37742
39456
  getVault(name) {
37743
- return this.listVaults().find((v) => v.name === name);
39457
+ return this.listVaults().find((v6) => v6.name === name);
37744
39458
  }
37745
39459
  /**
37746
39460
  * Looks up a registered vault by its on-chain proxy address (case-insensitive).
@@ -37748,7 +39462,7 @@ var LeverageYieldService = class {
37748
39462
  */
37749
39463
  getVaultByAddress(address) {
37750
39464
  const normalized = address.toLowerCase();
37751
- return this.listVaults().find((v) => v.vault.toLowerCase() === normalized);
39465
+ return this.listVaults().find((v6) => v6.vault.toLowerCase() === normalized);
37752
39466
  }
37753
39467
  /**
37754
39468
  * Resolves the intent `deadline`: returns the caller-supplied value verbatim, otherwise
@@ -38098,70 +39812,93 @@ var LeverageYieldService = class {
38098
39812
  const { params } = _params;
38099
39813
  const srcChainKey = params.srcChainKey;
38100
39814
  const baseCtx = { srcChainKey, dstChainKey: params.dstChainKey, action: "vaultSwap" };
38101
- try {
38102
- const timeout = _params.timeout;
38103
- const createIntentResult = await this.createVaultIntent(_params);
38104
- if (!createIntentResult.ok) {
38105
- return { ok: false, error: createIntentResult.error };
38106
- }
38107
- const { tx: spokeTxHash, intent, relayData } = createIntentResult.value;
38108
- const verifyTxHashResult = await this.spoke.verifyTxHash({
38109
- txHash: spokeTxHash,
38110
- chainKey: srcChainKey
38111
- });
38112
- if (!verifyTxHashResult.ok) {
38113
- return {
38114
- ok: false,
38115
- error: verifyFailed("leverageYield", verifyTxHashResult.error, baseCtx)
38116
- };
38117
- }
38118
- let dstIntentTxHash;
38119
- if (isHubChainKeyType(srcChainKey)) {
38120
- dstIntentTxHash = spokeTxHash;
38121
- } else {
38122
- const packet = await relayTxAndWaitPacket({
38123
- srcTxHash: spokeTxHash,
38124
- data: relayData,
38125
- chainKey: srcChainKey,
38126
- relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
38127
- timeout
38128
- });
38129
- if (!packet.ok) {
39815
+ return this.config.analytics.trackResult(
39816
+ "leverageYield",
39817
+ "vaultSwap",
39818
+ async () => {
39819
+ try {
39820
+ const timeout = _params.timeout;
39821
+ const createIntentResult = await this.createVaultIntent(_params);
39822
+ if (!createIntentResult.ok) {
39823
+ return { ok: false, error: createIntentResult.error };
39824
+ }
39825
+ const { tx: spokeTxHash, intent, relayData } = createIntentResult.value;
39826
+ const verifyTxHashResult = await this.spoke.verifyTxHash({
39827
+ txHash: spokeTxHash,
39828
+ chainKey: srcChainKey
39829
+ });
39830
+ if (!verifyTxHashResult.ok) {
39831
+ return {
39832
+ ok: false,
39833
+ error: verifyFailed("leverageYield", verifyTxHashResult.error, baseCtx)
39834
+ };
39835
+ }
39836
+ let dstIntentTxHash;
39837
+ if (isHubChainKeyType(srcChainKey)) {
39838
+ dstIntentTxHash = spokeTxHash;
39839
+ } else {
39840
+ const packet = await relayTxAndWaitPacket({
39841
+ srcTxHash: spokeTxHash,
39842
+ data: relayData,
39843
+ chainKey: srcChainKey,
39844
+ relayerApiEndpoint: this.config.relay.relayerApiEndpoint,
39845
+ timeout
39846
+ });
39847
+ if (!packet.ok) {
39848
+ return {
39849
+ ok: false,
39850
+ error: mapRelayFailure(packet.error, { feature: "leverageYield", ...baseCtx })
39851
+ };
39852
+ }
39853
+ dstIntentTxHash = packet.value.dst_tx_hash;
39854
+ }
39855
+ const postExecResult = await this.notifySolver({
39856
+ intent_tx_hash: dstIntentTxHash
39857
+ });
39858
+ if (!postExecResult.ok) {
39859
+ return { ok: false, error: postExecResult.error };
39860
+ }
39861
+ return {
39862
+ ok: true,
39863
+ value: {
39864
+ solverExecutionResponse: postExecResult.value,
39865
+ intent,
39866
+ intentDeliveryInfo: {
39867
+ srcChainKey,
39868
+ srcTxHash: spokeTxHash,
39869
+ srcAddress: params.srcAddress,
39870
+ dstChainKey: params.dstChainKey,
39871
+ dstTxHash: dstIntentTxHash,
39872
+ dstAddress: params.dstAddress
39873
+ }
39874
+ }
39875
+ };
39876
+ } catch (error) {
39877
+ if (isLeverageYieldSwapError(error)) return { ok: false, error };
38130
39878
  return {
38131
39879
  ok: false,
38132
- error: mapRelayFailure(packet.error, { feature: "leverageYield", ...baseCtx })
39880
+ error: unknownFailed("leverageYield", error, baseCtx)
38133
39881
  };
38134
39882
  }
38135
- dstIntentTxHash = packet.value.dst_tx_hash;
38136
- }
38137
- const postExecResult = await this.notifySolver({
38138
- intent_tx_hash: dstIntentTxHash
38139
- });
38140
- if (!postExecResult.ok) {
38141
- return { ok: false, error: postExecResult.error };
39883
+ },
39884
+ {
39885
+ start: () => ({
39886
+ srcChainKey: _params.params.srcChainKey,
39887
+ dstChainKey: _params.params.dstChainKey,
39888
+ srcAddress: _params.params.srcAddress,
39889
+ dstAddress: _params.params.dstAddress,
39890
+ inputToken: _params.params.inputToken,
39891
+ outputToken: _params.params.outputToken,
39892
+ inputAmount: _params.params.inputAmount
39893
+ }),
39894
+ success: (value) => ({
39895
+ intentId: value.intent.intentId,
39896
+ srcTxHash: value.intentDeliveryInfo.srcTxHash,
39897
+ dstTxHash: value.intentDeliveryInfo.dstTxHash
39898
+ }),
39899
+ failure: (error) => ({ code: error.code })
38142
39900
  }
38143
- return {
38144
- ok: true,
38145
- value: {
38146
- solverExecutionResponse: postExecResult.value,
38147
- intent,
38148
- intentDeliveryInfo: {
38149
- srcChainKey,
38150
- srcTxHash: spokeTxHash,
38151
- srcAddress: params.srcAddress,
38152
- dstChainKey: params.dstChainKey,
38153
- dstTxHash: dstIntentTxHash,
38154
- dstAddress: params.dstAddress
38155
- }
38156
- }
38157
- };
38158
- } catch (error) {
38159
- if (isLeverageYieldSwapError(error)) return { ok: false, error };
38160
- return {
38161
- ok: false,
38162
- error: unknownFailed("leverageYield", error, baseCtx)
38163
- };
38164
- }
39901
+ );
38165
39902
  }
38166
39903
  /**
38167
39904
  * Notifies the solver that the vault intent landed on the hub, triggering it to fill.
@@ -38518,6 +40255,8 @@ var Sodax = class {
38518
40255
  // ICX migration service enabling ICX migration to SODA
38519
40256
  backendApi;
38520
40257
  // backend API service enabling backend API endpoints
40258
+ api;
40259
+ // syntactic sugar for backend API service
38521
40260
  bridge;
38522
40261
  // Bridge service enabling cross-chain transfers
38523
40262
  staking;
@@ -38538,14 +40277,18 @@ var Sodax = class {
38538
40277
  // spoke service enabling spoke chain operations
38539
40278
  constructor(options) {
38540
40279
  const logger = resolveLogger(options?.logger);
40280
+ const analytics = resolveAnalytics(options?.analytics);
38541
40281
  const fee = options?.fee;
40282
+ const useBackendSubmitTx = options?.swapsOptions?.useBackendSubmitTx ?? false;
38542
40283
  this.instanceConfig = options ? mergeSodaxConfig(sodaxConfig, options) : sodaxConfig;
38543
40284
  this.backendApi = new BackendApiService(this.instanceConfig.api, logger);
40285
+ this.api = this.backendApi;
38544
40286
  this.config = new ConfigService({
38545
40287
  api: this.backendApi,
38546
40288
  config: this.instanceConfig,
38547
40289
  userConfig: options,
38548
40290
  logger,
40291
+ analytics,
38549
40292
  fee
38550
40293
  });
38551
40294
  this.hubProvider = new EvmHubProvider({ config: this.config });
@@ -38553,7 +40296,9 @@ var Sodax = class {
38553
40296
  this.swaps = new SwapService({
38554
40297
  config: this.config,
38555
40298
  hubProvider: this.hubProvider,
38556
- spoke: this.spoke
40299
+ spoke: this.spoke,
40300
+ backendApi: this.backendApi,
40301
+ useBackendSubmitTx
38557
40302
  });
38558
40303
  this.moneyMarket = new MoneyMarketService({
38559
40304
  config: this.config,
@@ -38753,6 +40498,7 @@ exports.StellarSpokeService = StellarSpokeService;
38753
40498
  exports.SuiSpokeService = SuiSpokeService;
38754
40499
  exports.SupportedMigrationTokens = SupportedMigrationTokens;
38755
40500
  exports.SwapService = SwapService;
40501
+ exports.SwapsApiService = SwapsApiService;
38756
40502
  exports.USD_DECIMALS = USD_DECIMALS;
38757
40503
  exports.UiPoolDataProviderService = UiPoolDataProviderService;
38758
40504
  exports.VAULT_TOKEN_DECIMALS = VAULT_TOKEN_DECIMALS;
@@ -38953,8 +40699,6 @@ exports.isStakingOrchestrationError = isStakingOrchestrationError;
38953
40699
  exports.isStellarChainKey = isStellarChainKey;
38954
40700
  exports.isStellarChainKeyType = isStellarChainKeyType;
38955
40701
  exports.isStellarWalletProviderType = isStellarWalletProviderType;
38956
- exports.isSubmitSwapTxResponse = isSubmitSwapTxResponse;
38957
- exports.isSubmitSwapTxStatusResponse = isSubmitSwapTxStatusResponse;
38958
40702
  exports.isSuiChainKey = isSuiChainKey;
38959
40703
  exports.isSuiChainKeyType = isSuiChainKeyType;
38960
40704
  exports.isSupportedBitcoinAddressType = isSupportedBitcoinAddressType;
@@ -38983,6 +40727,7 @@ exports.moneyMarketSupportedTokens = moneyMarketSupportedTokens;
38983
40727
  exports.nativeToUSD = nativeToUSD;
38984
40728
  exports.nearSupportedTokens = nearSupportedTokens;
38985
40729
  exports.newbnUSDSpokeChainIds = newbnUSDSpokeChainIds;
40730
+ exports.noopAnalytics = noopAnalytics;
38986
40731
  exports.normalize = normalize;
38987
40732
  exports.normalizeBN = normalizeBN;
38988
40733
  exports.normalizePsbtToBase64 = normalizePsbtToBase64;
@@ -39011,6 +40756,7 @@ exports.relayTxAndWaitPacket = relayTxAndWaitPacket;
39011
40756
  exports.requestAddress = requestAddress;
39012
40757
  exports.requestJsonRpc = requestJsonRpc;
39013
40758
  exports.requestSigning = requestSigning;
40759
+ exports.resolveAnalytics = resolveAnalytics;
39014
40760
  exports.resolveLogger = resolveLogger;
39015
40761
  exports.retry = retry;
39016
40762
  exports.reverseEncodeAddress = reverseEncodeAddress;