@sodax/sdk 2.0.0-rc.11 → 2.0.0-rc.12

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
@@ -3052,9 +3052,9 @@ var spokeChainConfig = {
3052
3052
  supportedTokens: bitcoinSupportedTokens,
3053
3053
  radfi: {
3054
3054
  walletMode: "TRADING",
3055
- apiUrl: "https://api.radfi.co/api",
3055
+ apiUrl: "https://api.bound.exchange/api",
3056
3056
  apiKey: "",
3057
- umsUrl: "https://ums.radfi.co/api",
3057
+ umsUrl: "https://api.ums.bound.exchange/api",
3058
3058
  accessToken: "",
3059
3059
  refreshToken: ""
3060
3060
  },
@@ -3896,6 +3896,9 @@ function detectBitcoinAddressType(address) {
3896
3896
  return "P2PKH";
3897
3897
  throw new Error(`Unknown Bitcoin address type: ${address}`);
3898
3898
  }
3899
+ function usesBip322MessageSigning(addressType) {
3900
+ return addressType === "P2WPKH" || addressType === "P2TR";
3901
+ }
3899
3902
  var BTC_WALLET_ADDRESS_TYPES = ["taproot", "segwit"];
3900
3903
  var BTC_ADDRESS_TYPES = ["P2PKH", "P2SH", "P2WPKH", "P2TR"];
3901
3904
 
@@ -4013,7 +4016,7 @@ function isValidWalletProviderForChainKey(chainKey, walletProvider) {
4013
4016
  }
4014
4017
 
4015
4018
  // ../types/dist/index.js
4016
- var CONFIG_VERSION = 209;
4019
+ var CONFIG_VERSION = 210;
4017
4020
  function isEvmSpokeChainConfig(value) {
4018
4021
  return typeof value === "object" && value !== null && value.chain.type === "EVM" && value.chain.key !== HUB_CHAIN_KEY;
4019
4022
  }
@@ -4440,7 +4443,7 @@ async function waitUntilIntentExecuted(payload) {
4440
4443
  }
4441
4444
  async function relayTxAndWaitPacket(params) {
4442
4445
  try {
4443
- const { srcTxHash, data, chainKey, relayerApiEndpoint, timeout = DEFAULT_RELAY_TX_TIMEOUT } = params;
4446
+ const { srcTxHash, data, chainKey, relayerApiEndpoint, timeout = DEFAULT_RELAY_TX_TIMEOUT, pollTxHash } = params;
4444
4447
  const intentRelayChainId = getIntentRelayChainId(chainKey).toString();
4445
4448
  const isSplitTxChain = isSolanaChainKeyType(chainKey) || isBitcoinChainKeyType(chainKey);
4446
4449
  invariant(!isSplitTxChain || data !== void 0, "Data is required for Solana and Bitcoin chain keys");
@@ -4459,7 +4462,9 @@ async function relayTxAndWaitPacket(params) {
4459
4462
  if (!submitResult.ok) return submitResult;
4460
4463
  return await waitUntilIntentExecuted({
4461
4464
  intentRelayChainId,
4462
- srcTxHash,
4465
+ // The relay may track the packet under a different id than the submit tx_hash (Bitcoin
4466
+ // on-demand: submit "withdraw", poll the derived `od:<hash>`). Defaults to the submit id.
4467
+ srcTxHash: pollTxHash ?? srcTxHash,
4463
4468
  timeout,
4464
4469
  apiUrl: relayerApiEndpoint
4465
4470
  });
@@ -13298,11 +13303,43 @@ function mergeSodaxConfig(base2, override) {
13298
13303
  return merged;
13299
13304
  }
13300
13305
 
13306
+ // src/shared/logger.ts
13307
+ var consoleLogger = {
13308
+ debug: (message, data) => data ? console.debug(message, data) : console.debug(message),
13309
+ info: (message, data) => data ? console.info(message, data) : console.info(message),
13310
+ warn: (message, data) => data ? console.warn(message, data) : console.warn(message),
13311
+ error: (message, error, data) => {
13312
+ if (error !== void 0 && data !== void 0) console.error(message, error, data);
13313
+ else if (error !== void 0) console.error(message, error);
13314
+ else console.error(message);
13315
+ }
13316
+ };
13317
+ var silentLogger = {
13318
+ debug: () => {
13319
+ },
13320
+ info: () => {
13321
+ },
13322
+ warn: () => {
13323
+ },
13324
+ error: () => {
13325
+ }
13326
+ };
13327
+ function resolveLogger(option) {
13328
+ if (option === void 0 || option === "console") return consoleLogger;
13329
+ if (option === "silent") return silentLogger;
13330
+ return option;
13331
+ }
13332
+
13301
13333
  // src/shared/config/ConfigService.ts
13302
13334
  var ConfigService = class {
13303
13335
  sodax;
13304
13336
  api;
13305
13337
  userConfig;
13338
+ /**
13339
+ * SDK log sink. Resolved once at construction and kept independent of {@link sodax} so that
13340
+ * {@link initialize}'s dynamic-config swap never clobbers it. Read by services via `config.logger`.
13341
+ */
13342
+ logger;
13306
13343
  initialized = false;
13307
13344
  // data structures for quick lookup
13308
13345
  supportedHubAssetsSet;
@@ -13314,10 +13351,11 @@ var ConfigService = class {
13314
13351
  stakedATokenAddressesSet;
13315
13352
  chainToSupportedTokenAddressMap;
13316
13353
  hubAssetToXTokenMap;
13317
- constructor({ api, config, userConfig }) {
13354
+ constructor({ api, config, userConfig, logger }) {
13318
13355
  this.api = api;
13319
13356
  this.sodax = config;
13320
13357
  this.userConfig = userConfig;
13358
+ this.logger = logger ?? resolveLogger(void 0);
13321
13359
  this.loadSodaxConfigDataStructures(config);
13322
13360
  }
13323
13361
  async initialize() {
@@ -13326,7 +13364,7 @@ var ConfigService = class {
13326
13364
  if (!result.ok) return result;
13327
13365
  const response = result.value;
13328
13366
  if (!response.version || response.version < CONFIG_VERSION) {
13329
- console.warn(
13367
+ this.logger.warn(
13330
13368
  `Dynamic config version is less than the current version, resorting to the default one. Current version: ${CONFIG_VERSION}, response version: ${response.version}`
13331
13369
  );
13332
13370
  } else {
@@ -14484,7 +14522,7 @@ var RadfiProvider = class {
14484
14522
  }
14485
14523
  }
14486
14524
  /**
14487
- * Authenticate with Radfi: BIP322-sign a login message, then call the Radfi API.
14525
+ * Authenticate with Bound Exchange: BIP322-sign a login message, then call the Bound Exchange API.
14488
14526
  * Returns accessToken, refreshToken, and tradingAddress.
14489
14527
  */
14490
14528
  async authenticateWithWallet(walletProvider, cachedPublicKey) {
@@ -14500,15 +14538,14 @@ var RadfiProvider = class {
14500
14538
  throw new Error("Failed to retrieve public key from wallet. Please unlock your wallet and try again.");
14501
14539
  }
14502
14540
  const message = `${Date.now()}`;
14503
- const addressType = detectBitcoinAddressType(address);
14504
- const signature = addressType === "P2WPKH" || addressType === "P2TR" ? await walletProvider.signBip322Message(message) : await walletProvider.signEcdsaMessage(message);
14541
+ const signature = usesBip322MessageSigning(detectBitcoinAddressType(address)) ? await walletProvider.signBip322Message(message) : await walletProvider.signEcdsaMessage(message);
14505
14542
  const result = await this.authenticate({ message, signature, address, publicKey });
14506
14543
  this.setRadfiAccessToken(result.accessToken, result.refreshToken);
14507
14544
  return { ...result, publicKey };
14508
14545
  }
14509
14546
  /**
14510
- * Ensure a valid Radfi access token is set on this provider.
14511
- * If a token exists, validates it via the Radfi API.
14547
+ * Ensure a valid Bound Exchange access token is set on this provider.
14548
+ * If a token exists, validates it via the Bound Exchange API.
14512
14549
  * If invalid, tries refreshing with the refresh token first.
14513
14550
  * If refresh also fails, falls back to full re-authentication (BIP322 sign).
14514
14551
  */
@@ -14541,7 +14578,7 @@ var RadfiProvider = class {
14541
14578
  });
14542
14579
  if (!res.ok) {
14543
14580
  const err = await res.json();
14544
- throw new RadfiApiError(res.status, err, "Radfi authentication failed");
14581
+ throw new RadfiApiError(res.status, err, "Bound Exchange authentication failed");
14545
14582
  }
14546
14583
  return res.json().then((r) => ({
14547
14584
  accessToken: r.data?.accessToken ?? "",
@@ -14633,12 +14670,20 @@ var RadfiProvider = class {
14633
14670
  }
14634
14671
  })
14635
14672
  });
14636
- if (!res.ok) {
14637
- const err = await res.json();
14638
- throw new RadfiApiError(res.status, err, "Radfi transaction request failed");
14673
+ const body = await res.json();
14674
+ if (!res.ok || !body?.data) {
14675
+ throw new RadfiApiError(res.status, body, "Bound Exchange transaction request failed");
14639
14676
  }
14640
- return res.json().then((r) => r.data);
14677
+ return body.data;
14641
14678
  }
14679
+ /**
14680
+ * Co-sign and broadcast a `sodax-withdraw` deposit via the Bound Exchange API.
14681
+ *
14682
+ * `relayData` ({ address, payload }) is the same `RelayExtraData` the SDK returns from
14683
+ * `createIntent()` / money-market supply etc. It is optional and non-breaking: when supplied,
14684
+ * the Bound Exchange backend persists it so it can auto-resubmit the intent relay if the relay
14685
+ * gets stuck (otherwise a stuck relay eventually refunds instead of completing the swap).
14686
+ */
14642
14687
  async requestRadfiSignature(params, accessToken) {
14643
14688
  const res = await this.request("/sodax/transaction/sign", {
14644
14689
  method: "POST",
@@ -14652,7 +14697,7 @@ var RadfiProvider = class {
14652
14697
  });
14653
14698
  if (!res.ok) {
14654
14699
  const err = await res.json();
14655
- throw new RadfiApiError(res.status, err, "Radfi signature request failed");
14700
+ throw new RadfiApiError(res.status, err, "Bound Exchange signature request failed");
14656
14701
  }
14657
14702
  return res.json().then((r) => r.data.txId);
14658
14703
  }
@@ -14676,7 +14721,7 @@ var RadfiProvider = class {
14676
14721
  return res.json();
14677
14722
  }
14678
14723
  /**
14679
- * Build a renew-utxo transaction via the Radfi API.
14724
+ * Build a renew-utxo transaction via the Bound Exchange API.
14680
14725
  * Returns a PSBT that needs to be signed by the user.
14681
14726
  */
14682
14727
  async buildRenewUtxoTransaction(params, accessToken) {
@@ -14700,8 +14745,8 @@ var RadfiProvider = class {
14700
14745
  return res.json().then((r) => r.data);
14701
14746
  }
14702
14747
  /**
14703
- * Sign and broadcast a renew-utxo transaction via the Radfi API.
14704
- * The user signs the PSBT first, then Radfi co-signs and broadcasts.
14748
+ * Sign and broadcast a renew-utxo transaction via the Bound Exchange API.
14749
+ * The user signs the PSBT first, then Bound Exchange co-signs and broadcasts.
14705
14750
  */
14706
14751
  async signAndBroadcastRenewUtxo(params, accessToken) {
14707
14752
  const res = await this.request("/transactions/sign", {
@@ -14742,7 +14787,7 @@ var RadfiProvider = class {
14742
14787
  return res.json().then((r) => r.data);
14743
14788
  }
14744
14789
  /**
14745
- * Sign and broadcast a withdraw transaction via Radfi.
14790
+ * Sign and broadcast a withdraw transaction via Bound Exchange.
14746
14791
  */
14747
14792
  async signAndBroadcastWithdraw(params, accessToken) {
14748
14793
  const res = await this.request("/transactions/sign", {
@@ -14796,7 +14841,20 @@ var RadfiProvider = class {
14796
14841
  };
14797
14842
 
14798
14843
  // src/shared/entities/btc/btc-utils.ts
14799
- function estimateBitcoinTxSize(inputCount, outputCount, addressType) {
14844
+ var BITCOIN_FEE_SAFETY_VBYTES = 20;
14845
+ function calcOpReturnOutputVbytes(payloadByteLength) {
14846
+ let scriptSize;
14847
+ if (payloadByteLength <= 75) {
14848
+ scriptSize = 3 + payloadByteLength;
14849
+ } else if (payloadByteLength <= 255) {
14850
+ scriptSize = 4 + payloadByteLength;
14851
+ } else {
14852
+ scriptSize = 5 + payloadByteLength;
14853
+ }
14854
+ const scriptLenVarint = scriptSize <= 252 ? 1 : 3;
14855
+ return 8 + scriptLenVarint + scriptSize;
14856
+ }
14857
+ function estimateBitcoinTxSize(inputCount, outputCount, addressType, opReturnOutputVbytes = 44) {
14800
14858
  let inputWeight;
14801
14859
  switch (addressType) {
14802
14860
  case "P2PKH":
@@ -14812,7 +14870,9 @@ function estimateBitcoinTxSize(inputCount, outputCount, addressType) {
14812
14870
  inputWeight = 68;
14813
14871
  break;
14814
14872
  }
14815
- return Math.ceil(10.5 + 44 + inputCount * inputWeight + outputCount * 31);
14873
+ return Math.ceil(
14874
+ 10.5 + opReturnOutputVbytes + BITCOIN_FEE_SAFETY_VBYTES + inputCount * inputWeight + outputCount * 31
14875
+ );
14816
14876
  }
14817
14877
  function encodeBtcPayloadToBytes(payload) {
14818
14878
  return JSON.stringify({
@@ -14829,6 +14889,10 @@ function normalizePsbtToBase64(signedPsbt) {
14829
14889
  const isHex = /^[0-9a-fA-F]+$/.test(signedPsbt);
14830
14890
  return isHex ? Buffer.from(signedPsbt, "hex").toString("base64") : signedPsbt;
14831
14891
  }
14892
+ function normalizeSignatureToBase64(signature) {
14893
+ const isHex = /^[0-9a-fA-F]+$/.test(signature);
14894
+ return isHex ? Buffer.from(signature, "hex").toString("base64") : signature;
14895
+ }
14832
14896
 
14833
14897
  // src/shared/services/spoke/BitcoinSpokeService.ts
14834
14898
  bitcoinjsLib.initEccLib(ecc__namespace);
@@ -14937,7 +15001,7 @@ var BitcoinSpokeService = class {
14937
15001
  return BigInt(totalBalance);
14938
15002
  }
14939
15003
  /**
14940
- * Fund the Radfi trading wallet by sending BTC from the user's personal wallet
15004
+ * Fund the Bound Exchange trading wallet by sending BTC from the user's personal wallet
14941
15005
  *
14942
15006
  * @param {bigint} amount - Amount in satoshis to send
14943
15007
  * @param {BitcoinSpokeProvider} spokeProvider - The Bitcoin spoke provider (must have signing capability)
@@ -14961,10 +15025,28 @@ var BitcoinSpokeService = class {
14961
15025
  async sendMessage(params) {
14962
15026
  return await this.encodeWithdrawalData(params);
14963
15027
  }
15028
+ /**
15029
+ * Build the relay submit/poll identity for an on-demand action (borrow/withdraw).
15030
+ *
15031
+ * Bitcoin borrow/withdraw are on-demand: there is no broadcast transaction — the spoke result is
15032
+ * the signed payload JSON produced by {@link encodeWithdrawalData}/{@link sendMessage}. The relay
15033
+ * accepts the submit under the literal `withdraw` tx_hash with the signed payload (as a JSON object)
15034
+ * in `data`, then tracks the resulting packet under a derived id: `od:` + keccak256 of the ASCII
15035
+ * `payload_hex` string (hash the hex characters, not the decoded bytes). Polling must use that
15036
+ * derived id (`pollTxHash`), not `withdraw`.
15037
+ *
15038
+ * @param tx - The JSON-stringified signed payload returned by `sendMessage` / `encodeWithdrawalData`.
15039
+ */
15040
+ getOnDemandRelayIdentity(tx) {
15041
+ const data = JSON.parse(tx);
15042
+ const payloadHex = data.payload_hex.startsWith("0x") ? data.payload_hex.slice(2) : data.payload_hex;
15043
+ const pollTxHash = `od:${viem.keccak256(viem.stringToBytes(payloadHex)).slice(2)}`;
15044
+ return { srcTxHash: "withdraw", data, pollTxHash };
15045
+ }
14964
15046
  /**
14965
15047
  * Build a priority Bitcoin transaction with proper fee calculation
14966
15048
  */
14967
- async buildBitcoinTransaction(utxos, outputs, changeAddress, chainId, walletProvider, feeRate) {
15049
+ async buildBitcoinTransaction(utxos, outputs, changeAddress, chainId, walletProvider, feeRate, opReturnOutputVbytes) {
14968
15050
  const psbt = new bitcoinjsLib.Psbt({ network: this.getBtcNetwork(chainId) });
14969
15051
  const effectiveFeeRate = feeRate ?? await this.getFeeRateEstimate();
14970
15052
  const walletAddress = await walletProvider.getWalletAddress();
@@ -15027,7 +15109,7 @@ var BitcoinSpokeService = class {
15027
15109
  });
15028
15110
  }
15029
15111
  inputSum += utxo.value;
15030
- const estimatedSize = estimateBitcoinTxSize(psbt.inputCount, outputs.length, addressType);
15112
+ const estimatedSize = estimateBitcoinTxSize(psbt.inputCount, outputs.length, addressType, opReturnOutputVbytes);
15031
15113
  const estimatedFee = Math.ceil(effectiveFeeRate * estimatedSize);
15032
15114
  if (inputSum >= outputSum + estimatedFee + DUST_THRESHOLD) {
15033
15115
  break;
@@ -15039,8 +15121,13 @@ var BitcoinSpokeService = class {
15039
15121
  value: output.value
15040
15122
  });
15041
15123
  }
15042
- const sizeWithChange = estimateBitcoinTxSize(psbt.inputCount, outputs.length + 1, addressType);
15043
- const sizeWithoutChange = estimateBitcoinTxSize(psbt.inputCount, outputs.length, addressType);
15124
+ const sizeWithChange = estimateBitcoinTxSize(
15125
+ psbt.inputCount,
15126
+ outputs.length + 1,
15127
+ addressType,
15128
+ opReturnOutputVbytes
15129
+ );
15130
+ const sizeWithoutChange = estimateBitcoinTxSize(psbt.inputCount, outputs.length, addressType, opReturnOutputVbytes);
15044
15131
  const feeWithChange = Math.ceil(effectiveFeeRate * sizeWithChange);
15045
15132
  const feeWithoutChange = Math.ceil(effectiveFeeRate * sizeWithoutChange);
15046
15133
  let change = inputSum - outputSum - feeWithChange;
@@ -15109,7 +15196,12 @@ var BitcoinSpokeService = class {
15109
15196
  return await this.radfi.requestRadfiSignature(
15110
15197
  {
15111
15198
  userAddress: from,
15112
- signedBase64Tx
15199
+ signedBase64Tx,
15200
+ // Forward the relay identity ({ hub wallet address, full payload }) so the Bound Exchange
15201
+ // backend can auto-resubmit the intent relay if it gets stuck. `to` is the hub wallet
15202
+ // (relayData.address) and `data` is the full payload (relayData.payload) — the same pair
15203
+ // feature services return as `relayData` from createIntent()/supply().
15204
+ relayData: { address: params.to, payload: data }
15113
15205
  },
15114
15206
  accessToken
15115
15207
  );
@@ -15119,7 +15211,11 @@ var BitcoinSpokeService = class {
15119
15211
  "Raw mode is not supported for normal Bitcoin deposits. Use TRADING wallet mode for raw transactions."
15120
15212
  );
15121
15213
  }
15122
- const utxos = await this.fetchUTXOs(from);
15214
+ const [allUtxos, mempoolSpent] = await Promise.all([
15215
+ this.fetchUTXOs(from),
15216
+ this.fetchMempoolSpentOutpoints(from)
15217
+ ]);
15218
+ const utxos = allUtxos.filter((u) => !mempoolSpent.has(`${u.txid}:${u.vout}`));
15123
15219
  if (!utxos?.length) {
15124
15220
  throw new Error("No UTXOs available for deposit");
15125
15221
  }
@@ -15134,7 +15230,7 @@ var BitcoinSpokeService = class {
15134
15230
  );
15135
15231
  return await this.signAndBroadcastTransaction(depositPsbt, params.walletProvider);
15136
15232
  } catch (error) {
15137
- console.error("Error during deposit:", error);
15233
+ this.config.logger.error("Error during deposit", error);
15138
15234
  throw error;
15139
15235
  }
15140
15236
  }
@@ -15142,22 +15238,37 @@ var BitcoinSpokeService = class {
15142
15238
  * Build deposit PSBT with embedded cross-chain data
15143
15239
  */
15144
15240
  async buildDepositPsbt(walletAddress, walletProvider, srcChainKey, token, amount, data, utxos) {
15145
- const assetManagerAddress = this.config.getChainConfig(srcChainKey).addresses.assetManager;
15146
- if (token.toLocaleLowerCase() === "btc") {
15241
+ const chainConfig = this.config.getChainConfig(srcChainKey);
15242
+ const assetManagerAddress = chainConfig.addresses.assetManager;
15243
+ const normalizedToken = token.toLocaleLowerCase();
15244
+ const nativeBtcTokens = new Set(
15245
+ ["btc", chainConfig.nativeToken, chainConfig.supportedTokens.BTC?.address].filter((value) => !!value).map((value) => value.toLocaleLowerCase())
15246
+ );
15247
+ const isNativeBtc = nativeBtcTokens.has(normalizedToken);
15248
+ if (isNativeBtc) {
15249
+ const OP_RETURN = bitcoinjsLib.opcodes.OP_RETURN;
15250
+ const OP_12 = bitcoinjsLib.opcodes.OP_12;
15251
+ if (OP_RETURN === void 0 || OP_12 === void 0) {
15252
+ throw new Error("bitcoinjs-lib opcodes OP_RETURN or OP_12 are undefined");
15253
+ }
15254
+ const OP_RADFI_SODAX_DATA = 49;
15255
+ const payload = Buffer.concat([Buffer.from([OP_RADFI_SODAX_DATA]), Buffer.from(data.slice(2), "hex")]);
15256
+ const opReturnOutputVbytes = calcOpReturnOutputVbytes(payload.length);
15147
15257
  const outputs = [
15148
15258
  {
15149
15259
  address: assetManagerAddress,
15150
15260
  value: Number(amount)
15151
15261
  }
15152
15262
  ];
15153
- const psbt = await this.buildBitcoinTransaction(utxos, outputs, walletAddress, srcChainKey, walletProvider);
15154
- const OP_RADFI_SODAX_DATA = 49;
15155
- const payload = Buffer.concat([Buffer.from([OP_RADFI_SODAX_DATA]), Buffer.from(data.slice(2), "hex")]);
15156
- const OP_RETURN = bitcoinjsLib.opcodes.OP_RETURN;
15157
- const OP_12 = bitcoinjsLib.opcodes.OP_12;
15158
- if (OP_RETURN === void 0 || OP_12 === void 0) {
15159
- throw new Error("bitcoinjs-lib opcodes OP_RETURN or OP_12 are undefined");
15160
- }
15263
+ const psbt = await this.buildBitcoinTransaction(
15264
+ utxos,
15265
+ outputs,
15266
+ walletAddress,
15267
+ srcChainKey,
15268
+ walletProvider,
15269
+ void 0,
15270
+ opReturnOutputVbytes
15271
+ );
15161
15272
  const compiledScript = bitcoinjsLib.script.compile([OP_RETURN, OP_12, payload]);
15162
15273
  psbt.addOutput({
15163
15274
  script: compiledScript,
@@ -15177,6 +15288,27 @@ var BitcoinSpokeService = class {
15177
15288
  }
15178
15289
  return await response.json();
15179
15290
  }
15291
+ /**
15292
+ * Returns the set of "txid:vout" outpoints currently being spent by
15293
+ * unconfirmed transactions in the mempool for the given address.
15294
+ * Used to prevent double-spend when building a new PSBT.
15295
+ */
15296
+ async fetchMempoolSpentOutpoints(address) {
15297
+ try {
15298
+ const response = await fetch(`${this.rpcUrl}/address/${address}/txs/mempool`);
15299
+ if (!response.ok) return /* @__PURE__ */ new Set();
15300
+ const mempoolTxs = await response.json();
15301
+ const spent = /* @__PURE__ */ new Set();
15302
+ for (const tx of mempoolTxs) {
15303
+ for (const input of tx.vin) {
15304
+ spent.add(`${input.txid}:${input.vout}`);
15305
+ }
15306
+ }
15307
+ return spent;
15308
+ } catch {
15309
+ return /* @__PURE__ */ new Set();
15310
+ }
15311
+ }
15180
15312
  /**
15181
15313
  * Fetch raw transaction hex
15182
15314
  */
@@ -15192,7 +15324,7 @@ var BitcoinSpokeService = class {
15192
15324
  let srcAddress = from;
15193
15325
  const addressType = detectBitcoinAddressType(from);
15194
15326
  if (walletMode === "TRADING") {
15195
- srcAddress = await this.radfi.getTradingWallet(srcAddress).then((res) => res.tradingAddress).catch(() => srcAddress);
15327
+ srcAddress = (await this.radfi.getTradingWallet(srcAddress)).tradingAddress;
15196
15328
  }
15197
15329
  const payload = {
15198
15330
  src_address: srcAddress,
@@ -15211,8 +15343,12 @@ var BitcoinSpokeService = class {
15211
15343
  if (params.raw === true) {
15212
15344
  return JSON.stringify(onDemandWithdraw);
15213
15345
  }
15214
- const signature = await params.walletProvider.signEcdsaMessage(orderedPayload);
15215
- onDemandWithdraw.signature = signature;
15346
+ if (!params.walletProvider.getPublicKey) {
15347
+ throw new Error("Wallet provider does not support getPublicKey");
15348
+ }
15349
+ const rawSignature = usesBip322MessageSigning(addressType) ? await params.walletProvider.signBip322Message(orderedPayload) : await params.walletProvider.signEcdsaMessage(orderedPayload);
15350
+ onDemandWithdraw.signature = normalizeSignatureToBase64(rawSignature);
15351
+ onDemandWithdraw.public_key = await params.walletProvider.getPublicKey();
15216
15352
  return JSON.stringify(onDemandWithdraw);
15217
15353
  }
15218
15354
  /**
@@ -15220,8 +15356,14 @@ var BitcoinSpokeService = class {
15220
15356
  */
15221
15357
  async signAndBroadcastTransaction(psbt, walletProvider) {
15222
15358
  const psbtBase64 = typeof psbt === "string" ? psbt : psbt.toBase64();
15223
- const signedPsbtHex = await walletProvider.signTransaction(psbtBase64);
15224
- const txHash = await this.broadcastTransaction(signedPsbtHex);
15359
+ const signedRaw = await walletProvider.signTransaction(psbtBase64, false);
15360
+ const signedPsbt = bitcoinjsLib.Psbt.fromBase64(normalizePsbtToBase64(signedRaw));
15361
+ try {
15362
+ signedPsbt.finalizeAllInputs();
15363
+ } catch {
15364
+ }
15365
+ const txHex = signedPsbt.extractTransaction().toHex();
15366
+ const txHash = await this.broadcastTransaction(txHex);
15225
15367
  return txHash;
15226
15368
  }
15227
15369
  /**
@@ -17460,7 +17602,7 @@ function base64Decode(str) {
17460
17602
  }
17461
17603
  var encoder = new TextEncoder();
17462
17604
  var decoder = new TextDecoder();
17463
- function stringToBytes(str) {
17605
+ function stringToBytes2(str) {
17464
17606
  return encoder.encode(str);
17465
17607
  }
17466
17608
  function bytesToString(bytes) {
@@ -20857,7 +20999,7 @@ var JsonRpcProvider = class {
20857
20999
  };
20858
21000
  }
20859
21001
  async viewContractState({ contractId, prefix: prefix2, blockQuery = { finality: DEFAULT_FINALITY } }) {
20860
- const prefixBase64 = base64Encode(stringToBytes(prefix2 || ""));
21002
+ const prefixBase64 = base64Encode(stringToBytes2(prefix2 || ""));
20861
21003
  let reference;
20862
21004
  if ("blockId" in blockQuery) {
20863
21005
  reference = { block_id: blockQuery.blockId };
@@ -20885,7 +21027,7 @@ var JsonRpcProvider = class {
20885
21027
  }
20886
21028
  }
20887
21029
  async callFunctionRaw({ contractId, method, args, blockQuery = { finality: DEFAULT_FINALITY } }) {
20888
- const argsBytes = args instanceof Uint8Array ? args : stringToBytes(JSON.stringify(args));
21030
+ const argsBytes = args instanceof Uint8Array ? args : stringToBytes2(JSON.stringify(args));
20889
21031
  const argsBase64 = base64Encode(argsBytes);
20890
21032
  if ("blockId" in blockQuery) {
20891
21033
  return this.query({
@@ -21151,6 +21293,7 @@ var JsonRpcProvider = class {
21151
21293
 
21152
21294
  // src/shared/services/spoke/NearSpokeService.ts
21153
21295
  var NEAR_DEFAULT_GAS = BigInt("300000000000000");
21296
+ var NEAR_STORAGE_DEPOSIT = BigInt("1250000000000000000000");
21154
21297
  var NearSpokeService = class {
21155
21298
  config;
21156
21299
  rpcProvider;
@@ -21200,7 +21343,7 @@ var NearSpokeService = class {
21200
21343
  token: Array.from(Buffer.from(fillData.token, "utf-8"))
21201
21344
  };
21202
21345
  }
21203
- async fillIntent(fromInfo, fillData, deposit = BigInt("0"), gas = BigInt("300000000000000")) {
21346
+ async fillIntent(fromInfo, fillData, deposit = BigInt("1"), gas = BigInt("300000000000000")) {
21204
21347
  const intentFiller = this.config.getChainConfig(fromInfo.srcChainKey).addresses.intentFiller;
21205
21348
  if (isNativeToken(fromInfo.srcChainKey, fillData.token)) {
21206
21349
  deposit = BigInt(fillData.amount);
@@ -21274,7 +21417,8 @@ var NearSpokeService = class {
21274
21417
  data: inputParams.data
21275
21418
  })
21276
21419
  },
21277
- deposit: BigInt("0"),
21420
+ // NEP-141 requires exactly 1 yoctoNEAR attached to ft_transfer_call.
21421
+ deposit: BigInt("1"),
21278
21422
  gas: NEAR_DEFAULT_GAS
21279
21423
  }
21280
21424
  };
@@ -21305,6 +21449,52 @@ var NearSpokeService = class {
21305
21449
  }
21306
21450
  return BigInt(bal);
21307
21451
  }
21452
+ /**
21453
+ * Whether `accountId` is storage-registered on a NEP-141 `token` contract. NEP-141 requires an
21454
+ * account to pay a one-time storage bond before it can receive (hold a balance of) the token, so
21455
+ * this gates any leg that delivers a token to a user on NEAR (swap output on NEAR, bridge into
21456
+ * NEAR, money-market borrow/withdraw to NEAR).
21457
+ *
21458
+ * Native NEAR is not a NEP-141 token and has no storage registration, so this returns `true` for
21459
+ * the native token. The view goes through {@link queryContract}, i.e. the configurable RPC from
21460
+ * chain config — a custom `rpcUrl` passed to the SDK is honoured.
21461
+ */
21462
+ async isStorageRegistered(token, accountId) {
21463
+ if (isNativeToken(ChainKeys.NEAR_MAINNET, token)) {
21464
+ return true;
21465
+ }
21466
+ const balance = await this.queryContract(token, "storage_balance_of", { account_id: accountId });
21467
+ return balance != null;
21468
+ }
21469
+ /**
21470
+ * Build (and, unless `raw`, submit) a NEP-141 `storage_deposit` registration for `accountId` on
21471
+ * the `token` contract. Call this when {@link isStorageRegistered} is `false` before a token is
21472
+ * delivered to the account on NEAR.
21473
+ *
21474
+ * Native NEAR has no storage registration — passing the native token throws.
21475
+ *
21476
+ * @param params.deposit Storage bond to attach; defaults to {@link NEAR_STORAGE_DEPOSIT}
21477
+ * (0.00125 NEAR). Override per token if its `storage_balance_bounds.min` differs.
21478
+ */
21479
+ async registerStorage(params) {
21480
+ if (isNativeToken(ChainKeys.NEAR_MAINNET, params.token)) {
21481
+ throw new Error("[NearSpokeService.registerStorage] Native NEAR has no NEP-141 storage registration.");
21482
+ }
21483
+ const tx = {
21484
+ signerId: params.accountId,
21485
+ params: {
21486
+ contractId: params.token,
21487
+ method: "storage_deposit",
21488
+ args: { account_id: params.accountId, registration_only: true },
21489
+ deposit: params.deposit ?? NEAR_STORAGE_DEPOSIT,
21490
+ gas: NEAR_DEFAULT_GAS
21491
+ }
21492
+ };
21493
+ if (params.raw === true) {
21494
+ return tx;
21495
+ }
21496
+ return params.walletProvider.signAndSubmitTxn(tx);
21497
+ }
21308
21498
  /**
21309
21499
  * Sends a message to the hub chain.
21310
21500
  * @param {SendMessageParams} params - Includes dstChainKey, the chain key of the hub chain.
@@ -22512,13 +22702,13 @@ var StellarSpokeService = class {
22512
22702
  );
22513
22703
  return `${hash}`;
22514
22704
  } catch (error) {
22515
- console.error("Error during sendMessage:", error);
22705
+ this.config.logger.error("Error during sendMessage", error);
22516
22706
  throw error;
22517
22707
  }
22518
22708
  }
22519
22709
  handleSendTransactionError(response) {
22520
22710
  if (response.status === "ERROR") {
22521
- console.error(JSON.stringify(response, null, 2));
22711
+ this.config.logger.error(JSON.stringify(response, null, 2));
22522
22712
  throw new Error(JSON.stringify(response, null, 2));
22523
22713
  }
22524
22714
  return response;
@@ -22629,7 +22819,7 @@ var StellarSpokeService = class {
22629
22819
  );
22630
22820
  return `${hash}`;
22631
22821
  } catch (error) {
22632
- console.error("Error during deposit:", error);
22822
+ this.config.logger.error("Error during deposit", error);
22633
22823
  throw error;
22634
22824
  }
22635
22825
  }
@@ -22657,7 +22847,7 @@ var StellarSpokeService = class {
22657
22847
  (balance2) => "limit" in balance2 && "balance" in balance2 && "asset_code" in balance2 && trustlineConfig.assetCode.toLowerCase() === balance2.asset_code?.toLowerCase() && "asset_issuer" in balance2 && trustlineConfig.assetIssuer.toLowerCase() === balance2.asset_issuer?.toLowerCase()
22658
22848
  );
22659
22849
  if (!tokenBalance) {
22660
- console.error(`No token balances found for token: ${token}`);
22850
+ this.config.logger.error(`No token balances found for token: ${token}`);
22661
22851
  return false;
22662
22852
  }
22663
22853
  const limit = parseToStroops(tokenBalance.limit);
@@ -22706,7 +22896,7 @@ var StellarSpokeService = class {
22706
22896
  const hash = await this.signAndSendTransaction(walletProvider, transaction);
22707
22897
  return `${hash}`;
22708
22898
  } catch (error) {
22709
- console.error("Error during requestTrustline:", error);
22899
+ this.config.logger.error("Error during requestTrustline", error);
22710
22900
  throw error;
22711
22901
  }
22712
22902
  }
@@ -23565,7 +23755,7 @@ var SpokeService = class _SpokeService {
23565
23755
  }
23566
23756
  default: {
23567
23757
  const exhaustiveCheck = chainType;
23568
- console.log(exhaustiveCheck);
23758
+ this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
23569
23759
  throw new Error(`[getSpokeService] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`);
23570
23760
  }
23571
23761
  }
@@ -23738,7 +23928,7 @@ var SpokeService = class _SpokeService {
23738
23928
  }
23739
23929
  default: {
23740
23930
  const exhaustiveCheck = chainType;
23741
- console.log(exhaustiveCheck);
23931
+ this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
23742
23932
  return {
23743
23933
  ok: false,
23744
23934
  error: new Error(`[estimateGas] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`)
@@ -23845,17 +24035,17 @@ var SpokeService = class _SpokeService {
23845
24035
  args: [params.srcChainId, params.srcAddress, params.payload]
23846
24036
  })
23847
24037
  });
23848
- console.warn("simulateRecvMessage did not revert as expected", { result });
24038
+ this.config.logger.warn("simulateRecvMessage did not revert as expected", { result });
23849
24039
  return {
23850
24040
  ok: false,
23851
24041
  error: new Error('Function should have reverted with "Simulation completed"')
23852
24042
  };
23853
24043
  } catch (error) {
23854
24044
  if (error instanceof Error && error.message?.includes("Simulation completed")) {
23855
- console.warn("simulateRecvMessage completed successfully with expected revert");
24045
+ this.config.logger.warn("simulateRecvMessage completed successfully with expected revert");
23856
24046
  return { ok: true, value: true };
23857
24047
  }
23858
- console.error("simulateRecvMessage failed with unexpected error:", error);
24048
+ this.config.logger.error("simulateRecvMessage failed with unexpected error", error);
23859
24049
  return { ok: false, error };
23860
24050
  }
23861
24051
  }
@@ -23940,7 +24130,7 @@ var SpokeService = class _SpokeService {
23940
24130
  }
23941
24131
  default: {
23942
24132
  const exhaustiveCheck = chainType;
23943
- console.log(exhaustiveCheck);
24133
+ this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
23944
24134
  return {
23945
24135
  ok: false,
23946
24136
  error: new Error(`[deposit] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`)
@@ -24017,7 +24207,7 @@ var SpokeService = class _SpokeService {
24017
24207
  }
24018
24208
  default: {
24019
24209
  const exhaustiveCheck = chainType;
24020
- console.log(exhaustiveCheck);
24210
+ this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
24021
24211
  return {
24022
24212
  ok: false,
24023
24213
  error: new Error(`[getDeposit] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`)
@@ -24126,7 +24316,7 @@ var SpokeService = class _SpokeService {
24126
24316
  }
24127
24317
  default: {
24128
24318
  const exhaustiveCheck = chainType;
24129
- console.log(exhaustiveCheck);
24319
+ this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
24130
24320
  return {
24131
24321
  ok: false,
24132
24322
  error: new Error(`[sendMessage] Invalid chain type. Valid chain types: ${ChainTypeArr.join(", ")}`)
@@ -24205,10 +24395,10 @@ var SpokeService = class _SpokeService {
24205
24395
  if (isSolanaChainKeyType(chainKey)) {
24206
24396
  const result = await this.solana.waitForTransactionReceipt({ txHash, chainKey });
24207
24397
  if (!result.ok || result.value.status !== "success") {
24208
- console.warn(
24398
+ this.config.logger.warn(
24209
24399
  `Solana verifyTxHash failed: ${!result.ok ? result.error : "error" in result.value ? result.value.error : "unknown"}`
24210
24400
  );
24211
- console.warn("Returning true to assume transaction exists on chain in future ");
24401
+ this.config.logger.warn("Returning true to assume transaction exists on chain in future ");
24212
24402
  return { ok: true, value: true };
24213
24403
  }
24214
24404
  return { ok: true, value: true };
@@ -24295,7 +24485,7 @@ var SpokeService = class _SpokeService {
24295
24485
  }
24296
24486
  default: {
24297
24487
  const exhaustiveCheck = chainType;
24298
- console.log(exhaustiveCheck);
24488
+ this.config.logger.debug("Unhandled exhaustive case", { value: exhaustiveCheck });
24299
24489
  return { ok: false, error: new Error(`waitForTransactionReceipt not supported for ${params.chainKey}`) };
24300
24490
  }
24301
24491
  }
@@ -25346,6 +25536,7 @@ var EvmHubProvider = class {
25346
25536
  };
25347
25537
 
25348
25538
  // src/swap/SolverApiService.ts
25539
+ var bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
25349
25540
  var SolverApiService = class {
25350
25541
  constructor() {
25351
25542
  }
@@ -25414,13 +25605,16 @@ var SolverApiService = class {
25414
25605
  }
25415
25606
  };
25416
25607
  } catch (e) {
25417
- console.error(`[SolverApiService.getQuote] failed. Details: ${JSON.stringify(e)}`);
25608
+ configService.logger.error(
25609
+ "[SolverApiService.getQuote] failed",
25610
+ e instanceof Error ? e : new Error(JSON.stringify(e, bigintReplacer))
25611
+ );
25418
25612
  return {
25419
25613
  ok: false,
25420
25614
  error: {
25421
25615
  detail: {
25422
25616
  code: exports.SolverIntentErrorCode.UNKNOWN,
25423
- message: e ? JSON.stringify(e) : "Unknown error"
25617
+ message: e ? JSON.stringify(e, bigintReplacer) : "Unknown error"
25424
25618
  }
25425
25619
  }
25426
25620
  };
@@ -25437,7 +25631,7 @@ var SolverApiService = class {
25437
25631
  * @param config - Solver endpoint configuration.
25438
25632
  * @returns A `Result` containing `{ answer: 'OK', intent_hash: Hex }` on success.
25439
25633
  */
25440
- static async postExecution(request, config) {
25634
+ static async postExecution(request, config, logger = silentLogger) {
25441
25635
  try {
25442
25636
  const response = await retry(
25443
25637
  () => fetch(`${config.solverApiEndpoint}/execute`, {
@@ -25459,13 +25653,16 @@ var SolverApiService = class {
25459
25653
  value: await response.json()
25460
25654
  };
25461
25655
  } catch (e) {
25462
- console.error(`[SolverApiService.postExecution] failed. Details: ${JSON.stringify(e)}`);
25656
+ logger.error(
25657
+ "[SolverApiService.postExecution] failed",
25658
+ e instanceof Error ? e : new Error(JSON.stringify(e, bigintReplacer))
25659
+ );
25463
25660
  return {
25464
25661
  ok: false,
25465
25662
  error: {
25466
25663
  detail: {
25467
25664
  code: exports.SolverIntentErrorCode.UNKNOWN,
25468
- message: e ? JSON.stringify(e) : "Unknown error"
25665
+ message: e ? JSON.stringify(e, bigintReplacer) : "Unknown error"
25469
25666
  }
25470
25667
  }
25471
25668
  };
@@ -25480,7 +25677,7 @@ var SolverApiService = class {
25480
25677
  * `fill_tx_hash` is set only when `status === SolverIntentStatusCode.SOLVED (3)`.
25481
25678
  * @throws Invariant error if `intent_tx_hash` is empty (thrown before the async request).
25482
25679
  */
25483
- static async getStatus(request, config) {
25680
+ static async getStatus(request, config, logger = silentLogger) {
25484
25681
  invariant(request.intent_tx_hash.length > 0, "Empty intent_tx_hash");
25485
25682
  try {
25486
25683
  const response = await fetch(`${config.solverApiEndpoint}/status`, {
@@ -25501,13 +25698,16 @@ var SolverApiService = class {
25501
25698
  value: await response.json()
25502
25699
  };
25503
25700
  } catch (e) {
25504
- console.error(`[SolverApiService.getStatus] failed. Details: ${JSON.stringify(e)}`);
25701
+ logger.error(
25702
+ "[SolverApiService.getStatus] failed",
25703
+ e instanceof Error ? e : new Error(JSON.stringify(e, bigintReplacer))
25704
+ );
25505
25705
  return {
25506
25706
  ok: false,
25507
25707
  error: {
25508
25708
  detail: {
25509
25709
  code: exports.SolverIntentErrorCode.UNKNOWN,
25510
- message: e ? JSON.stringify(e) : "Unknown error"
25710
+ message: e ? JSON.stringify(e, bigintReplacer) : "Unknown error"
25511
25711
  }
25512
25712
  }
25513
25713
  };
@@ -25627,7 +25827,7 @@ var SwapService = class {
25627
25827
  * `fill_tx_hash` is populated only when `status === SolverIntentStatusCode.SOLVED (3)`.
25628
25828
  */
25629
25829
  async getStatus(request) {
25630
- return SolverApiService.getStatus(request, this.solver);
25830
+ return SolverApiService.getStatus(request, this.solver, this.config.logger);
25631
25831
  }
25632
25832
  /**
25633
25833
  * Notifies the solver API that an intent has been registered on the hub chain, triggering
@@ -25650,7 +25850,7 @@ var SwapService = class {
25650
25850
  */
25651
25851
  async postExecution(request) {
25652
25852
  try {
25653
- const result = await SolverApiService.postExecution(request, this.solver);
25853
+ const result = await SolverApiService.postExecution(request, this.solver, this.config.logger);
25654
25854
  if (result.ok) return result;
25655
25855
  const detail = result.error?.detail ?? {
25656
25856
  code: -999,
@@ -25923,7 +26123,7 @@ var SwapService = class {
25923
26123
  * - `raw: false` — broadcasts the transaction; `walletProvider` is required and must match `K`.
25924
26124
  *
25925
26125
  * Validates tokens and chain keys against the active `ConfigService` before constructing the
25926
- * intent. Bitcoin source chains require an additional RadFi access token step.
26126
+ * intent. Bitcoin source chains require an additional Bound Exchange access token step.
25927
26127
  *
25928
26128
  * @param _params - Intent parameters, source chain key, wallet provider (when `raw: false`),
25929
26129
  * and optional `skipSimulation` flag.
@@ -28404,11 +28604,13 @@ var MigrationService = class {
28404
28604
 
28405
28605
  // src/backendApi/BackendApiService.ts
28406
28606
  var BackendApiService = class {
28407
- constructor(config) {
28607
+ constructor(config, logger = consoleLogger) {
28408
28608
  this.config = config;
28409
28609
  this.headers = { ...config.headers };
28610
+ this.logger = logger;
28410
28611
  }
28411
28612
  headers;
28613
+ logger;
28412
28614
  /**
28413
28615
  * Execute a single HTTP request and return the parsed JSON body.
28414
28616
  *
@@ -28447,10 +28649,10 @@ var BackendApiService = class {
28447
28649
  if (error.name === "AbortError") {
28448
28650
  throw new Error("REQUEST_TIMEOUT", { cause: new Error(`Request timeout after ${timeout}ms`) });
28449
28651
  }
28450
- console.error("[BackendApiService] Request error:", error.message);
28652
+ this.logger.error("[BackendApiService] Request error", error);
28451
28653
  throw error;
28452
28654
  }
28453
- console.error("[BackendApiService] Unknown error:", error);
28655
+ this.logger.error("[BackendApiService] Unknown error", error);
28454
28656
  throw new Error("UNKNOWN_REQUEST_ERROR", { cause: error });
28455
28657
  }
28456
28658
  }
@@ -29120,7 +29322,7 @@ var BridgeService = class {
29120
29322
  * transaction simulation or batching). When `raw` is `false`, signs and submits the deposit
29121
29323
  * transaction via the provided wallet provider.
29122
29324
  *
29123
- * Bitcoin is only supported with `raw: false` because it requires the RadFi trading wallet
29325
+ * Bitcoin is only supported with `raw: false` because it requires the Bound Exchange trading wallet
29124
29326
  * derivation flow.
29125
29327
  *
29126
29328
  * @param _params - Bridge parameters including source/destination chain keys, token addresses,
@@ -29153,9 +29355,12 @@ var BridgeService = class {
29153
29355
  { ...baseCtx, field: "walletProvider" }
29154
29356
  );
29155
29357
  walletAddress = await this.spoke.bitcoin.getEffectiveWalletAddress(personalAddress);
29156
- await this.spoke.bitcoin.radfi.ensureRadfiAccessToken(_params.walletProvider);
29358
+ if (this.spoke.bitcoin.walletMode === "TRADING") {
29359
+ await this.spoke.bitcoin.radfi.ensureRadfiAccessToken(_params.walletProvider);
29360
+ }
29157
29361
  }
29158
- const hubWallet = await this.hubProvider.getUserHubWalletAddress(params.srcAddress, params.srcChainKey);
29362
+ const hubWallet = await this.hubProvider.getUserHubWalletAddress(walletAddress, params.srcChainKey);
29363
+ const effectiveSkipSimulation = skipSimulation || isBitcoinChainKeyType(params.srcChainKey) && this.spoke.bitcoin.walletMode === "USER";
29159
29364
  const data = this.buildBridgeData(params, srcToken, dstToken, this.config.bridge.partnerFee);
29160
29365
  const coreParams = {
29161
29366
  srcChainKey: params.srcChainKey,
@@ -29164,7 +29369,7 @@ var BridgeService = class {
29164
29369
  token: params.srcToken,
29165
29370
  amount: params.amount,
29166
29371
  data,
29167
- skipSimulation
29372
+ skipSimulation: effectiveSkipSimulation
29168
29373
  };
29169
29374
  const txResult = await this.spoke.deposit(
29170
29375
  _params.raw ? {
@@ -29177,7 +29382,7 @@ var BridgeService = class {
29177
29382
  }
29178
29383
  );
29179
29384
  if (!txResult.ok) {
29180
- console.error(txResult.error);
29385
+ this.config.logger.error("createBridgeIntent failed", txResult.error);
29181
29386
  if (isBridgeCreateIntentError(txResult.error)) return { ok: false, error: txResult.error };
29182
29387
  return {
29183
29388
  ok: false,
@@ -29192,7 +29397,7 @@ var BridgeService = class {
29192
29397
  }
29193
29398
  };
29194
29399
  } catch (error) {
29195
- console.error(error);
29400
+ this.config.logger.error("createBridgeIntent failed", error);
29196
29401
  if (isBridgeCreateIntentError(error)) return { ok: false, error };
29197
29402
  return {
29198
29403
  ok: false,
@@ -29362,7 +29567,7 @@ var BridgeService = class {
29362
29567
  value: availableDepositNormalised.isLessThan(assetManagerBalanceNormalised) ? { amount: availableDeposit, decimals: fromTokenInfo.decimals, type: "DEPOSIT_LIMIT" } : { amount: assetManagerBalance, decimals: toTokenInfo.decimals, type: "WITHDRAWAL_LIMIT" }
29363
29568
  };
29364
29569
  } catch (error) {
29365
- console.error(error);
29570
+ this.config.logger.error("getBridgeableAmount failed", error);
29366
29571
  if (isBridgeLookupError(error)) return { ok: false, error };
29367
29572
  return {
29368
29573
  ok: false,
@@ -29409,7 +29614,7 @@ var BridgeService = class {
29409
29614
  });
29410
29615
  return srcToken.vault.toLowerCase() === dstToken.vault.toLowerCase();
29411
29616
  } catch (error) {
29412
- console.error(error);
29617
+ this.config.logger.error("isBridgeable check failed", error);
29413
29618
  return false;
29414
29619
  }
29415
29620
  }
@@ -31799,7 +32004,7 @@ var ClService = class _ClService {
31799
32004
  };
31800
32005
  const txResult = await this.spoke.sendMessage(sendMessageParams);
31801
32006
  if (!txResult.ok) {
31802
- console.error("executeSupplyLiquidity error:", txResult.error);
32007
+ this.config.logger.error("executeSupplyLiquidity error", txResult.error);
31803
32008
  return {
31804
32009
  ok: false,
31805
32010
  error: txResult.error
@@ -31813,7 +32018,7 @@ var ClService = class _ClService {
31813
32018
  }
31814
32019
  };
31815
32020
  } catch (error) {
31816
- console.error("executeSupplyLiquidity error:", error);
32021
+ this.config.logger.error("executeSupplyLiquidity error", error);
31817
32022
  return {
31818
32023
  ok: false,
31819
32024
  error
@@ -32086,7 +32291,7 @@ var ClService = class _ClService {
32086
32291
  }
32087
32292
  return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32088
32293
  } catch (error) {
32089
- console.error("supplyLiquidity error:", error);
32294
+ this.config.logger.error("supplyLiquidity error", error);
32090
32295
  return {
32091
32296
  ok: false,
32092
32297
  error
@@ -32230,7 +32435,7 @@ var ClService = class _ClService {
32230
32435
  }
32231
32436
  };
32232
32437
  } catch (error) {
32233
- console.error("getPoolRewardConfig error:", error);
32438
+ this.config.logger.error("getPoolRewardConfig error", error);
32234
32439
  return {
32235
32440
  ok: false,
32236
32441
  error: lookupFailed("dex", "getPoolRewardConfig", error)
@@ -32291,7 +32496,7 @@ var ClService = class _ClService {
32291
32496
  };
32292
32497
  const txResult = await this.spoke.sendMessage(sendMessageParams);
32293
32498
  if (!txResult.ok) {
32294
- console.error("executeClaimRewards error:", txResult.error);
32499
+ this.config.logger.error("executeClaimRewards error", txResult.error);
32295
32500
  return {
32296
32501
  ok: false,
32297
32502
  error: txResult.error
@@ -32305,7 +32510,7 @@ var ClService = class _ClService {
32305
32510
  }
32306
32511
  };
32307
32512
  } catch (error) {
32308
- console.error("executeClaimRewards error:", error);
32513
+ this.config.logger.error("executeClaimRewards error", error);
32309
32514
  return {
32310
32515
  ok: false,
32311
32516
  error
@@ -32347,7 +32552,7 @@ var ClService = class _ClService {
32347
32552
  }
32348
32553
  return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: hubTxHash } };
32349
32554
  } catch (error) {
32350
- console.error("claimRewards error:", error);
32555
+ this.config.logger.error("claimRewards error", error);
32351
32556
  return {
32352
32557
  ok: false,
32353
32558
  error
@@ -32395,7 +32600,7 @@ var ClService = class _ClService {
32395
32600
  address: tokenAddress
32396
32601
  };
32397
32602
  } catch (error) {
32398
- console.error(`Failed to fetch token info for ${tokenAddress}:`, error);
32603
+ this.config.logger.error(`Failed to fetch token info for ${tokenAddress}`, error);
32399
32604
  return {
32400
32605
  symbol: "UNKNOWN",
32401
32606
  name: "Unknown Token",
@@ -32420,12 +32625,12 @@ var ClService = class _ClService {
32420
32625
  const oneShare = BigInt(10 ** 18);
32421
32626
  const result = await Erc4626Service.convertToAssets(statATokenAddress, oneShare, this.hubProvider.publicClient);
32422
32627
  if (!result.ok) {
32423
- console.error("[getStatATokenConversionRate] Failed to get conversion rate:", result.error);
32628
+ this.config.logger.error("[getStatATokenConversionRate] Failed to get conversion rate", result.error);
32424
32629
  return oneShare;
32425
32630
  }
32426
32631
  return result.value;
32427
32632
  } catch (error) {
32428
- console.error("[getStatATokenConversionRate] Error:", error);
32633
+ this.config.logger.error("[getStatATokenConversionRate] Error", error);
32429
32634
  return BigInt(10 ** 18);
32430
32635
  }
32431
32636
  }
@@ -32462,7 +32667,7 @@ var ClService = class _ClService {
32462
32667
  underlyingToken
32463
32668
  };
32464
32669
  } catch (error) {
32465
- console.error(`[getTokenEnrichmentData] Failed to enrich token ${token.address}:`, error);
32670
+ this.config.logger.error(`[getTokenEnrichmentData] Failed to enrich token ${token.address}`, error);
32466
32671
  return {
32467
32672
  token,
32468
32673
  isStatAToken: true
@@ -32532,7 +32737,7 @@ var ClService = class _ClService {
32532
32737
  rewardConfig = rewardConfigResult.value;
32533
32738
  }
32534
32739
  } catch (error) {
32535
- console.warn("Failed to fetch reward config for pool:", error);
32740
+ this.config.logger.warn("Failed to fetch reward config for pool", { error });
32536
32741
  }
32537
32742
  }
32538
32743
  return {
@@ -32568,7 +32773,7 @@ var ClService = class _ClService {
32568
32773
  }
32569
32774
  };
32570
32775
  } catch (error) {
32571
- console.error("Failed to fetch pool data:", error);
32776
+ this.config.logger.error("Failed to fetch pool data", error);
32572
32777
  return {
32573
32778
  ok: false,
32574
32779
  error: lookupFailed("dex", "getPoolData", error)
@@ -35352,14 +35557,25 @@ var MoneyMarketService = class _MoneyMarketService {
35352
35557
  );
35353
35558
  const dstChainKey = params.dstChainKey ?? srcChainKey;
35354
35559
  const dstAddress = params.dstAddress ?? params.srcAddress;
35355
- const [fromHubWallet, toHubWallet] = await Promise.all([
35356
- this.hubProvider.getUserHubWalletAddress(params.srcAddress, srcChainKey),
35357
- this.hubProvider.getUserHubWalletAddress(dstAddress, dstChainKey)
35358
- ]);
35560
+ const isBitcoinSrc = isBitcoinChainKeyType(srcChainKey);
35561
+ if (isBitcoinSrc && !_params.raw) {
35562
+ mmInvariant(
35563
+ walletProvider !== void 0 && isBitcoinWalletProviderType(walletProvider),
35564
+ `Invalid wallet provider for chain key: ${srcChainKey}. Expected bitcoin wallet provider.`,
35565
+ { ...baseCtx, field: "walletProvider" }
35566
+ );
35567
+ await this.spoke.bitcoin.radfi.ensureRadfiAccessToken(walletProvider);
35568
+ }
35569
+ const srcEffectiveAddress = isBitcoinSrc ? await this.spoke.bitcoin.getEffectiveWalletAddress(params.srcAddress) : params.srcAddress;
35570
+ const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(srcEffectiveAddress, srcChainKey);
35571
+ const toHubWallet = dstChainKey === srcChainKey && dstAddress === params.srcAddress ? fromHubWallet : await this.hubProvider.getUserHubWalletAddress(
35572
+ isBitcoinChainKeyType(dstChainKey) ? await this.spoke.bitcoin.getEffectiveWalletAddress(dstAddress) : dstAddress,
35573
+ dstChainKey
35574
+ );
35359
35575
  const data = this.buildSupplyData(srcChainKey, params.token, params.amount, toHubWallet);
35360
35576
  const coreParams = {
35361
35577
  srcChainKey,
35362
- srcAddress: params.srcAddress,
35578
+ srcAddress: srcEffectiveAddress,
35363
35579
  to: fromHubWallet,
35364
35580
  token: params.token,
35365
35581
  amount: params.amount,
@@ -35399,6 +35615,17 @@ var MoneyMarketService = class _MoneyMarketService {
35399
35615
  }
35400
35616
  }
35401
35617
  // ==== borrow ==========================================================================
35618
+ /**
35619
+ * Build the relay submit/poll identity for a money-market borrow/withdraw.
35620
+ *
35621
+ * Bitcoin borrow/withdraw are on-demand: the spoke result is a signed payload JSON that the relay
35622
+ * submits under the literal "withdraw" tx_hash and tracks under a derived `od:<hash>` poll id
35623
+ * (see {@link BitcoinSpokeService.getOnDemandRelayIdentity}). Every other chain relays and polls by
35624
+ * its real spoke tx hash, so `pollTxHash` is undefined and `srcChainTxHash` stays the spoke tx.
35625
+ */
35626
+ buildRelayIdentity(srcChainKey, tx, relayData) {
35627
+ return isBitcoinChainKeyType(srcChainKey) ? this.spoke.bitcoin.getOnDemandRelayIdentity(tx) : { srcTxHash: tx, data: relayData, pollTxHash: void 0 };
35628
+ }
35402
35629
  /**
35403
35630
  * Borrow tokens from the money market lending pool and wait for the cross-chain relay to
35404
35631
  * deliver the funds to the destination address.
@@ -35434,9 +35661,9 @@ var MoneyMarketService = class _MoneyMarketService {
35434
35661
  value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
35435
35662
  };
35436
35663
  }
35664
+ const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
35437
35665
  const packet = await relayTxAndWaitPacket({
35438
- srcTxHash: txResult.value.tx,
35439
- data: txResult.value.relayData,
35666
+ ...relayIdentity,
35440
35667
  chainKey: srcChainKey,
35441
35668
  relayerApiEndpoint: this.relayerApiEndpoint,
35442
35669
  timeout
@@ -35451,7 +35678,13 @@ var MoneyMarketService = class _MoneyMarketService {
35451
35678
  dstChainKey: baseCtx.dstChainKey
35452
35679
  })
35453
35680
  };
35454
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
35681
+ return {
35682
+ ok: true,
35683
+ value: {
35684
+ srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
35685
+ dstChainTxHash: packet.value.dst_tx_hash
35686
+ }
35687
+ };
35455
35688
  } catch (error) {
35456
35689
  if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
35457
35690
  return {
@@ -35493,7 +35726,8 @@ var MoneyMarketService = class _MoneyMarketService {
35493
35726
  field: "token"
35494
35727
  });
35495
35728
  const encodedDstAddress = encodeAddress(dstChainKey, dstAddress);
35496
- const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(params.srcAddress, srcChainKey);
35729
+ const srcEffectiveAddress = isBitcoinChainKeyType(srcChainKey) ? await this.spoke.bitcoin.getEffectiveWalletAddress(params.srcAddress) : params.srcAddress;
35730
+ const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(srcEffectiveAddress, srcChainKey);
35497
35731
  const payload = this.buildBorrowData(
35498
35732
  fromHubWallet,
35499
35733
  encodedDstAddress,
@@ -35577,9 +35811,9 @@ var MoneyMarketService = class _MoneyMarketService {
35577
35811
  value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: txResult.value.tx }
35578
35812
  };
35579
35813
  }
35814
+ const relayIdentity = this.buildRelayIdentity(srcChainKey, txResult.value.tx, txResult.value.relayData);
35580
35815
  const packet = await relayTxAndWaitPacket({
35581
- srcTxHash: txResult.value.tx,
35582
- data: txResult.value.relayData,
35816
+ ...relayIdentity,
35583
35817
  chainKey: srcChainKey,
35584
35818
  relayerApiEndpoint: this.relayerApiEndpoint,
35585
35819
  timeout
@@ -35594,7 +35828,13 @@ var MoneyMarketService = class _MoneyMarketService {
35594
35828
  dstChainKey: baseCtx.dstChainKey
35595
35829
  })
35596
35830
  };
35597
- return { ok: true, value: { srcChainTxHash: txResult.value.tx, dstChainTxHash: packet.value.dst_tx_hash } };
35831
+ return {
35832
+ ok: true,
35833
+ value: {
35834
+ srcChainTxHash: relayIdentity.pollTxHash ?? txResult.value.tx,
35835
+ dstChainTxHash: packet.value.dst_tx_hash
35836
+ }
35837
+ };
35598
35838
  } catch (error) {
35599
35839
  if (isMoneyMarketOrchestrationError(error)) return { ok: false, error };
35600
35840
  return {
@@ -35636,7 +35876,8 @@ var MoneyMarketService = class _MoneyMarketService {
35636
35876
  { ...baseCtx, field: "token" }
35637
35877
  );
35638
35878
  const encodedDstAddress = encodeAddress(dstChainKey, dstAddress);
35639
- const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(params.srcAddress, srcChainKey);
35879
+ const srcEffectiveAddress = isBitcoinChainKeyType(srcChainKey) ? await this.spoke.bitcoin.getEffectiveWalletAddress(params.srcAddress) : params.srcAddress;
35880
+ const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(srcEffectiveAddress, srcChainKey);
35640
35881
  const payload = this.buildWithdrawData(
35641
35882
  fromHubWallet,
35642
35883
  encodedDstAddress,
@@ -35773,14 +36014,25 @@ var MoneyMarketService = class _MoneyMarketService {
35773
36014
  );
35774
36015
  const dstChainKey = params.dstChainKey ?? srcChainKey;
35775
36016
  const dstAddress = params.dstAddress ?? params.srcAddress;
35776
- const [fromHubWallet, toHubWallet] = await Promise.all([
35777
- this.hubProvider.getUserHubWalletAddress(params.srcAddress, srcChainKey),
35778
- this.hubProvider.getUserHubWalletAddress(dstAddress, dstChainKey)
35779
- ]);
36017
+ const isBitcoinSrc = isBitcoinChainKeyType(srcChainKey);
36018
+ if (isBitcoinSrc && !_params.raw) {
36019
+ mmInvariant(
36020
+ walletProvider !== void 0 && isBitcoinWalletProviderType(walletProvider),
36021
+ `Invalid wallet provider for chain key: ${srcChainKey}. Expected bitcoin wallet provider.`,
36022
+ { ...baseCtx, field: "walletProvider" }
36023
+ );
36024
+ await this.spoke.bitcoin.radfi.ensureRadfiAccessToken(walletProvider);
36025
+ }
36026
+ const srcEffectiveAddress = isBitcoinSrc ? await this.spoke.bitcoin.getEffectiveWalletAddress(params.srcAddress) : params.srcAddress;
36027
+ const fromHubWallet = await this.hubProvider.getUserHubWalletAddress(srcEffectiveAddress, srcChainKey);
36028
+ const toHubWallet = dstChainKey === srcChainKey && dstAddress === params.srcAddress ? fromHubWallet : await this.hubProvider.getUserHubWalletAddress(
36029
+ isBitcoinChainKeyType(dstChainKey) ? await this.spoke.bitcoin.getEffectiveWalletAddress(dstAddress) : dstAddress,
36030
+ dstChainKey
36031
+ );
35780
36032
  const data = this.buildRepayData(srcChainKey, params.token, params.amount, toHubWallet);
35781
36033
  const coreParams = {
35782
36034
  srcChainKey,
35783
- srcAddress: params.srcAddress,
36035
+ srcAddress: srcEffectiveAddress,
35784
36036
  to: fromHubWallet,
35785
36037
  token: params.token,
35786
36038
  amount: params.amount,
@@ -36341,9 +36593,9 @@ var PartnerFeeClaimService = class {
36341
36593
  if (typeof balanceResult === "bigint") {
36342
36594
  balance = balanceResult;
36343
36595
  } else {
36344
- console.warn(
36345
- `[PartnerFeeClaimService] Unexpected balance result format for ${entry.hubAsset.symbol} (${entry.assetAddress}):`,
36346
- balanceResult
36596
+ this.config.logger.warn(
36597
+ `[PartnerFeeClaimService] Unexpected balance result format for ${entry.hubAsset.symbol} (${entry.assetAddress})`,
36598
+ { balanceResult }
36347
36599
  );
36348
36600
  balance = 0n;
36349
36601
  }
@@ -36679,7 +36931,8 @@ var PartnerFeeClaimService = class {
36679
36931
  }
36680
36932
  const solverExecutionResponse = await SolverApiService.postExecution(
36681
36933
  { intent_tx_hash: intentTxHash },
36682
- this.config.solver
36934
+ this.config.solver,
36935
+ this.config.logger
36683
36936
  );
36684
36937
  if (!solverExecutionResponse.ok) {
36685
36938
  return solverExecutionResponse;
@@ -36890,9 +37143,10 @@ var Sodax = class {
36890
37143
  spoke;
36891
37144
  // spoke service enabling spoke chain operations
36892
37145
  constructor(config) {
37146
+ const logger = resolveLogger(config?.logger);
36893
37147
  this.instanceConfig = config ? mergeSodaxConfig(sodaxConfig, config) : sodaxConfig;
36894
- this.backendApi = new BackendApiService(this.instanceConfig.api);
36895
- this.config = new ConfigService({ api: this.backendApi, config: this.instanceConfig, userConfig: config });
37148
+ this.backendApi = new BackendApiService(this.instanceConfig.api, logger);
37149
+ this.config = new ConfigService({ api: this.backendApi, config: this.instanceConfig, userConfig: config, logger });
36896
37150
  this.hubProvider = new EvmHubProvider({ config: this.config });
36897
37151
  this.spoke = new SpokeService({ config: this.config, hubProvider: this.hubProvider });
36898
37152
  this.swaps = new SwapService({
@@ -37045,6 +37299,7 @@ exports.MoneyMarketService = MoneyMarketService;
37045
37299
  exports.NEAR_CHAIN_KEYS = NEAR_CHAIN_KEYS;
37046
37300
  exports.NEAR_CHAIN_KEYS_SET = NEAR_CHAIN_KEYS_SET;
37047
37301
  exports.NEAR_DEFAULT_GAS = NEAR_DEFAULT_GAS;
37302
+ exports.NEAR_STORAGE_DEPOSIT = NEAR_STORAGE_DEPOSIT;
37048
37303
  exports.NearSpokeService = NearSpokeService;
37049
37304
  exports.PartnerFeeClaimService = PartnerFeeClaimService;
37050
37305
  exports.PartnerService = PartnerService;
@@ -37108,6 +37363,7 @@ exports.bnUSDLegacyTokens = bnUSDLegacyTokens;
37108
37363
  exports.bridgeConfig = bridgeConfig;
37109
37364
  exports.bridgeInvariant = bridgeInvariant;
37110
37365
  exports.bscSupportedTokens = bscSupportedTokens;
37366
+ exports.calcOpReturnOutputVbytes = calcOpReturnOutputVbytes;
37111
37367
  exports.calculateAllReserveIncentives = calculateAllReserveIncentives;
37112
37368
  exports.calculateAllUserIncentives = calculateAllUserIncentives;
37113
37369
  exports.calculateAvailableBorrowsMarketReferenceCurrency = calculateAvailableBorrowsMarketReferenceCurrency;
@@ -37126,6 +37382,7 @@ exports.clRouterAbi = clRouterAbi;
37126
37382
  exports.clTickLensAbi = clTickLensAbi;
37127
37383
  exports.concentratedLiquidityConfig = concentratedLiquidityConfig;
37128
37384
  exports.connectionAbi = connectionAbi;
37385
+ exports.consoleLogger = consoleLogger;
37129
37386
  exports.convertTransactionInstructionToRaw = convertTransactionInstructionToRaw;
37130
37387
  exports.createInvariant = createInvariant;
37131
37388
  exports.deepMerge = deepMerge;
@@ -37302,6 +37559,7 @@ exports.newbnUSDSpokeChainIds = newbnUSDSpokeChainIds;
37302
37559
  exports.normalize = normalize;
37303
37560
  exports.normalizeBN = normalizeBN;
37304
37561
  exports.normalizePsbtToBase64 = normalizePsbtToBase64;
37562
+ exports.normalizeSignatureToBase64 = normalizeSignatureToBase64;
37305
37563
  exports.normalizedToUsd = normalizedToUsd;
37306
37564
  exports.optimismSupportedTokens = optimismSupportedTokens;
37307
37565
  exports.pancakeSwapInfinityDefaultHookAbi = pancakeSwapInfinityDefaultHookAbi;
@@ -37326,9 +37584,11 @@ exports.relayTxAndWaitPacket = relayTxAndWaitPacket;
37326
37584
  exports.requestAddress = requestAddress;
37327
37585
  exports.requestJsonRpc = requestJsonRpc;
37328
37586
  exports.requestSigning = requestSigning;
37587
+ exports.resolveLogger = resolveLogger;
37329
37588
  exports.retry = retry;
37330
37589
  exports.reverseEncodeAddress = reverseEncodeAddress;
37331
37590
  exports.serializeAddressData = serializeAddressData;
37591
+ exports.silentLogger = silentLogger;
37332
37592
  exports.sleep = sleep;
37333
37593
  exports.sodaxConfig = sodaxConfig;
37334
37594
  exports.sodaxInvariant = sodaxInvariant;
@@ -37356,6 +37616,7 @@ exports.swapsConfig = swapsConfig;
37356
37616
  exports.uiPoolDataAbi = uiPoolDataAbi;
37357
37617
  exports.universalRouterAbi = universalRouterAbi;
37358
37618
  exports.unknownFailed = unknownFailed;
37619
+ exports.usesBip322MessageSigning = usesBip322MessageSigning;
37359
37620
  exports.valueToBigNumber = valueToBigNumber;
37360
37621
  exports.valueToZDBigNumber = valueToZDBigNumber;
37361
37622
  exports.variableDebtTokenAbi = variableDebtTokenAbi;