@sodax/sdk 1.3.1-beta-rc1 → 1.3.1-beta-rc3

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
@@ -17,6 +17,8 @@ var coreProtoTs = require('@injectivelabs/core-proto-ts');
17
17
  var rlp = require('rlp');
18
18
  var anchor = require('@coral-xyz/anchor');
19
19
  var BN = require('bn.js');
20
+ var bitcoin = require('bitcoinjs-lib');
21
+ var ecc = require('@bitcoinerlab/secp256k1');
20
22
 
21
23
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
24
 
@@ -44,6 +46,8 @@ var BigNumber4__default = /*#__PURE__*/_interopDefault(BigNumber4);
44
46
  var rlp__namespace = /*#__PURE__*/_interopNamespace(rlp);
45
47
  var anchor__namespace = /*#__PURE__*/_interopNamespace(anchor);
46
48
  var BN__default = /*#__PURE__*/_interopDefault(BN);
49
+ var bitcoin__namespace = /*#__PURE__*/_interopNamespace(bitcoin);
50
+ var ecc__namespace = /*#__PURE__*/_interopNamespace(ecc);
47
51
 
48
52
  // src/shared/abis/asset-manager.abi.ts
49
53
  var assetManagerAbi = [
@@ -6855,7 +6859,7 @@ var stakingRouterAbi = [
6855
6859
  ];
6856
6860
 
6857
6861
  // ../types/dist/constants/index.js
6858
- var CONFIG_VERSION = 27;
6862
+ var CONFIG_VERSION = 28;
6859
6863
  var AVALANCHE_MAINNET_CHAIN_ID = "0xa86a.avax";
6860
6864
  var ARBITRUM_MAINNET_CHAIN_ID = "0xa4b1.arbitrum";
6861
6865
  var BASE_MAINNET_CHAIN_ID = "0x2105.base";
@@ -6872,6 +6876,7 @@ var HYPEREVM_MAINNET_CHAIN_ID = "hyper";
6872
6876
  var LIGHTLINK_MAINNET_CHAIN_ID = "lightlink";
6873
6877
  var NEAR_MAINNET_CHAIN_ID = "near";
6874
6878
  var ETHEREUM_MAINNET_CHAIN_ID = "ethereum";
6879
+ var BITCOIN_MAINNET_CHAIN_ID = "bitcoin";
6875
6880
  var REDBELLY_MAINNET_CHAIN_ID = "redbelly";
6876
6881
  var KAIA_MAINNET_CHAIN_ID = "0x2019.kaia";
6877
6882
  var HUB_CHAIN_IDS = [SONIC_MAINNET_CHAIN_ID];
@@ -6892,6 +6897,7 @@ var CHAIN_IDS = [
6892
6897
  LIGHTLINK_MAINNET_CHAIN_ID,
6893
6898
  NEAR_MAINNET_CHAIN_ID,
6894
6899
  ETHEREUM_MAINNET_CHAIN_ID,
6900
+ BITCOIN_MAINNET_CHAIN_ID,
6895
6901
  REDBELLY_MAINNET_CHAIN_ID,
6896
6902
  KAIA_MAINNET_CHAIN_ID
6897
6903
  ];
@@ -7006,6 +7012,12 @@ var baseChainInfo = {
7006
7012
  type: "EVM",
7007
7013
  chainId: 1
7008
7014
  },
7015
+ [BITCOIN_MAINNET_CHAIN_ID]: {
7016
+ name: "Bitcoin",
7017
+ id: BITCOIN_MAINNET_CHAIN_ID,
7018
+ type: "BITCOIN",
7019
+ chainId: "bitcoin"
7020
+ },
7009
7021
  [REDBELLY_MAINNET_CHAIN_ID]: {
7010
7022
  name: "Redbelly",
7011
7023
  id: REDBELLY_MAINNET_CHAIN_ID,
@@ -7036,6 +7048,7 @@ var ChainIdToIntentRelayChainId = {
7036
7048
  [LIGHTLINK_MAINNET_CHAIN_ID]: 27756n,
7037
7049
  [NEAR_MAINNET_CHAIN_ID]: 15n,
7038
7050
  [ETHEREUM_MAINNET_CHAIN_ID]: 2n,
7051
+ [BITCOIN_MAINNET_CHAIN_ID]: 627463n,
7039
7052
  [REDBELLY_MAINNET_CHAIN_ID]: 726564n,
7040
7053
  [KAIA_MAINNET_CHAIN_ID]: 27489n
7041
7054
  };
@@ -7948,6 +7961,42 @@ var spokeChainConfig = {
7948
7961
  rpcUrl: "https://injective-rpc.publicnode.com:443",
7949
7962
  walletAddress: ""
7950
7963
  },
7964
+ [BITCOIN_MAINNET_CHAIN_ID]: {
7965
+ addresses: {
7966
+ assetManager: "bc1pxguu2r4p9jcxp3gj7dh4r4jd9qzccwpyap3nj5nlapy28s76lhrqw522fz"
7967
+ },
7968
+ chain: baseChainInfo[BITCOIN_MAINNET_CHAIN_ID],
7969
+ bnUSD: "no",
7970
+ nativeToken: "BTC",
7971
+ supportedTokens: {
7972
+ BTC: {
7973
+ symbol: "BTC",
7974
+ name: "Bitcoin",
7975
+ decimals: 8,
7976
+ address: "0:0",
7977
+ xChainId: BITCOIN_MAINNET_CHAIN_ID
7978
+ },
7979
+ bnUSD: {
7980
+ symbol: "bnUSD",
7981
+ name: "bnUSD",
7982
+ decimals: 18,
7983
+ address: "0:0",
7984
+ xChainId: BITCOIN_MAINNET_CHAIN_ID
7985
+ },
7986
+ BUSD: {
7987
+ symbol: "BUSD",
7988
+ name: "BUSDSTABLECOIN",
7989
+ decimals: 6,
7990
+ address: "897442:43",
7991
+ xChainId: BITCOIN_MAINNET_CHAIN_ID
7992
+ }
7993
+ },
7994
+ radfiApiUrl: "https://api.canary.radfi.co/api",
7995
+ radfiApiKey: "",
7996
+ radfiUmsUrl: "https://ums.radfi.co/api",
7997
+ network: "MAINNET",
7998
+ rpcUrl: "https://mempool.space/api"
7999
+ },
7951
8000
  [STELLAR_MAINNET_CHAIN_ID]: {
7952
8001
  addresses: {
7953
8002
  connection: "CDFQDDPUPAM3XPGORHDOEFRNLMKOH3N3X6XTXNLSXJQXIU3RVCM3OPEP",
@@ -9444,6 +9493,22 @@ var hubAssets = {
9444
9493
  name: "RedBelly POL",
9445
9494
  vault: SodaTokens.sodaPOL.address
9446
9495
  }
9496
+ },
9497
+ [BITCOIN_MAINNET_CHAIN_ID]: {
9498
+ [spokeChainConfig[BITCOIN_MAINNET_CHAIN_ID].supportedTokens.BTC.address]: {
9499
+ asset: "0xeb0393893b5bf98a50073d6740738b08e575058b",
9500
+ decimal: 8,
9501
+ symbol: "BTC",
9502
+ name: "Bitcoin",
9503
+ vault: "0x7A1A5555842Ad2D0eD274d09b5c4406a95799D5d"
9504
+ },
9505
+ [spokeChainConfig[BITCOIN_MAINNET_CHAIN_ID].supportedTokens.BUSD.address]: {
9506
+ asset: "0xdb41c7d09406026d4582bc2fc6d6319c323fe1bb",
9507
+ decimal: 6,
9508
+ symbol: "BUSD",
9509
+ name: "BUSD.BUSD.BUSD",
9510
+ vault: "0xE801CA34E19aBCbFeA12025378D19c4FBE250131"
9511
+ }
9447
9512
  }
9448
9513
  };
9449
9514
  var solverConfig = {
@@ -9584,6 +9649,10 @@ var swapSupportedTokens = {
9584
9649
  spokeChainConfig[NEAR_MAINNET_CHAIN_ID].supportedTokens.USDC,
9585
9650
  spokeChainConfig[NEAR_MAINNET_CHAIN_ID].supportedTokens.USDT
9586
9651
  ],
9652
+ [BITCOIN_MAINNET_CHAIN_ID]: [
9653
+ spokeChainConfig[BITCOIN_MAINNET_CHAIN_ID].supportedTokens.BTC
9654
+ // spokeChainConfig[BITCOIN_MAINNET_CHAIN_ID].supportedTokens.BUSD, // TODO: re-enable when trading wallet balance is ready
9655
+ ],
9587
9656
  [ETHEREUM_MAINNET_CHAIN_ID]: [
9588
9657
  spokeChainConfig[ETHEREUM_MAINNET_CHAIN_ID].supportedTokens.ETH,
9589
9658
  spokeChainConfig[ETHEREUM_MAINNET_CHAIN_ID].supportedTokens.bnUSD,
@@ -9785,6 +9854,9 @@ var moneyMarketSupportedTokens = {
9785
9854
  spokeChainConfig[KAIA_MAINNET_CHAIN_ID].supportedTokens.bnUSD,
9786
9855
  spokeChainConfig[KAIA_MAINNET_CHAIN_ID].supportedTokens.USDT,
9787
9856
  spokeChainConfig[KAIA_MAINNET_CHAIN_ID].supportedTokens.SODA
9857
+ ],
9858
+ [BITCOIN_MAINNET_CHAIN_ID]: [
9859
+ spokeChainConfig[BITCOIN_MAINNET_CHAIN_ID].supportedTokens.BTC
9788
9860
  ]
9789
9861
  };
9790
9862
  var moneyMarketReserveAssets = [
@@ -9809,7 +9881,7 @@ var defaultSharedConfig = {
9809
9881
  };
9810
9882
 
9811
9883
  // ../types/dist/common/index.js
9812
- var ChainTypeArr = ["ICON", "EVM", "INJECTIVE", "SUI", "STELLAR", "SOLANA", "NEAR"];
9884
+ var ChainTypeArr = ["ICON", "EVM", "INJECTIVE", "SUI", "STELLAR", "SOLANA", "NEAR", "BITCOIN"];
9813
9885
 
9814
9886
  // ../types/dist/injective/index.js
9815
9887
  var InjectiveExecuteResponse = class _InjectiveExecuteResponse {
@@ -9828,6 +9900,17 @@ var InjectiveExecuteResponse = class _InjectiveExecuteResponse {
9828
9900
  return response;
9829
9901
  }
9830
9902
  };
9903
+
9904
+ // ../types/dist/btc/index.js
9905
+ function detectBitcoinAddressType(address) {
9906
+ if (address.startsWith("bc1p") || address.startsWith("tb1p"))
9907
+ return "P2TR";
9908
+ if (address.startsWith("bc1") || address.startsWith("tb1"))
9909
+ return "P2WPKH";
9910
+ if (address.startsWith("1") || address.startsWith("m") || address.startsWith("n"))
9911
+ return "P2PKH";
9912
+ throw new Error(`Unknown Bitcoin address type: ${address}`);
9913
+ }
9831
9914
  var DEFAULT_MAX_RETRY = 3;
9832
9915
  var DEFAULT_RELAY_TX_TIMEOUT = 12e4;
9833
9916
  var DEFAULT_RETRY_DELAY_MS = 2e3;
@@ -21206,6 +21289,714 @@ var SolanaSpokeService = class _SolanaSpokeService {
21206
21289
  }
21207
21290
  }
21208
21291
  };
21292
+
21293
+ // src/shared/entities/btc/RadfiProvider.ts
21294
+ var RadfiProvider = class {
21295
+ constructor(config) {
21296
+ this.config = config;
21297
+ }
21298
+ async authenticate(params) {
21299
+ const res = await this.request("/auth/authenticate", {
21300
+ method: "POST",
21301
+ body: JSON.stringify(params)
21302
+ });
21303
+ if (!res.ok) {
21304
+ const err = await res.json();
21305
+ throw new Error(err.message || "Radfi authentication failed");
21306
+ }
21307
+ return res.json().then((r) => ({
21308
+ accessToken: r.data?.accessToken ?? "",
21309
+ refreshToken: r.data?.refreshToken ?? "",
21310
+ tradingAddress: r.data?.tradingAddress ?? r.data?.wallet?.tradingAddress ?? ""
21311
+ }));
21312
+ }
21313
+ async refreshAccessToken(refreshToken) {
21314
+ const res = await this.request("/auth/refresh-token", {
21315
+ method: "POST",
21316
+ body: JSON.stringify({ refreshToken })
21317
+ });
21318
+ if (!res.ok) {
21319
+ const err = await res.json();
21320
+ throw new Error(err.message || "Token refresh failed");
21321
+ }
21322
+ return res.json().then((r) => ({
21323
+ accessToken: r.data?.accessToken ?? "",
21324
+ refreshToken: r.data?.refreshToken ?? refreshToken
21325
+ }));
21326
+ }
21327
+ async createTradingWallet(params, accessToken) {
21328
+ const res = await this.request("/wallets", {
21329
+ method: "POST",
21330
+ headers: {
21331
+ Authorization: `Bearer ${accessToken || this.config.apiKey}`
21332
+ },
21333
+ body: JSON.stringify(params)
21334
+ });
21335
+ if (!res.ok) {
21336
+ const err = await res.json();
21337
+ throw new Error(err.message || "Failed to create trading wallet");
21338
+ }
21339
+ return res.json().then((r) => r.data);
21340
+ }
21341
+ async getTradingWallet(userAddress, accessToken) {
21342
+ const res = await this.request(`/wallets/details/${userAddress}`, {
21343
+ method: "GET",
21344
+ headers: accessToken ? { Authorization: `Bearer ${accessToken}` } : {}
21345
+ });
21346
+ if (!res.ok) {
21347
+ throw new Error("Trading wallet not found");
21348
+ }
21349
+ const data = await res.json().then((r) => r.data);
21350
+ if (!data) throw new Error("Trading wallet not found");
21351
+ return data;
21352
+ }
21353
+ async getBalance(address) {
21354
+ if (!this.config.umsUrl) {
21355
+ throw new Error("RadfiConfig.umsUrl is required for getBalance");
21356
+ }
21357
+ const umsUrl = this.config.umsUrl;
21358
+ const res = await fetch(`${umsUrl}/wallets/balance?address=${address}`, {
21359
+ method: "GET",
21360
+ headers: { "Content-Type": "application/json" }
21361
+ });
21362
+ if (!res.ok) {
21363
+ throw new Error("Failed to fetch wallet balance");
21364
+ }
21365
+ const { data } = await res.json();
21366
+ return {
21367
+ btcSatoshi: BigInt(data.btcSatoshi ?? "0"),
21368
+ pendingSatoshi: BigInt(data.pendingSatoshi ?? "0"),
21369
+ externalPendingSatoshi: BigInt(data.externalPendingSatoshi ?? "0"),
21370
+ totalUtxos: Number(data.totalUtxos ?? 0)
21371
+ };
21372
+ }
21373
+ async checkIfTradingWalletExists(userAddress) {
21374
+ try {
21375
+ await this.getTradingWallet(userAddress);
21376
+ return true;
21377
+ } catch (error) {
21378
+ return false;
21379
+ }
21380
+ }
21381
+ async createWithdrawTransaction(params, accessToken) {
21382
+ const res = await this.request("/sodax/transaction", {
21383
+ method: "POST",
21384
+ headers: {
21385
+ Authorization: `Bearer ${accessToken ?? this.config.apiKey}`
21386
+ },
21387
+ body: JSON.stringify({
21388
+ type: "sodax-withdraw",
21389
+ params: {
21390
+ amount: params.amount.toString(),
21391
+ tokenId: params.token,
21392
+ sodaxData: params.data
21393
+ }
21394
+ })
21395
+ });
21396
+ if (!res.ok) {
21397
+ const err = await res.json();
21398
+ throw new Error(err.message || "Radfi transaction request failed");
21399
+ }
21400
+ return res.json().then((r) => r.data);
21401
+ }
21402
+ async requestRadfiSignature(params, accessToken) {
21403
+ const res = await this.request("/sodax/transaction/sign", {
21404
+ method: "POST",
21405
+ headers: {
21406
+ Authorization: `Bearer ${accessToken ?? this.config.apiKey}`
21407
+ },
21408
+ body: JSON.stringify({
21409
+ type: "sodax-withdraw",
21410
+ params
21411
+ })
21412
+ });
21413
+ if (!res.ok) {
21414
+ const err = await res.json();
21415
+ throw new Error(err.message || "Radfi signature request failed");
21416
+ }
21417
+ return res.json().then((r) => r.data.txId);
21418
+ }
21419
+ /**
21420
+ * Fetch expired (or near-expiry) UTXOs for a trading wallet address from UMS API.
21421
+ */
21422
+ async getExpiredUtxos(tradingAddress, params) {
21423
+ if (!this.config.umsUrl) {
21424
+ throw new Error("RadfiConfig.umsUrl is required for getExpiredUtxos");
21425
+ }
21426
+ const page = params?.page ?? 1;
21427
+ const pageSize = params?.pageSize ?? 100;
21428
+ const url = `${this.config.umsUrl}/utxos?address_eq=${tradingAddress}&isSpent_eq=false&isExpired_eq=true&page=${page}&pageSize=${pageSize}`;
21429
+ const res = await fetch(url, {
21430
+ method: "GET",
21431
+ headers: { "Content-Type": "application/json" }
21432
+ });
21433
+ if (!res.ok) {
21434
+ throw new Error("Failed to fetch expired UTXOs");
21435
+ }
21436
+ return res.json();
21437
+ }
21438
+ /**
21439
+ * Build a renew-utxo transaction via the Radfi API.
21440
+ * Returns a PSBT that needs to be signed by the user.
21441
+ */
21442
+ async buildRenewUtxoTransaction(params, accessToken) {
21443
+ const res = await this.request("/transactions", {
21444
+ method: "POST",
21445
+ headers: {
21446
+ Authorization: `Bearer ${accessToken}`
21447
+ },
21448
+ body: JSON.stringify({
21449
+ type: "renew-utxo",
21450
+ params: {
21451
+ userAddress: params.userAddress,
21452
+ txIdVouts: params.txIdVouts
21453
+ }
21454
+ })
21455
+ });
21456
+ if (!res.ok) {
21457
+ const err = await res.json();
21458
+ throw new Error(err.message || "Failed to build renew-utxo transaction");
21459
+ }
21460
+ return res.json().then((r) => r.data);
21461
+ }
21462
+ /**
21463
+ * Sign and broadcast a renew-utxo transaction via the Radfi API.
21464
+ * The user signs the PSBT first, then Radfi co-signs and broadcasts.
21465
+ */
21466
+ async signAndBroadcastRenewUtxo(params, accessToken) {
21467
+ const res = await this.request("/transactions/sign", {
21468
+ method: "POST",
21469
+ headers: {
21470
+ Authorization: `Bearer ${accessToken}`
21471
+ },
21472
+ body: JSON.stringify({
21473
+ type: "renew-utxo",
21474
+ params
21475
+ })
21476
+ });
21477
+ if (!res.ok) {
21478
+ const err = await res.json();
21479
+ throw new Error(err.message || "Failed to sign and broadcast renew-utxo transaction");
21480
+ }
21481
+ return res.json().then((r) => r.data.txId);
21482
+ }
21483
+ async request(endpoint, options) {
21484
+ return fetch(`${this.config.url}${endpoint}`, {
21485
+ ...options,
21486
+ headers: {
21487
+ "Content-Type": "application/json",
21488
+ ...options?.headers || {}
21489
+ }
21490
+ });
21491
+ }
21492
+ };
21493
+ bitcoin__namespace.initEccLib(ecc__namespace);
21494
+ var BITCOIN_DEFAULT_FEE_RATE = 3;
21495
+ var DUST_THRESHOLD = 546;
21496
+ function normalizePsbtToBase64(signedPsbt) {
21497
+ const isHex = /^[0-9a-fA-F]+$/.test(signedPsbt);
21498
+ return isHex ? Buffer.from(signedPsbt, "hex").toString("base64") : signedPsbt;
21499
+ }
21500
+ var BitcoinBaseSpokeProvider = class _BitcoinBaseSpokeProvider {
21501
+ rpcUrl;
21502
+ network;
21503
+ chainConfig;
21504
+ radfi;
21505
+ walletMode;
21506
+ radfiAccessToken = "";
21507
+ constructor(config, radfiConfig, walletMode = "USER", rpcURL) {
21508
+ this.chainConfig = config;
21509
+ this.rpcUrl = rpcURL ?? config.rpcUrl;
21510
+ this.network = config.network === "TESTNET" ? bitcoin__namespace.networks.testnet : bitcoin__namespace.networks.bitcoin;
21511
+ this.radfi = new RadfiProvider(radfiConfig);
21512
+ this.walletMode = walletMode;
21513
+ }
21514
+ setRadfiAccessToken(token) {
21515
+ this.radfiAccessToken = token;
21516
+ }
21517
+ /**
21518
+ * Get current fee estimates
21519
+ */
21520
+ async getFeeEstimate(targetBlocks = 6) {
21521
+ try {
21522
+ const response = await fetch(`${this.rpcUrl}/fee-estimates`);
21523
+ if (!response.ok) {
21524
+ return BITCOIN_DEFAULT_FEE_RATE;
21525
+ }
21526
+ const feeEstimates = await response.json();
21527
+ return feeEstimates[targetBlocks] ?? BITCOIN_DEFAULT_FEE_RATE;
21528
+ } catch {
21529
+ return BITCOIN_DEFAULT_FEE_RATE;
21530
+ }
21531
+ }
21532
+ static async getBalance(tokenAddress, provider) {
21533
+ const walletAddress = await provider.walletProvider.getWalletAddress();
21534
+ if (!tokenAddress || tokenAddress === "0x" || tokenAddress === "BTC") {
21535
+ const utxos = await provider.fetchUTXOs(walletAddress);
21536
+ const totalBalance = utxos.reduce((sum, utxo) => sum + utxo.value, 0);
21537
+ return BigInt(totalBalance);
21538
+ }
21539
+ throw new Error("Token balance queries not yet implemented for non-BTC assets");
21540
+ }
21541
+ async fetchScriptPubKey(utxo, provider) {
21542
+ const txHex = await provider.fetchRawTransaction(utxo.txid);
21543
+ const tx = bitcoin__namespace.Transaction.fromHex(txHex);
21544
+ const out = tx.outs[utxo.vout];
21545
+ if (!out) {
21546
+ throw new Error(`UTXO not found: ${utxo.txid}:${utxo.vout}`);
21547
+ }
21548
+ return out.script.toString("hex");
21549
+ }
21550
+ /**
21551
+ * Build a priority Bitcoin transaction with proper fee calculation
21552
+ */
21553
+ static async buildBitcoinTransaction(utxos, outputs, changeAddress, provider, feeRate) {
21554
+ const psbt = new bitcoin__namespace.Psbt({ network: provider.network });
21555
+ const effectiveFeeRate = feeRate ?? await provider.getFeeEstimate();
21556
+ let inputSum = 0;
21557
+ const outputSum = outputs.reduce((sum, o) => sum + o.value, 0);
21558
+ for (const utxo of utxos) {
21559
+ if (!utxo.status.confirmed) continue;
21560
+ const scriptPubKey = await provider.fetchScriptPubKey(utxo, provider);
21561
+ const isTaproot = scriptPubKey.startsWith("51");
21562
+ const isSegwitV0 = scriptPubKey.startsWith("00");
21563
+ if (isTaproot) {
21564
+ if (!provider.walletProvider.getPublicKey) {
21565
+ throw new Error("Missing public key for P2TR input");
21566
+ }
21567
+ const tapInternalKey = await provider.walletProvider.getPublicKey();
21568
+ psbt.addInput({
21569
+ hash: utxo.txid,
21570
+ index: utxo.vout,
21571
+ witnessUtxo: {
21572
+ script: Buffer.from(scriptPubKey, "hex"),
21573
+ value: utxo.value
21574
+ },
21575
+ tapInternalKey: Buffer.from(tapInternalKey, "hex")
21576
+ });
21577
+ } else if (isSegwitV0) {
21578
+ psbt.addInput({
21579
+ hash: utxo.txid,
21580
+ index: utxo.vout,
21581
+ witnessUtxo: {
21582
+ script: Buffer.from(scriptPubKey, "hex"),
21583
+ value: utxo.value
21584
+ }
21585
+ });
21586
+ } else {
21587
+ const txHex = await provider.fetchRawTransaction(utxo.txid);
21588
+ psbt.addInput({
21589
+ hash: utxo.txid,
21590
+ index: utxo.vout,
21591
+ nonWitnessUtxo: Buffer.from(txHex, "hex")
21592
+ });
21593
+ }
21594
+ inputSum += utxo.value;
21595
+ const estimatedSize = provider.estimateTxSize(psbt.inputCount, outputs.length);
21596
+ const estimatedFee = Math.ceil(effectiveFeeRate * estimatedSize);
21597
+ if (inputSum >= outputSum + estimatedFee + DUST_THRESHOLD) {
21598
+ break;
21599
+ }
21600
+ }
21601
+ for (const output of outputs) {
21602
+ psbt.addOutput({
21603
+ address: output.address,
21604
+ value: output.value
21605
+ });
21606
+ }
21607
+ const sizeWithChange = provider.estimateTxSize(psbt.inputCount, outputs.length + 1);
21608
+ const sizeWithoutChange = provider.estimateTxSize(psbt.inputCount, outputs.length);
21609
+ const feeWithChange = Math.ceil(effectiveFeeRate * sizeWithChange);
21610
+ const feeWithoutChange = Math.ceil(effectiveFeeRate * sizeWithoutChange);
21611
+ let change = inputSum - outputSum - feeWithChange;
21612
+ if (change < 0) {
21613
+ const confirmedCount = utxos.filter((u) => u.status.confirmed).length;
21614
+ const unconfirmedCount = utxos.length - confirmedCount;
21615
+ const hint = unconfirmedCount > 0 ? ` (${unconfirmedCount} unconfirmed UTXO(s) skipped \u2014 wait for confirmation)` : "";
21616
+ throw new Error(`Insufficient funds. Need ${outputSum + feeWithChange} satoshis, have ${inputSum}${hint}`);
21617
+ }
21618
+ if (change > DUST_THRESHOLD) {
21619
+ psbt.addOutput({
21620
+ address: changeAddress,
21621
+ value: change
21622
+ });
21623
+ } else {
21624
+ const finalFee = feeWithoutChange;
21625
+ change = inputSum - outputSum - finalFee;
21626
+ if (change < 0) {
21627
+ throw new Error(`Insufficient funds after dust handling. Need ${outputSum + finalFee}`);
21628
+ }
21629
+ }
21630
+ return psbt;
21631
+ }
21632
+ /**
21633
+ * Deposit operation - transfer BTC to the asset manager
21634
+ */
21635
+ static async deposit(token, amount, data, provider, raw, accessToken = "") {
21636
+ try {
21637
+ const walletAddress = await provider.walletProvider.getWalletAddress();
21638
+ const returnRawTx = (psbtBase64) => ({
21639
+ from: walletAddress,
21640
+ to: provider.chainConfig.addresses.assetManager,
21641
+ value: amount,
21642
+ data: psbtBase64
21643
+ });
21644
+ if (provider.walletMode === "TRADING") {
21645
+ const tokenId = Object.values(provider.chainConfig.supportedTokens).find((t) => t.address === token)?.address;
21646
+ if (!tokenId) {
21647
+ throw new Error(`Unsupported token: ${token}`);
21648
+ }
21649
+ data = data.startsWith("0x") ? data.slice(2) : data;
21650
+ data = data.length === 64 ? data : viem.keccak256(`0x${data}`).slice(2);
21651
+ accessToken = accessToken || provider.radfiAccessToken;
21652
+ const withdrawTx = await provider.radfi.createWithdrawTransaction(
21653
+ {
21654
+ token: tokenId,
21655
+ amount,
21656
+ recipient: provider.chainConfig.addresses.assetManager,
21657
+ userAddress: walletAddress,
21658
+ data
21659
+ },
21660
+ accessToken
21661
+ );
21662
+ if (raw || isBitcoinRawSpokeProvider(provider)) {
21663
+ return returnRawTx(withdrawTx.base64Psbt);
21664
+ }
21665
+ const signedTx = await provider.walletProvider.signTransaction(withdrawTx.base64Psbt, false);
21666
+ const signedBase64Tx = normalizePsbtToBase64(signedTx);
21667
+ return await provider.radfi.requestRadfiSignature(
21668
+ {
21669
+ userAddress: walletAddress,
21670
+ signedBase64Tx
21671
+ },
21672
+ accessToken
21673
+ );
21674
+ }
21675
+ const utxos = await provider.fetchUTXOs(walletAddress);
21676
+ if (!utxos?.length) {
21677
+ throw new Error("No UTXOs available for deposit");
21678
+ }
21679
+ const depositPsbt = await _BitcoinBaseSpokeProvider.buildDepositPsbt(
21680
+ walletAddress,
21681
+ token,
21682
+ amount,
21683
+ data,
21684
+ utxos,
21685
+ provider
21686
+ );
21687
+ if (raw || isBitcoinRawSpokeProvider(provider)) {
21688
+ return returnRawTx(depositPsbt.toBase64());
21689
+ }
21690
+ return await provider.signAndBroadcastTransaction(depositPsbt);
21691
+ } catch (error) {
21692
+ console.error("Error during deposit:", error);
21693
+ throw error;
21694
+ }
21695
+ }
21696
+ /**
21697
+ * Build deposit PSBT with embedded cross-chain data
21698
+ */
21699
+ static async buildDepositPsbt(walletAddress, token, amount, data, utxos, provider) {
21700
+ const assetManagerAddress = provider.chainConfig.addresses.assetManager;
21701
+ if (token.toLocaleLowerCase() === "btc") {
21702
+ const outputs = [
21703
+ {
21704
+ address: assetManagerAddress,
21705
+ value: Number(amount)
21706
+ }
21707
+ ];
21708
+ const psbt = await _BitcoinBaseSpokeProvider.buildBitcoinTransaction(utxos, outputs, walletAddress, provider);
21709
+ const OP_RADFI_SODAX_DATA = 49;
21710
+ const payload = Buffer.concat([Buffer.from([OP_RADFI_SODAX_DATA]), Buffer.from(data.slice(2), "hex")]);
21711
+ const OP_RETURN = bitcoin__namespace.opcodes.OP_RETURN;
21712
+ const OP_12 = bitcoin__namespace.opcodes.OP_12;
21713
+ if (OP_RETURN === void 0 || OP_12 === void 0) {
21714
+ throw new Error("bitcoinjs-lib opcodes OP_RETURN or OP_12 are undefined");
21715
+ }
21716
+ const script2 = bitcoin__namespace.script.compile([OP_RETURN, OP_12, payload]);
21717
+ psbt.addOutput({
21718
+ script: script2,
21719
+ value: 0
21720
+ });
21721
+ return psbt;
21722
+ }
21723
+ throw new Error(`Non-BTC token deposits not yet implemented (token: ${token})`);
21724
+ }
21725
+ /**
21726
+ * Fetch UTXOs for an address
21727
+ */
21728
+ async fetchUTXOs(address) {
21729
+ const response = await fetch(`${this.rpcUrl}/address/${address}/utxo`);
21730
+ if (!response.ok) {
21731
+ throw new Error(`Failed to fetch UTXOs: ${response.statusText}`);
21732
+ }
21733
+ return await response.json();
21734
+ }
21735
+ /**
21736
+ * Fetch raw transaction hex
21737
+ */
21738
+ async fetchRawTransaction(txid) {
21739
+ const response = await fetch(`${this.rpcUrl}/tx/${txid}/hex`);
21740
+ if (!response.ok) {
21741
+ throw new Error(`Failed to fetch transaction: ${response.statusText}`);
21742
+ }
21743
+ return await response.text();
21744
+ }
21745
+ /**
21746
+ * Estimate transaction size in vbytes
21747
+ */
21748
+ estimateTxSize(inputCount, outputCount) {
21749
+ return Math.ceil(10.5 + 44 + inputCount * 68 + outputCount * 31);
21750
+ }
21751
+ getAddressType(address) {
21752
+ return detectBitcoinAddressType(address);
21753
+ }
21754
+ encodePayloadToBytes(payload) {
21755
+ const ordered = {
21756
+ src_address: payload.src_address.toLowerCase(),
21757
+ data: payload.data.toLowerCase(),
21758
+ src_chain_id: payload.src_chain_id,
21759
+ dst_chain_id: payload.dst_chain_id,
21760
+ wallet_used: payload.wallet_used,
21761
+ timestamp: payload.timestamp,
21762
+ address_type: payload.address_type
21763
+ };
21764
+ const json = JSON.stringify(ordered);
21765
+ return json;
21766
+ }
21767
+ static async encodeWithdrawalData(dstChainId, data, provider, raw) {
21768
+ let srcAddress = await provider.walletProvider.getWalletAddress();
21769
+ const addressType = provider.getAddressType(srcAddress);
21770
+ if (provider.walletMode === "TRADING") {
21771
+ srcAddress = await provider.radfi.getTradingWallet(srcAddress).then((res) => res.tradingAddress).catch(() => srcAddress);
21772
+ }
21773
+ const payload = {
21774
+ src_address: srcAddress,
21775
+ data,
21776
+ src_chain_id: Number(getIntentRelayChainId(provider.chainConfig.chain.id)),
21777
+ dst_chain_id: Number(getIntentRelayChainId(dstChainId)),
21778
+ wallet_used: provider.walletMode,
21779
+ timestamp: Date.now(),
21780
+ address_type: addressType
21781
+ };
21782
+ const orderedPayload = provider.encodePayloadToBytes(payload);
21783
+ const onDemandWithdraw = {
21784
+ payload_hex: Buffer.from(orderedPayload).toString("hex"),
21785
+ signature: void 0
21786
+ };
21787
+ if (raw || isBitcoinRawSpokeProvider(provider)) {
21788
+ return JSON.stringify(onDemandWithdraw);
21789
+ }
21790
+ const signature = await provider.walletProvider.signEcdsaMessage(orderedPayload);
21791
+ onDemandWithdraw.signature = signature;
21792
+ return JSON.stringify(onDemandWithdraw);
21793
+ }
21794
+ };
21795
+ var BitcoinRawSpokeProvider = class extends BitcoinBaseSpokeProvider {
21796
+ walletProvider;
21797
+ raw = true;
21798
+ constructor(walletAddress, publicKey, chainConfig, radfiConfig, walletMode = "USER", rpcUrl) {
21799
+ super(chainConfig, radfiConfig, walletMode, rpcUrl);
21800
+ this.walletProvider = {
21801
+ getWalletAddress: async () => walletAddress,
21802
+ getPublicKey: async () => publicKey
21803
+ };
21804
+ }
21805
+ };
21806
+ var BitcoinSpokeProvider = class extends BitcoinBaseSpokeProvider {
21807
+ walletProvider;
21808
+ constructor(walletProvider, chainConfig, radfiConfig, walletMode = "USER", rpcUrl) {
21809
+ super(chainConfig, radfiConfig, walletMode, rpcUrl);
21810
+ this.walletProvider = walletProvider;
21811
+ }
21812
+ /**
21813
+ * Authenticate with Radfi: BIP322-sign a login message, then call the Radfi API.
21814
+ * Returns accessToken, refreshToken, and tradingAddress.
21815
+ */
21816
+ async authenticateWithWallet(cachedPublicKey) {
21817
+ const address = await this.walletProvider.getWalletAddress();
21818
+ let publicKey = cachedPublicKey;
21819
+ if (!publicKey) {
21820
+ if (!this.walletProvider.getPublicKey) {
21821
+ throw new Error("Wallet provider does not support getPublicKey");
21822
+ }
21823
+ publicKey = await this.walletProvider.getPublicKey();
21824
+ }
21825
+ if (!publicKey) {
21826
+ throw new Error("Failed to retrieve public key from wallet. Please unlock your wallet and try again.");
21827
+ }
21828
+ const message = `Login to Radfi via Sodax: ${Date.now()}`;
21829
+ const signature = await this.walletProvider.signBip322Message(message);
21830
+ const result = await this.radfi.authenticate({ message, signature, address, publicKey });
21831
+ this.setRadfiAccessToken(result.accessToken);
21832
+ return { ...result, publicKey };
21833
+ }
21834
+ /**
21835
+ * Ensure a valid Radfi access token is set on this provider.
21836
+ * No-op if a token is already present.
21837
+ */
21838
+ async ensureRadfiAccessToken() {
21839
+ if (this.radfiAccessToken) return;
21840
+ await this.authenticateWithWallet();
21841
+ }
21842
+ /**
21843
+ * Sign and broadcast a Bitcoin transaction
21844
+ */
21845
+ async signAndBroadcastTransaction(psbt) {
21846
+ const psbtBase64 = typeof psbt === "string" ? psbt : psbt.toBase64();
21847
+ const signedPsbtHex = await this.walletProvider.signTransaction(psbtBase64);
21848
+ const txHash = await this.broadcastTransaction(signedPsbtHex);
21849
+ return txHash;
21850
+ }
21851
+ /**
21852
+ * Broadcast a signed transaction
21853
+ */
21854
+ async broadcastTransaction(txHex) {
21855
+ const response = await fetch(`${this.rpcUrl}/tx`, {
21856
+ method: "POST",
21857
+ body: txHex
21858
+ });
21859
+ if (!response.ok) {
21860
+ const errorText = await response.text();
21861
+ throw new Error(`Failed to broadcast transaction: ${errorText}`);
21862
+ }
21863
+ return await response.text();
21864
+ }
21865
+ };
21866
+
21867
+ // src/shared/services/spoke/BitcoinSpokeService.ts
21868
+ var BitcoinSpokeService = class _BitcoinSpokeService {
21869
+ constructor() {
21870
+ }
21871
+ /**
21872
+ * Estimate transaction fee for a Bitcoin transaction
21873
+ *
21874
+ * @param {Hex} rawTx - The raw transaction parameters
21875
+ * @param {BitcoinSpokeProviderType} spokeProvider - The Bitcoin spoke provider
21876
+ * @returns {Promise<bigint>} Estimated fee in satoshis
21877
+ */
21878
+ static async estimateGas(rawTx, spokeProvider) {
21879
+ const txBytes = Buffer.from(rawTx, "hex");
21880
+ const vsize = Math.ceil(txBytes.length);
21881
+ const feeRate = await spokeProvider.getFeeEstimate();
21882
+ const feeRateBigInt = typeof feeRate === "bigint" ? feeRate : BigInt(Math.ceil(feeRate));
21883
+ return BigInt(vsize) * feeRateBigInt;
21884
+ }
21885
+ /**
21886
+ * Deposit tokens to the spoke chain and bridge to hub
21887
+ *
21888
+ * @param {BitcoinSpokeDepositParams} params - Deposit parameters
21889
+ * @param {BitcoinSpokeProviderType} spokeProvider - The Bitcoin spoke provider
21890
+ * @param {EvmHubProvider} EvmHubProvider - The hub chain provider
21891
+ * @param {boolean} raw - Whether to return raw PSBT or transaction hash
21892
+ * @returns {Promise<TxReturnType<BitcoinSpokeProviderType, R>>} Transaction hash or raw PSBT
21893
+ */
21894
+ static async deposit(params, spokeProvider, raw) {
21895
+ return _BitcoinSpokeService.transfer(
21896
+ {
21897
+ token: params.token,
21898
+ amount: params.amount,
21899
+ data: params.data ?? "0x",
21900
+ accessToken: params.accessToken
21901
+ },
21902
+ spokeProvider,
21903
+ raw
21904
+ );
21905
+ }
21906
+ /**
21907
+ * Get the balance of deposited tokens in the asset manager
21908
+ *
21909
+ * @param {string} token - Token identifier ('BTC' for native Bitcoin)
21910
+ * @param {BitcoinSpokeProviderType} spokeProvider - The Bitcoin spoke provider
21911
+ * @returns {Promise<bigint>} Balance in satoshis
21912
+ */
21913
+ static async getDeposit(token, spokeProvider) {
21914
+ const assetManagerAddress = spokeProvider.chainConfig.addresses.assetManager;
21915
+ const utxos = await spokeProvider.fetchUTXOs(assetManagerAddress);
21916
+ const totalBalance = utxos.reduce((sum, utxo) => sum + utxo.value, 0);
21917
+ return BigInt(totalBalance);
21918
+ }
21919
+ /**
21920
+ * Generate simulation parameters for deposit
21921
+ *
21922
+ * @param {BitcoinSpokeDepositParams} params - Deposit parameters
21923
+ * @param {BitcoinSpokeProviderType} spokeProvider - The Bitcoin spoke provider
21924
+ * @param {EvmHubProvider} EvmHubProvider - The hub chain provider
21925
+ * @returns {Promise<DepositSimulationParams>} Simulation parameters
21926
+ */
21927
+ static async getSimulateDepositParams(params, spokeProvider, EvmHubProvider2) {
21928
+ const to = params.to ?? await EvmWalletAbstraction.getUserHubWalletAddress(
21929
+ spokeProvider.chainConfig.chain.id,
21930
+ encodeAddress(spokeProvider.chainConfig.chain.id, params.from),
21931
+ EvmHubProvider2
21932
+ );
21933
+ const tokenEntry = Object.values(spokeProvider.chainConfig.supportedTokens).find((t) => t.address === params.token);
21934
+ const token = tokenEntry?.address ?? params.token;
21935
+ return {
21936
+ spokeChainID: spokeProvider.chainConfig.chain.id,
21937
+ token: encodeAddress(spokeProvider.chainConfig.chain.id, token),
21938
+ from: encodeAddress(spokeProvider.chainConfig.chain.id, params.from),
21939
+ to,
21940
+ amount: params.amount,
21941
+ data: params.data,
21942
+ srcAddress: encodeAddress(spokeProvider.chainConfig.chain.id, spokeProvider.chainConfig.addresses.assetManager)
21943
+ };
21944
+ }
21945
+ /**
21946
+ * Fund the Radfi trading wallet by sending BTC from the user's personal wallet
21947
+ *
21948
+ * @param {bigint} amount - Amount in satoshis to send
21949
+ * @param {BitcoinSpokeProvider} spokeProvider - The Bitcoin spoke provider (must have signing capability)
21950
+ * @returns {Promise<string>} Transaction ID of the funding transaction
21951
+ */
21952
+ static async fundTradingWallet(amount, spokeProvider) {
21953
+ const walletAddress = await spokeProvider.walletProvider.getWalletAddress();
21954
+ const { tradingAddress } = await spokeProvider.radfi.getTradingWallet(walletAddress);
21955
+ return spokeProvider.walletProvider.sendBitcoin(tradingAddress, amount);
21956
+ }
21957
+ /**
21958
+ * Call a contract on the hub chain from Bitcoin spoke
21959
+ *
21960
+ * @param {HubAddress} from - The hub wallet address
21961
+ * @param {Hex} payload - The payload to send
21962
+ * @param {BitcoinSpokeProviderType} spokeProvider - The Bitcoin spoke provider
21963
+ * @param {EvmHubProvider} EvmHubProvider - The hub chain provider
21964
+ * @param {boolean} raw - Whether to return raw PSBT or transaction hash
21965
+ * @returns {Promise<TxReturnType<BitcoinSpokeProviderType, R>>} Stringified JSON for payload and signature
21966
+ */
21967
+ static async callWallet(from, payload, spokeProvider, EvmHubProvider2, raw) {
21968
+ return _BitcoinSpokeService.call(EvmHubProvider2.chainConfig.chain.id, from, payload, spokeProvider, raw);
21969
+ }
21970
+ /**
21971
+ * Transfer tokens to the hub chain
21972
+ *
21973
+ * @param {BitcoinTransferToHubParams} params - Transfer parameters
21974
+ * @param {BitcoinSpokeProviderType} spokeProvider - The Bitcoin spoke provider
21975
+ * @param {boolean} raw - Whether to return raw PSBT or transaction hash
21976
+ * @returns {Promise<TxReturnType<BitcoinSpokeProviderType, R>>} Transaction hash or raw PSBT
21977
+ */
21978
+ static async transfer({ token, amount, data = "0x", accessToken }, spokeProvider, raw) {
21979
+ return await BitcoinBaseSpokeProvider.deposit(token, amount, data, spokeProvider, raw, accessToken);
21980
+ }
21981
+ /**
21982
+ * Send a message to the hub chain
21983
+ *
21984
+ * @param {HubChainId} dstChainId - Destination chain ID
21985
+ * @param {HubAddress} dstAddress - Destination address on hub
21986
+ * @param {Hex} payload - Message payload
21987
+ * @param {BitcoinSpokeProviderType} spokeProvider - The Bitcoin spoke provider
21988
+ * @param {boolean} raw - Whether to return raw PSBT or transaction hash
21989
+ * @returns {Promise<TxReturnType<BitcoinSpokeProviderType, R>>} Transaction hash or raw PSBT
21990
+ */
21991
+ static async call(dstChainId, dstAddress, payload, spokeProvider, raw) {
21992
+ return await BitcoinBaseSpokeProvider.encodeWithdrawalData(
21993
+ dstChainId,
21994
+ payload,
21995
+ spokeProvider,
21996
+ raw
21997
+ );
21998
+ }
21999
+ };
21209
22000
  var NearSpokeService = class _NearSpokeService {
21210
22001
  constructor() {
21211
22002
  }
@@ -21564,6 +22355,14 @@ var SpokeService = class _SpokeService {
21564
22355
  raw
21565
22356
  );
21566
22357
  }
22358
+ if (isBitcoinSpokeProviderType(spokeProvider)) {
22359
+ await _SpokeService.verifyDepositSimulation(params, spokeProvider, hubProvider, skipSimulation);
22360
+ return BitcoinSpokeService.deposit(
22361
+ params,
22362
+ spokeProvider,
22363
+ raw
22364
+ );
22365
+ }
21567
22366
  if (isNearSpokeProviderType(spokeProvider)) {
21568
22367
  await _SpokeService.verifyDepositSimulation(params, spokeProvider, hubProvider, skipSimulation);
21569
22368
  return NearSpokeService.deposit(
@@ -21618,6 +22417,13 @@ var SpokeService = class _SpokeService {
21618
22417
  hubProvider
21619
22418
  );
21620
22419
  }
22420
+ if (isBitcoinSpokeProviderType(spokeProvider)) {
22421
+ return BitcoinSpokeService.getSimulateDepositParams(
22422
+ params,
22423
+ spokeProvider,
22424
+ hubProvider
22425
+ );
22426
+ }
21621
22427
  if (isNearSpokeProviderType(spokeProvider)) {
21622
22428
  return NearSpokeService.getSimulateDepositParams(
21623
22429
  params,
@@ -21664,6 +22470,9 @@ var SpokeService = class _SpokeService {
21664
22470
  if (isSonicSpokeProviderType(spokeProvider)) {
21665
22471
  return SonicSpokeService.getDeposit(token, spokeProvider);
21666
22472
  }
22473
+ if (isBitcoinSpokeProviderType(spokeProvider)) {
22474
+ return BitcoinSpokeService.getDeposit(token, spokeProvider);
22475
+ }
21667
22476
  if (isNearSpokeProviderType(spokeProvider)) {
21668
22477
  return NearSpokeService.getDeposit(token, spokeProvider);
21669
22478
  }
@@ -21681,15 +22490,24 @@ var SpokeService = class _SpokeService {
21681
22490
  if (isSonicSpokeProviderType(spokeProvider)) {
21682
22491
  return await SonicSpokeService.callWallet(payload, spokeProvider, raw);
21683
22492
  }
22493
+ let srcAddress = encodeAddress(
22494
+ spokeProvider.chainConfig.chain.id,
22495
+ await spokeProvider.walletProvider.getWalletAddress()
22496
+ );
22497
+ if (isBitcoinSpokeProvider(spokeProvider)) {
22498
+ if (spokeProvider.walletMode === "TRADING") {
22499
+ const tradingWalletAddress = await spokeProvider.radfi.getTradingWallet(
22500
+ await spokeProvider.walletProvider.getWalletAddress()
22501
+ );
22502
+ srcAddress = encodeAddress(spokeProvider.chainConfig.chain.id, tradingWalletAddress.tradingAddress);
22503
+ }
22504
+ }
21684
22505
  if (!skipSimulation) {
21685
22506
  const result = await _SpokeService.simulateRecvMessage(
21686
22507
  {
21687
22508
  target: from,
21688
22509
  srcChainId: getIntentRelayChainId(spokeProvider.chainConfig.chain.id),
21689
- srcAddress: encodeAddress(
21690
- spokeProvider.chainConfig.chain.id,
21691
- await spokeProvider.walletProvider.getWalletAddress()
21692
- ),
22510
+ srcAddress,
21693
22511
  payload
21694
22512
  },
21695
22513
  hubProvider
@@ -21740,6 +22558,16 @@ var SpokeService = class _SpokeService {
21740
22558
  raw
21741
22559
  );
21742
22560
  }
22561
+ if (isBitcoinSpokeProviderType(spokeProvider)) {
22562
+ await _SpokeService.verifySimulation(from, payload, spokeProvider, hubProvider, skipSimulation);
22563
+ return await BitcoinSpokeService.callWallet(
22564
+ from,
22565
+ payload,
22566
+ spokeProvider,
22567
+ hubProvider,
22568
+ raw
22569
+ );
22570
+ }
21743
22571
  if (isNearSpokeProviderType(spokeProvider)) {
21744
22572
  await _SpokeService.verifySimulation(from, payload, spokeProvider, hubProvider, skipSimulation);
21745
22573
  return await NearSpokeService.callWallet(from, payload, spokeProvider, hubProvider, raw);
@@ -21748,14 +22576,26 @@ var SpokeService = class _SpokeService {
21748
22576
  }
21749
22577
  static async verifySimulation(from, payload, spokeProvider, hubProvider, skipSimulation) {
21750
22578
  if (!skipSimulation) {
22579
+ let srcAddress = encodeAddress(
22580
+ spokeProvider.chainConfig.chain.id,
22581
+ await spokeProvider.walletProvider.getWalletAddress()
22582
+ );
22583
+ if (isBitcoinSpokeProvider(spokeProvider)) {
22584
+ if (spokeProvider.walletMode === "TRADING") {
22585
+ const tradingWalletAddress = await spokeProvider.radfi.getTradingWallet(
22586
+ await spokeProvider.walletProvider.getWalletAddress()
22587
+ );
22588
+ srcAddress = encodeAddress(
22589
+ spokeProvider.chainConfig.chain.id,
22590
+ tradingWalletAddress.tradingAddress
22591
+ );
22592
+ }
22593
+ }
21751
22594
  const result = await _SpokeService.simulateRecvMessage(
21752
22595
  {
21753
22596
  target: from,
21754
22597
  srcChainId: getIntentRelayChainId(spokeProvider.chainConfig.chain.id),
21755
- srcAddress: encodeAddress(
21756
- spokeProvider.chainConfig.chain.id,
21757
- await spokeProvider.walletProvider.getWalletAddress()
21758
- ),
22598
+ srcAddress,
21759
22599
  payload
21760
22600
  },
21761
22601
  hubProvider
@@ -22189,7 +23029,7 @@ var SwapService = class {
22189
23029
  let dstIntentTxHash;
22190
23030
  if (spokeProvider.chainConfig.chain.id !== this.hubProvider.chainConfig.chain.id) {
22191
23031
  const intentRelayChainId = getIntentRelayChainId(params.srcChain).toString();
22192
- const submitPayload = params.srcChain === SOLANA_MAINNET_CHAIN_ID && data ? {
23032
+ const submitPayload = (params.srcChain === SOLANA_MAINNET_CHAIN_ID || params.srcChain === BITCOIN_MAINNET_CHAIN_ID) && data ? {
22193
23033
  action: "submit",
22194
23034
  params: {
22195
23035
  chain_id: intentRelayChainId,
@@ -22514,17 +23354,51 @@ var SwapService = class {
22514
23354
  this.configService.isValidSpokeChainId(params.dstChain),
22515
23355
  `Invalid spoke chain (params.dstChain): ${params.dstChain}`
22516
23356
  );
23357
+ if (params.dstChain === BITCOIN_MAINNET_CHAIN_ID && params.outputToken === "BTC") {
23358
+ invariant6__default.default(
23359
+ params.minOutputAmount >= 546n,
23360
+ `Invalid minOutputAmount (params.minOutputAmount): ${params.minOutputAmount}`
23361
+ );
23362
+ }
22517
23363
  try {
22518
- const walletAddress = await spokeProvider.walletProvider.getWalletAddress();
23364
+ console.log("[SwapService.createIntent] start", {
23365
+ srcChain: params.srcChain,
23366
+ dstChain: params.dstChain,
23367
+ inputToken: params.inputToken,
23368
+ inputAmount: params.inputAmount.toString()
23369
+ });
23370
+ let walletAddress = await spokeProvider.walletProvider.getWalletAddress();
23371
+ console.log("[SwapService.createIntent] walletAddress", walletAddress, "srcAddress", params.srcAddress);
22519
23372
  invariant6__default.default(
22520
23373
  params.srcAddress.toLowerCase() === walletAddress.toLowerCase(),
22521
23374
  "srcAddress must be the same as wallet address"
22522
23375
  );
23376
+ if (isBitcoinSpokeProvider(spokeProvider)) {
23377
+ console.log(
23378
+ "[SwapService.createIntent] Bitcoin detected, walletMode:",
23379
+ spokeProvider.walletMode,
23380
+ "hasToken:",
23381
+ !!spokeProvider.radfiAccessToken
23382
+ );
23383
+ await spokeProvider.ensureRadfiAccessToken();
23384
+ console.log(
23385
+ "[SwapService.createIntent] ensureRadfiAccessToken done, hasToken:",
23386
+ !!spokeProvider.radfiAccessToken
23387
+ );
23388
+ if (spokeProvider.walletMode === "TRADING") {
23389
+ const tradingWalletAddress = await spokeProvider.radfi.getTradingWallet(
23390
+ await spokeProvider.walletProvider.getWalletAddress()
23391
+ );
23392
+ console.log("[SwapService.createIntent] tradingWalletAddress", tradingWalletAddress);
23393
+ walletAddress = tradingWalletAddress.tradingAddress;
23394
+ }
23395
+ }
22523
23396
  const creatorHubWalletAddress = await deriveUserWalletAddress(
22524
23397
  this.hubProvider,
22525
23398
  spokeProvider.chainConfig.chain.id,
22526
23399
  walletAddress
22527
23400
  );
23401
+ console.log("[SwapService.createIntent] creatorHubWalletAddress", creatorHubWalletAddress);
22528
23402
  if (spokeProvider.chainConfig.chain.id === this.hubProvider.chainConfig.chain.id && isSonicSpokeProviderType(spokeProvider)) {
22529
23403
  const [txResult, intent, feeAmount, data] = await SonicSpokeService.createSwapIntent(
22530
23404
  params,
@@ -22555,6 +23429,11 @@ var SwapService = class {
22555
23429
  this.configService,
22556
23430
  fee
22557
23431
  );
23432
+ console.log("[SwapService.createIntent] intent data constructed", {
23433
+ data,
23434
+ intentId: intent.intentId?.toString()
23435
+ });
23436
+ console.log("[SwapService.createIntent] calling SpokeService.deposit...");
22558
23437
  const txResult = await SpokeService.deposit(
22559
23438
  {
22560
23439
  from: walletAddress,
@@ -22568,12 +23447,14 @@ var SwapService = class {
22568
23447
  raw,
22569
23448
  skipSimulation
22570
23449
  );
23450
+ console.log("[SwapService.createIntent] SpokeService.deposit done, txResult:", txResult);
22571
23451
  return {
22572
23452
  ok: true,
22573
23453
  value: [txResult, { ...intent, feeAmount }, data]
22574
23454
  };
22575
23455
  }
22576
23456
  } catch (error) {
23457
+ console.error("[SwapService.createIntent] FAILED", error);
22577
23458
  return {
22578
23459
  ok: false,
22579
23460
  error: {
@@ -25632,9 +26513,15 @@ function isNearSpokeProvider(value) {
25632
26513
  function isStellarSpokeProviderType(value) {
25633
26514
  return typeof value === "object" && value !== null && (isStellarSpokeProvider(value) || isStellarRawSpokeProvider(value));
25634
26515
  }
26516
+ function isBitcoinSpokeProviderType(value) {
26517
+ return typeof value === "object" && value !== null && (isBitcoinSpokeProvider(value) || isBitcoinRawSpokeProvider(value));
26518
+ }
25635
26519
  function isStellarSpokeProvider(value) {
25636
26520
  return typeof value === "object" && value !== null && value instanceof StellarSpokeProvider && !("raw" in value) && value.chainConfig.chain.type === "STELLAR";
25637
26521
  }
26522
+ function isBitcoinSpokeProvider(value) {
26523
+ return typeof value === "object" && value !== null && value instanceof BitcoinSpokeProvider && !("raw" in value) && value.chainConfig.chain.type === "BITCOIN";
26524
+ }
25638
26525
  function isNearSpokeProviderType(value) {
25639
26526
  return typeof value === "object" && value !== null && (isNearSpokeProvider(value) || isNearRawSpokeProvider(value));
25640
26527
  }
@@ -25731,6 +26618,9 @@ function isSolanaRawSpokeProvider(value) {
25731
26618
  function isStellarRawSpokeProvider(value) {
25732
26619
  return isRawSpokeProvider(value) && value.chainConfig.chain.type === "STELLAR";
25733
26620
  }
26621
+ function isBitcoinRawSpokeProvider(value) {
26622
+ return isRawSpokeProvider(value) && value.chainConfig.chain.type === "BITCOIN";
26623
+ }
25734
26624
  function isIconRawSpokeProvider(value) {
25735
26625
  return isRawSpokeProvider(value) && value.chainConfig.chain.type === "ICON";
25736
26626
  }
@@ -25764,6 +26654,26 @@ function isSolanaRawSpokeProviderConfig(value) {
25764
26654
  function isNearRawSpokeProviderConfig(value) {
25765
26655
  return typeof value === "object" && value !== null && "walletAddress" in value && "chainConfig" in value && value.chainConfig.chain.type === "NEAR";
25766
26656
  }
26657
+ function isSubmitSwapTxResponse(value) {
26658
+ return typeof value === "object" && value !== null && typeof value.success === "boolean" && typeof value.message === "string";
26659
+ }
26660
+ function isSubmitSwapTxStatusResponse(value) {
26661
+ if (typeof value !== "object" || value === null) return false;
26662
+ const obj = value;
26663
+ if (typeof obj.success !== "boolean") return false;
26664
+ if (typeof obj.data !== "object" || obj.data === null) return false;
26665
+ const data = obj.data;
26666
+ if (typeof data.txHash !== "string") return false;
26667
+ if (typeof data.srcChainId !== "string") return false;
26668
+ if (typeof data.status !== "string") return false;
26669
+ if (typeof data.failedAttempts !== "number") return false;
26670
+ if (data.result !== void 0) {
26671
+ if (typeof data.result !== "object" || data.result === null) return false;
26672
+ const result = data.result;
26673
+ if (typeof result.dstIntentTxHash !== "string") return false;
26674
+ }
26675
+ return true;
26676
+ }
25767
26677
  async function retry(action, retryCount = DEFAULT_MAX_RETRY, delayMs = DEFAULT_RETRY_DELAY_MS) {
25768
26678
  do {
25769
26679
  try {
@@ -25839,8 +26749,6 @@ function encodeAddress(spokeChainId, address) {
25839
26749
  case "0xa4b1.arbitrum":
25840
26750
  case "sonic":
25841
26751
  return address;
25842
- case "injective-1":
25843
- return viem.toHex(Buffer.from(address, "utf-8"));
25844
26752
  case "0x1.icon":
25845
26753
  return viem.toHex(Buffer.from(address.replace("cx", "01").replace("hx", "00") ?? "f8", "hex"));
25846
26754
  case "sui":
@@ -25849,7 +26757,9 @@ function encodeAddress(spokeChainId, address) {
25849
26757
  return viem.toHex(Buffer.from(new web3_js.PublicKey(address).toBytes()));
25850
26758
  case "stellar":
25851
26759
  return `0x${stellarSdk.Address.fromString(address).toScVal().toXDR("hex")}`;
26760
+ case "bitcoin":
25852
26761
  case "near":
26762
+ case "injective-1":
25853
26763
  return viem.toHex(Buffer.from(address, "utf-8"));
25854
26764
  default:
25855
26765
  return address;
@@ -25975,10 +26885,11 @@ var BackendApiService = class {
25975
26885
  * @returns Promise<T>
25976
26886
  */
25977
26887
  async makeRequest(endpoint, config) {
25978
- const url = `${this.baseURL}${endpoint}`;
26888
+ const url = config.baseURL ? `${config.baseURL}${endpoint}` : `${this.baseURL}${endpoint}`;
25979
26889
  const headers = { ...this.defaultHeaders, ...config.headers };
25980
26890
  const controller = new AbortController();
25981
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
26891
+ const timeout = config.timeout ?? this.timeout;
26892
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
25982
26893
  try {
25983
26894
  const response = await fetch(url, {
25984
26895
  method: config.method,
@@ -25997,7 +26908,7 @@ var BackendApiService = class {
25997
26908
  clearTimeout(timeoutId);
25998
26909
  if (error instanceof Error) {
25999
26910
  if (error.name === "AbortError") {
26000
- throw new Error(`Request timeout after ${this.timeout}ms`);
26911
+ throw new Error(`Request timeout after ${timeout}ms`);
26001
26912
  }
26002
26913
  console.error("[BackendApiService] Request error:", error.message);
26003
26914
  throw error;
@@ -26013,16 +26924,50 @@ var BackendApiService = class {
26013
26924
  * @param txHash - The intent created transaction hash from the hub chain
26014
26925
  * @returns Promise<IntentResponse>
26015
26926
  */
26016
- async getIntentByTxHash(txHash) {
26017
- return this.makeRequest(`/intent/tx/${txHash}`, { method: "GET" });
26927
+ async getIntentByTxHash(txHash, config) {
26928
+ return this.makeRequest(`/intent/tx/${txHash}`, { ...config, method: "GET" });
26018
26929
  }
26019
26930
  /**
26020
26931
  * Get intent details by intent hash
26021
26932
  * @param intentHash - Intent hash
26022
26933
  * @returns Promise<IntentResponse>
26023
26934
  */
26024
- async getIntentByHash(intentHash) {
26025
- return this.makeRequest(`/intent/${intentHash}`, { method: "GET" });
26935
+ async getIntentByHash(intentHash, config) {
26936
+ return this.makeRequest(`/intent/${intentHash}`, { ...config, method: "GET" });
26937
+ }
26938
+ // Swap submit-tx endpoints
26939
+ /**
26940
+ * Submit a swap transaction to be processed (relay, post execution to solver, etc.)
26941
+ * @param params - Swap transaction submission data
26942
+ * @returns Promise<SubmitSwapTxResponse>
26943
+ */
26944
+ async submitSwapTx(params, config) {
26945
+ const data = await this.makeRequest("/swaps/submit-tx", {
26946
+ ...config,
26947
+ method: "POST",
26948
+ body: JSON.stringify(params)
26949
+ });
26950
+ if (!isSubmitSwapTxResponse(data)) {
26951
+ throw new Error("Invalid submitSwapTx response: unexpected response shape");
26952
+ }
26953
+ return data;
26954
+ }
26955
+ /**
26956
+ * Get the processing status of a submitted swap transaction
26957
+ * @param params - Query parameters containing txHash and optional srcChainId
26958
+ * @returns Promise<SubmitSwapTxStatusResponse>
26959
+ */
26960
+ async getSubmitSwapTxStatus(params, config) {
26961
+ const queryParams = new URLSearchParams();
26962
+ queryParams.append("txHash", params.txHash);
26963
+ if (params.srcChainId) queryParams.append("srcChainId", params.srcChainId);
26964
+ const queryString = queryParams.toString();
26965
+ const endpoint = `/swaps/submit-tx/status?${queryString}`;
26966
+ const data = await this.makeRequest(endpoint, { ...config, method: "GET" });
26967
+ if (!isSubmitSwapTxStatusResponse(data)) {
26968
+ throw new Error("Invalid submitSwapTxStatus response: unexpected response shape");
26969
+ }
26970
+ return data;
26026
26971
  }
26027
26972
  // Solver endpoints
26028
26973
  /**
@@ -26030,13 +26975,13 @@ var BackendApiService = class {
26030
26975
  * @param params - Object containing offset and limit parameters for pagination
26031
26976
  * @returns Promise<OrderbookResponse>
26032
26977
  */
26033
- async getOrderbook(params) {
26978
+ async getOrderbook(params, config) {
26034
26979
  const queryParams = new URLSearchParams();
26035
26980
  queryParams.append("offset", params.offset);
26036
26981
  queryParams.append("limit", params.limit);
26037
26982
  const queryString = queryParams.toString();
26038
26983
  const endpoint = `/solver/orderbook?${queryString}`;
26039
- return this.makeRequest(endpoint, { method: "GET" });
26984
+ return this.makeRequest(endpoint, { ...config, method: "GET" });
26040
26985
  }
26041
26986
  /**
26042
26987
  * Get all intents created by a specific user address with optional filters.
@@ -26050,7 +26995,7 @@ var BackendApiService = class {
26050
26995
  *
26051
26996
  * @returns {Promise<UserIntentsResponse>} Promise resolving to an array of intent responses for the user.
26052
26997
  */
26053
- async getUserIntents(params) {
26998
+ async getUserIntents(params, config) {
26054
26999
  const { userAddress, startDate, endDate, limit, offset } = params;
26055
27000
  const queryParams = new URLSearchParams();
26056
27001
  if (startDate) queryParams.append("startDate", new Date(startDate).toISOString());
@@ -26059,7 +27004,7 @@ var BackendApiService = class {
26059
27004
  if (offset) queryParams.append("offset", offset);
26060
27005
  const queryString = queryParams.toString();
26061
27006
  const endpoint = queryString.length > 0 ? `/intent/user/${userAddress}?${queryString}` : `/intent/user/${userAddress}`;
26062
- return this.makeRequest(endpoint, { method: "GET" });
27007
+ return this.makeRequest(endpoint, { ...config, method: "GET" });
26063
27008
  }
26064
27009
  // Money Market endpoints
26065
27010
  /**
@@ -26067,23 +27012,23 @@ var BackendApiService = class {
26067
27012
  * @param userAddress - User's wallet address
26068
27013
  * @returns Promise<MoneyMarketPosition>
26069
27014
  */
26070
- async getMoneyMarketPosition(userAddress) {
26071
- return this.makeRequest(`/moneymarket/position/${userAddress}`, { method: "GET" });
27015
+ async getMoneyMarketPosition(userAddress, config) {
27016
+ return this.makeRequest(`/moneymarket/position/${userAddress}`, { ...config, method: "GET" });
26072
27017
  }
26073
27018
  /**
26074
27019
  * Get all money market assets
26075
27020
  * @returns Promise<MoneyMarketAsset[]>
26076
27021
  */
26077
- async getAllMoneyMarketAssets() {
26078
- return this.makeRequest("/moneymarket/asset/all", { method: "GET" });
27022
+ async getAllMoneyMarketAssets(config) {
27023
+ return this.makeRequest("/moneymarket/asset/all", { ...config, method: "GET" });
26079
27024
  }
26080
27025
  /**
26081
27026
  * Get specific money market asset details
26082
27027
  * @param reserveAddress - Reserve contract address
26083
27028
  * @returns Promise<MoneyMarketAsset>
26084
27029
  */
26085
- async getMoneyMarketAsset(reserveAddress) {
26086
- return this.makeRequest(`/moneymarket/asset/${reserveAddress}`, { method: "GET" });
27030
+ async getMoneyMarketAsset(reserveAddress, config) {
27031
+ return this.makeRequest(`/moneymarket/asset/${reserveAddress}`, { ...config, method: "GET" });
26087
27032
  }
26088
27033
  /**
26089
27034
  * Get borrowers for a specific money market asset
@@ -26091,13 +27036,13 @@ var BackendApiService = class {
26091
27036
  * @param params - Object containing offset and limit parameters for pagination
26092
27037
  * @returns Promise<MoneyMarketAssetBorrowers>
26093
27038
  */
26094
- async getMoneyMarketAssetBorrowers(reserveAddress, params) {
27039
+ async getMoneyMarketAssetBorrowers(reserveAddress, params, config) {
26095
27040
  const queryParams = new URLSearchParams();
26096
27041
  queryParams.append("offset", params.offset);
26097
27042
  queryParams.append("limit", params.limit);
26098
27043
  const queryString = queryParams.toString();
26099
27044
  const endpoint = `/moneymarket/asset/${reserveAddress}/borrowers?${queryString}`;
26100
- return this.makeRequest(endpoint, { method: "GET" });
27045
+ return this.makeRequest(endpoint, { ...config, method: "GET" });
26101
27046
  }
26102
27047
  /**
26103
27048
  * Get suppliers for a specific money market asset
@@ -26105,69 +27050,76 @@ var BackendApiService = class {
26105
27050
  * @param params - Object containing offset and limit parameters for pagination
26106
27051
  * @returns Promise<MoneyMarketAssetSuppliers>
26107
27052
  */
26108
- async getMoneyMarketAssetSuppliers(reserveAddress, params) {
27053
+ async getMoneyMarketAssetSuppliers(reserveAddress, params, config) {
26109
27054
  const queryParams = new URLSearchParams();
26110
27055
  queryParams.append("offset", params.offset);
26111
27056
  queryParams.append("limit", params.limit);
26112
27057
  const queryString = queryParams.toString();
26113
27058
  const endpoint = `/moneymarket/asset/${reserveAddress}/suppliers?${queryString}`;
26114
- return this.makeRequest(endpoint, { method: "GET" });
27059
+ return this.makeRequest(endpoint, { ...config, method: "GET" });
26115
27060
  }
26116
27061
  /**
26117
27062
  * Get all money market borrowers
26118
27063
  * @param params - Object containing offset and limit parameters for pagination
26119
27064
  * @returns Promise<MoneyMarketBorrowers>
26120
27065
  */
26121
- async getAllMoneyMarketBorrowers(params) {
27066
+ async getAllMoneyMarketBorrowers(params, config) {
26122
27067
  const queryParams = new URLSearchParams();
26123
27068
  queryParams.append("offset", params.offset);
26124
27069
  queryParams.append("limit", params.limit);
26125
27070
  const queryString = queryParams.toString();
26126
27071
  const endpoint = `/moneymarket/borrowers?${queryString}`;
26127
- return this.makeRequest(endpoint, { method: "GET" });
27072
+ return this.makeRequest(endpoint, { ...config, method: "GET" });
26128
27073
  }
26129
27074
  /**
26130
27075
  * Get all supported config
26131
27076
  * @returns Promise<GetAllConfigApiResponse>
26132
27077
  */
26133
- async getAllConfig() {
26134
- return this.makeRequest("/config/all", { method: "GET" });
27078
+ async getAllConfig(config) {
27079
+ return this.makeRequest("/config/all", { ...config, method: "GET" });
26135
27080
  }
26136
27081
  /**
26137
27082
  * Get all supported spoke chains
26138
27083
  * @returns Promise<GetChainsApiResponse>
26139
27084
  */
26140
- async getChains() {
26141
- return this.makeRequest("/config/spoke/chains", { method: "GET" });
27085
+ async getChains(config) {
27086
+ return this.makeRequest("/config/spoke/chains", { ...config, method: "GET" });
26142
27087
  }
26143
27088
  /**
26144
27089
  * Get all supported swap tokens
26145
27090
  * @returns Promise<GetSwapTokensApiResponse>
26146
27091
  */
26147
- async getSwapTokens() {
26148
- return this.makeRequest("/config/swap/tokens", { method: "GET" });
27092
+ async getSwapTokens(config) {
27093
+ return this.makeRequest("/config/swap/tokens", { ...config, method: "GET" });
26149
27094
  }
26150
27095
  /**
26151
27096
  * Get supported swap tokens for a specific spoke chain
26152
27097
  * @param chainId - Spoke chain id
26153
27098
  * @returns Promise<GetSwapTokensByChainIdApiResponse>
26154
27099
  */
26155
- async getSwapTokensByChainId(chainId) {
26156
- return this.makeRequest(`/config/swap/${chainId}/tokens`, { method: "GET" });
27100
+ async getSwapTokensByChainId(chainId, config) {
27101
+ return this.makeRequest(`/config/swap/${chainId}/tokens`, {
27102
+ ...config,
27103
+ method: "GET"
27104
+ });
26157
27105
  }
26158
27106
  /**
26159
27107
  * Get all supported money market tokens
26160
27108
  * @returns Promise<GetMoneyMarketTokensApiResponse>
26161
27109
  */
26162
- async getMoneyMarketTokens() {
26163
- return this.makeRequest("/config/money-market/tokens", { method: "GET" });
27110
+ async getMoneyMarketTokens(config) {
27111
+ return this.makeRequest("/config/money-market/tokens", {
27112
+ ...config,
27113
+ method: "GET"
27114
+ });
26164
27115
  }
26165
27116
  /**
26166
27117
  * Get all supported money market tokens
26167
27118
  * @returns Promise<GetMoneyMarketTokensApiResponse>
26168
27119
  */
26169
- async getMoneyMarketReserveAssets() {
27120
+ async getMoneyMarketReserveAssets(config) {
26170
27121
  return this.makeRequest("/config/money-market/reserve-assets", {
27122
+ ...config,
26171
27123
  method: "GET"
26172
27124
  });
26173
27125
  }
@@ -26176,8 +27128,9 @@ var BackendApiService = class {
26176
27128
  * @param chainId - Spoke chain id
26177
27129
  * @returns Promise<GetMoneyMarketTokensByChainIdApiResponse>
26178
27130
  */
26179
- async getMoneyMarketTokensByChainId(chainId) {
27131
+ async getMoneyMarketTokensByChainId(chainId, config) {
26180
27132
  return this.makeRequest(`/config/money-market/${chainId}/tokens`, {
27133
+ ...config,
26181
27134
  method: "GET"
26182
27135
  });
26183
27136
  }
@@ -26185,30 +27138,39 @@ var BackendApiService = class {
26185
27138
  * Get all supported hub assets (assets representing spoke token deposit)
26186
27139
  * @returns Promise<GetHubAssetsApiResponse>
26187
27140
  */
26188
- async getHubAssets() {
26189
- return this.makeRequest("/config/hub/assets", { method: "GET" });
27141
+ async getHubAssets(config) {
27142
+ return this.makeRequest("/config/hub/assets", { ...config, method: "GET" });
26190
27143
  }
26191
27144
  /**
26192
27145
  * Get supported hub assets (assets representing spoke token deposit) for a specific spoke chain
26193
27146
  * @param chainId - Spoke chain id
26194
27147
  * @returns Promise<GetHubAssetsByChainIdApiResponse>
26195
27148
  */
26196
- async getHubAssetsByChainId(chainId) {
26197
- return this.makeRequest(`/config/hub/${chainId}/assets`, { method: "GET" });
27149
+ async getHubAssetsByChainId(chainId, config) {
27150
+ return this.makeRequest(`/config/hub/${chainId}/assets`, {
27151
+ ...config,
27152
+ method: "GET"
27153
+ });
26198
27154
  }
26199
27155
  /**
26200
27156
  * Get the intent relay chain id map
26201
27157
  * @returns Promise<GetRelayChainIdMapApiResponse>
26202
27158
  */
26203
- async getRelayChainIdMap() {
26204
- return this.makeRequest("/config/relay/chain-id-map", { method: "GET" });
27159
+ async getRelayChainIdMap(config) {
27160
+ return this.makeRequest("/config/relay/chain-id-map", {
27161
+ ...config,
27162
+ method: "GET"
27163
+ });
26205
27164
  }
26206
27165
  /**
26207
27166
  * Get the spoke chain config
26208
27167
  * @returns Promise<GetSpokeChainConfigApiResponse>
26209
27168
  */
26210
- async getSpokeChainConfig() {
26211
- return this.makeRequest("/config/spoke/all-chains-configs", { method: "GET" });
27169
+ async getSpokeChainConfig(config) {
27170
+ return this.makeRequest("/config/spoke/all-chains-configs", {
27171
+ ...config,
27172
+ method: "GET"
27173
+ });
26212
27174
  }
26213
27175
  /**
26214
27176
  * Set custom headers for API requests
@@ -26473,7 +27435,7 @@ var BridgeService = class {
26473
27435
  }
26474
27436
  const packetResult = await relayTxAndWaitPacket(
26475
27437
  txResult.value,
26476
- spokeProvider instanceof SolanaSpokeProvider ? txResult.data : void 0,
27438
+ spokeProvider instanceof SolanaSpokeProvider || spokeProvider instanceof BitcoinSpokeProvider ? txResult.data : void 0,
26477
27439
  spokeProvider,
26478
27440
  this.relayerApiEndpoint,
26479
27441
  timeout
@@ -26546,7 +27508,11 @@ var BridgeService = class {
26546
27508
  const dstAssetInfo = this.configService.getHubAssetInfo(params.dstChainId, params.dstAsset);
26547
27509
  invariant6__default.default(srcAssetInfo, `Unsupported spoke chain (${params.srcChainId}) token: ${params.srcAsset}`);
26548
27510
  invariant6__default.default(dstAssetInfo, `Unsupported spoke chain (${params.dstChainId}) token: ${params.dstAsset}`);
26549
- const walletAddress = await spokeProvider.walletProvider.getWalletAddress();
27511
+ let walletAddress = await spokeProvider.walletProvider.getWalletAddress();
27512
+ if (spokeProvider instanceof BitcoinSpokeProvider && spokeProvider.walletMode === "TRADING") {
27513
+ const tradingWallet = await spokeProvider.radfi.getTradingWallet(walletAddress);
27514
+ walletAddress = tradingWallet.tradingAddress;
27515
+ }
26550
27516
  const hubWallet = await WalletAbstractionService.getUserAbstractedWalletAddress(
26551
27517
  walletAddress,
26552
27518
  spokeProvider,
@@ -30080,11 +31046,16 @@ var BalnSwapService = class {
30080
31046
  exports.ARBITRUM_MAINNET_CHAIN_ID = ARBITRUM_MAINNET_CHAIN_ID;
30081
31047
  exports.AVALANCHE_MAINNET_CHAIN_ID = AVALANCHE_MAINNET_CHAIN_ID;
30082
31048
  exports.BASE_MAINNET_CHAIN_ID = BASE_MAINNET_CHAIN_ID;
31049
+ exports.BITCOIN_MAINNET_CHAIN_ID = BITCOIN_MAINNET_CHAIN_ID;
30083
31050
  exports.BSC_MAINNET_CHAIN_ID = BSC_MAINNET_CHAIN_ID;
30084
31051
  exports.BackendApiService = BackendApiService;
30085
31052
  exports.BalnSwapService = BalnSwapService;
30086
31053
  exports.BigIntToHex = BigIntToHex;
30087
31054
  exports.BigNumberZeroDecimal = BigNumberZeroDecimal;
31055
+ exports.BitcoinBaseSpokeProvider = BitcoinBaseSpokeProvider;
31056
+ exports.BitcoinRawSpokeProvider = BitcoinRawSpokeProvider;
31057
+ exports.BitcoinSpokeProvider = BitcoinSpokeProvider;
31058
+ exports.BitcoinSpokeService = BitcoinSpokeService;
30088
31059
  exports.BnUSDMigrationService = BnUSDMigrationService;
30089
31060
  exports.BridgeService = BridgeService;
30090
31061
  exports.CHAIN_IDS = CHAIN_IDS;
@@ -30161,6 +31132,7 @@ exports.ProtocolIntentsAbi = ProtocolIntentsAbi;
30161
31132
  exports.RAY = RAY;
30162
31133
  exports.RAY_DECIMALS = RAY_DECIMALS;
30163
31134
  exports.REDBELLY_MAINNET_CHAIN_ID = REDBELLY_MAINNET_CHAIN_ID;
31135
+ exports.RadfiProvider = RadfiProvider;
30164
31136
  exports.SECONDS_PER_YEAR = SECONDS_PER_YEAR;
30165
31137
  exports.SOLANA_MAINNET_CHAIN_ID = SOLANA_MAINNET_CHAIN_ID;
30166
31138
  exports.SONIC_MAINNET_CHAIN_ID = SONIC_MAINNET_CHAIN_ID;
@@ -30226,6 +31198,7 @@ exports.convertTransactionInstructionToRaw = convertTransactionInstructionToRaw;
30226
31198
  exports.defaultSharedConfig = defaultSharedConfig;
30227
31199
  exports.defaultSodaxConfig = defaultSodaxConfig;
30228
31200
  exports.deriveUserWalletAddress = deriveUserWalletAddress;
31201
+ exports.detectBitcoinAddressType = detectBitcoinAddressType;
30229
31202
  exports.encodeAddress = encodeAddress;
30230
31203
  exports.encodeContractCalls = encodeContractCalls;
30231
31204
  exports.erc20Abi = erc20Abi;
@@ -30264,6 +31237,9 @@ exports.hubChainConfig = hubChainConfig;
30264
31237
  exports.hyper = hyper;
30265
31238
  exports.isAddressString = isAddressString;
30266
31239
  exports.isBalnMigrateParams = isBalnMigrateParams;
31240
+ exports.isBitcoinRawSpokeProvider = isBitcoinRawSpokeProvider;
31241
+ exports.isBitcoinSpokeProvider = isBitcoinSpokeProvider;
31242
+ exports.isBitcoinSpokeProviderType = isBitcoinSpokeProviderType;
30267
31243
  exports.isConfiguredMoneyMarketConfig = isConfiguredMoneyMarketConfig;
30268
31244
  exports.isConfiguredSolverConfig = isConfiguredSolverConfig;
30269
31245
  exports.isCreateIntentAutoSwapError = isCreateIntentAutoSwapError;
@@ -30331,6 +31307,8 @@ exports.isStellarRawSpokeProvider = isStellarRawSpokeProvider;
30331
31307
  exports.isStellarRawSpokeProviderConfig = isStellarRawSpokeProviderConfig;
30332
31308
  exports.isStellarSpokeProvider = isStellarSpokeProvider;
30333
31309
  exports.isStellarSpokeProviderType = isStellarSpokeProviderType;
31310
+ exports.isSubmitSwapTxResponse = isSubmitSwapTxResponse;
31311
+ exports.isSubmitSwapTxStatusResponse = isSubmitSwapTxStatusResponse;
30334
31312
  exports.isSuiRawSpokeProvider = isSuiRawSpokeProvider;
30335
31313
  exports.isSuiSpokeProvider = isSuiSpokeProvider;
30336
31314
  exports.isSuiSpokeProviderType = isSuiSpokeProviderType;
@@ -30345,6 +31323,7 @@ exports.nativeToUSD = nativeToUSD;
30345
31323
  exports.newbnUSDSpokeChainIds = newbnUSDSpokeChainIds;
30346
31324
  exports.normalize = normalize;
30347
31325
  exports.normalizeBN = normalizeBN;
31326
+ exports.normalizePsbtToBase64 = normalizePsbtToBase64;
30348
31327
  exports.normalizedToUsd = normalizedToUsd;
30349
31328
  exports.parseToStroops = parseToStroops;
30350
31329
  exports.parseTokenArrayFromJson = parseTokenArrayFromJson;