@hyperbridge/sdk 1.3.4 → 1.3.6

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.
@@ -3,7 +3,7 @@ import { join } from 'path';
3
3
  import { TextDecoder as TextDecoder$1, TextEncoder as TextEncoder$1 } from 'util';
4
4
  import { createConsola, LogLevels } from 'consola';
5
5
  import { flatten, zip, capitalize, maxBy, isNil } from 'lodash-es';
6
- import { toHex, hexToBytes, encodePacked, keccak256, encodeAbiParameters, bytesToHex, concatHex, createPublicClient, http, encodeFunctionData, erc20Abi, bytesToBigInt, pad, toBytes, maxUint256, parseUnits } from 'viem';
6
+ import { toHex, hexToBytes, encodePacked, keccak256, encodeAbiParameters, bytesToHex, concatHex, createPublicClient, http, encodeFunctionData, erc20Abi, bytesToBigInt, pad, toBytes, maxUint256, formatUnits, parseUnits } from 'viem';
7
7
  import mergeRace from '@async-generator/merge-race';
8
8
  import { gnosisChiado, gnosis, bscTestnet, bsc, soneium, baseSepolia, base, optimismSepolia, optimism, arbitrumSepolia, arbitrum, mainnet, sepolia } from 'viem/chains';
9
9
  import { hasWindow, isNode, env } from 'std-env';
@@ -13,6 +13,7 @@ import { WsProvider, ApiPromise } from '@polkadot/api';
13
13
  import { RpcWebSocketClient } from 'rpc-websocket-client';
14
14
  import { keccakAsU8a, decodeAddress, xxhashAsU8a } from '@polkadot/util-crypto';
15
15
  import { GraphQLClient } from 'graphql-request';
16
+ import { Decimal } from 'decimal.js';
16
17
  import { u8aToHex } from '@polkadot/util';
17
18
 
18
19
  var __defProp = Object.defineProperty;
@@ -3410,18 +3411,18 @@ var addresses = {
3410
3411
  ["EVM-10200" /* GNOSIS_CHIADO */]: "0x0000000000000000000000000000000000000000",
3411
3412
  ["EVM-11155111" /* SEPOLIA */]: "0x0000000000000000000000000000000000000000",
3412
3413
  ["EVM-1" /* MAINNET */]: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
3413
- ["EVM-56" /* BSC_MAINNET */]: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
3414
- ["EVM-42161" /* ARBITRUM_MAINNET */]: "0x0000000000000000000000000000000000000000",
3415
- ["EVM-8453" /* BASE_MAINNET */]: "0x0000000000000000000000000000000000000000"
3414
+ ["EVM-56" /* BSC_MAINNET */]: "0x10ED43C718714eb63d5aA57B78B54704E256024E",
3415
+ ["EVM-42161" /* ARBITRUM_MAINNET */]: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
3416
+ ["EVM-8453" /* BASE_MAINNET */]: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24"
3416
3417
  },
3417
3418
  UniswapV2Factory: {
3418
3419
  ["EVM-97" /* BSC_CHAPEL */]: "0x12e036669DA18F4A2777853d6e2136b32AceEC86",
3419
3420
  ["EVM-10200" /* GNOSIS_CHIADO */]: "0x0000000000000000000000000000000000000000",
3420
3421
  ["EVM-11155111" /* SEPOLIA */]: "0x0000000000000000000000000000000000000000",
3421
3422
  ["EVM-1" /* MAINNET */]: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f",
3422
- ["EVM-56" /* BSC_MAINNET */]: "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6",
3423
- ["EVM-42161" /* ARBITRUM_MAINNET */]: "0x0000000000000000000000000000000000000000",
3424
- ["EVM-8453" /* BASE_MAINNET */]: "0x0000000000000000000000000000000000000000"
3423
+ ["EVM-56" /* BSC_MAINNET */]: "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73",
3424
+ ["EVM-42161" /* ARBITRUM_MAINNET */]: "0xf1D7CC64Fb4452F05c498126312eBE29f30Fbcf9",
3425
+ ["EVM-8453" /* BASE_MAINNET */]: "0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6"
3425
3426
  },
3426
3427
  BatchExecutor: {
3427
3428
  ["EVM-97" /* BSC_CHAPEL */]: "0x4CC58B5D8FBf838d062E4b21F75C327835B5F0ef",
@@ -3933,6 +3934,10 @@ var GetRequest = Struct({
3933
3934
  * Substrate Keys
3934
3935
  */
3935
3936
  keys: Vector(Vector(u8)),
3937
+ /*
3938
+ * The height of the state machine
3939
+ */
3940
+ height: u64,
3936
3941
  /*
3937
3942
  * Some application-specific metadata relating to this request
3938
3943
  */
@@ -4059,6 +4064,24 @@ var TimeoutMessage = Enum({
4059
4064
  requests: Vector(Request)
4060
4065
  })
4061
4066
  });
4067
+ var GetRequestsWithProof = Struct({
4068
+ /*
4069
+ * Requests to be fetched
4070
+ */
4071
+ requests: Vector(GetRequest),
4072
+ /*
4073
+ * Membership batch proof for these requests
4074
+ */
4075
+ source: Proof,
4076
+ /*
4077
+ * Storage proof for these responses
4078
+ */
4079
+ response: Proof,
4080
+ /*
4081
+ * Signer information. Ideally should be their account identifier
4082
+ */
4083
+ signer: Vector(u8)
4084
+ });
4062
4085
  var Message = Enum({
4063
4086
  /*
4064
4087
  * A consensus update message
@@ -4807,6 +4830,8 @@ var EvmChain = class {
4807
4830
  ]
4808
4831
  });
4809
4832
  return encoded2;
4833
+ }).with({ kind: "GetRequest" }, (message2) => {
4834
+ throw new Error("GetResponse is not yet supported on Substrate chains");
4810
4835
  }).exhaustive();
4811
4836
  return encoded;
4812
4837
  }
@@ -5122,8 +5147,13 @@ var SubstrateChain = class {
5122
5147
  async submitUnsigned(message) {
5123
5148
  if (!this.api) throw new Error("API not initialized");
5124
5149
  const { api } = this;
5125
- const args = hexToBytes(this.encode(message)).slice(2);
5126
- const tx = api.tx.ismp.handleUnsigned(args);
5150
+ const args = encodeISMPMessage(message);
5151
+ let tx;
5152
+ if (message.kind === "GetRequest") {
5153
+ tx = api.tx.stateCoprocessor.handleUnsigned(args);
5154
+ } else {
5155
+ tx = api.tx.ismp.handleUnsigned(args);
5156
+ }
5127
5157
  return new Promise((resolve, reject) => {
5128
5158
  let unsub = () => {
5129
5159
  };
@@ -5266,6 +5296,18 @@ function convertIPostRequestToCodec(request) {
5266
5296
  }
5267
5297
  };
5268
5298
  }
5299
+ function convertIGetRequestToCodec(request) {
5300
+ return {
5301
+ source: convertStateMachineIdToEnum(request.source),
5302
+ dest: convertStateMachineIdToEnum(request.dest),
5303
+ from: Array.from(hexToBytes(request.from)),
5304
+ nonce: request.nonce,
5305
+ keys: request.keys.map((key) => Array.from(hexToBytes(key))),
5306
+ context: Array.from(hexToBytes(request.context)),
5307
+ timeoutTimestamp: request.timeoutTimestamp,
5308
+ height: request.height
5309
+ };
5310
+ }
5269
5311
  function encodeISMPMessage(message) {
5270
5312
  try {
5271
5313
  return match(message).with({ kind: "PostRequest" }, (message2) => {
@@ -5292,6 +5334,31 @@ function encodeISMPMessage(message) {
5292
5334
  ]);
5293
5335
  }).with({ kind: "GetResponse" }, (message2) => {
5294
5336
  throw new Error("GetResponse is not yet supported on Substrate chains");
5337
+ }).with({ kind: "GetRequest" }, (message2) => {
5338
+ return GetRequestsWithProof.enc({
5339
+ requests: message2.requests.map((request) => convertIGetRequestToCodec(request)),
5340
+ source: {
5341
+ height: {
5342
+ height: message2.source.height,
5343
+ id: {
5344
+ consensusStateId: Array.from(toBytes(message2.source.consensusStateId)),
5345
+ id: convertStateMachineIdToEnum(message2.source.stateMachine)
5346
+ }
5347
+ },
5348
+ proof: Array.from(hexToBytes(message2.source.proof))
5349
+ },
5350
+ response: {
5351
+ height: {
5352
+ height: message2.response.height,
5353
+ id: {
5354
+ consensusStateId: Array.from(toBytes(message2.response.consensusStateId)),
5355
+ id: convertStateMachineIdToEnum(message2.response.stateMachine)
5356
+ }
5357
+ },
5358
+ proof: Array.from(hexToBytes(message2.response.proof))
5359
+ },
5360
+ signer: Array.from(hexToBytes(message2.signer))
5361
+ });
5295
5362
  }).with({ kind: "TimeoutPostRequest" }, (message2) => {
5296
5363
  return Vector(Message).enc([
5297
5364
  {
@@ -5692,6 +5759,7 @@ async function _queryGetRequestInternal(params) {
5692
5759
  const { statusMetadata, ...rest } = response.getRequests.nodes[0];
5693
5760
  return {
5694
5761
  ...rest,
5762
+ commitment: commitmentHash,
5695
5763
  timeoutTimestamp: BigInt(rest.timeoutTimestamp),
5696
5764
  nonce: BigInt(rest.nonce),
5697
5765
  height: BigInt(rest.height),
@@ -6109,6 +6177,120 @@ var IndexerClient = class {
6109
6177
  );
6110
6178
  return request;
6111
6179
  }
6180
+ /**
6181
+ * Queries a GET request and returns it alongside its statuses,
6182
+ * including any finalization events.
6183
+ * @param hash - Can be commitment, hyperbridge tx hash, source tx hash, destination tx hash, or timeout tx hash
6184
+ * @returns Full GET request data with all inferred status events, including SOURCE_FINALIZED and HYPERBRIDGE_FINALIZED
6185
+ * @remarks Unlike queryGetRequest(), this method adds derived finalization status events by querying state machine updates
6186
+ */
6187
+ async queryGetRequestWithStatus(hash) {
6188
+ let request = await this.queryGetRequest(hash);
6189
+ if (!request) return;
6190
+ request = await this.addGetRequestFinalityEvents(request);
6191
+ request.statuses = request.statuses.sort(
6192
+ (a, b) => COMBINED_STATUS_WEIGHTS[a.status] - COMBINED_STATUS_WEIGHTS[b.status]
6193
+ );
6194
+ return request;
6195
+ }
6196
+ /**
6197
+ * Enhances a GET request with finality events by querying state machine updates.
6198
+ *
6199
+ * This method augments a GET request object with additional inferred status events
6200
+ * that represent chain finality confirmations. It adds:
6201
+ * - SOURCE_FINALIZED: When the source chain has finalized the request
6202
+ * - HYPERBRIDGE_FINALIZED: When Hyperbridge has finalized the delivery confirmation and response is ready
6203
+ *
6204
+ * The method also generates appropriate calldata for submitting cross-chain proofs
6205
+ * when applicable.
6206
+ *
6207
+ * @param request - The GET request to enhance with finality events
6208
+ * @returns The request with finality events added
6209
+ * @private
6210
+ */
6211
+ async addGetRequestFinalityEvents(request) {
6212
+ const events = [];
6213
+ const addFinalityEvents = (request2) => {
6214
+ this.logger.trace(`Added ${events.length} \`GetRequest\` finality events`, events);
6215
+ request2.statuses = [...request2.statuses, ...events];
6216
+ return request2;
6217
+ };
6218
+ let hyperbridgeDelivered;
6219
+ if (request.source === this.config.hyperbridge.stateMachineId) {
6220
+ hyperbridgeDelivered = request.statuses[0];
6221
+ return addFinalityEvents(request);
6222
+ } else {
6223
+ const sourceFinality = await this.queryStateMachineUpdateByHeight({
6224
+ statemachineId: request.source,
6225
+ height: request.statuses[0].metadata.blockNumber,
6226
+ chain: this.config.hyperbridge.stateMachineId
6227
+ });
6228
+ if (!sourceFinality) return addFinalityEvents(request);
6229
+ events.push({
6230
+ status: RequestStatus.SOURCE_FINALIZED,
6231
+ metadata: {
6232
+ blockHash: sourceFinality.blockHash,
6233
+ blockNumber: sourceFinality.height,
6234
+ transactionHash: sourceFinality.transactionHash,
6235
+ timestamp: sourceFinality.timestamp
6236
+ }
6237
+ });
6238
+ hyperbridgeDelivered = request.statuses.find((item) => item.status === RequestStatus.HYPERBRIDGE_DELIVERED);
6239
+ if (!hyperbridgeDelivered) return addFinalityEvents(request);
6240
+ }
6241
+ const hyperbridgeFinality = await this.queryStateMachineUpdateByHeight({
6242
+ statemachineId: this.config.hyperbridge.stateMachineId,
6243
+ height: hyperbridgeDelivered.metadata.blockNumber,
6244
+ chain: request.source
6245
+ });
6246
+ if (!hyperbridgeFinality) return addFinalityEvents(request);
6247
+ const sourceChain = await getChain(this.config.source);
6248
+ const hyperbridge = await getChain({
6249
+ ...this.config.hyperbridge,
6250
+ hasher: "Keccak"
6251
+ });
6252
+ try {
6253
+ const response = await this.queryResponseByRequestId(request.commitment);
6254
+ if (!response) return addFinalityEvents(request);
6255
+ const proof = await hyperbridge.queryProof(
6256
+ { Responses: [response.commitment] },
6257
+ request.source,
6258
+ BigInt(hyperbridgeFinality.height)
6259
+ );
6260
+ const calldata = sourceChain.encode({
6261
+ kind: "GetResponse",
6262
+ proof: {
6263
+ stateMachine: this.config.hyperbridge.stateMachineId,
6264
+ consensusStateId: this.config.hyperbridge.consensusStateId,
6265
+ proof,
6266
+ height: BigInt(hyperbridgeFinality.height)
6267
+ },
6268
+ responses: [
6269
+ {
6270
+ get: request,
6271
+ values: request.keys.map((key, index) => ({
6272
+ key,
6273
+ value: response.values[index] || "0x"
6274
+ }))
6275
+ }
6276
+ ],
6277
+ signer: pad("0x")
6278
+ });
6279
+ events.push({
6280
+ status: RequestStatus.HYPERBRIDGE_FINALIZED,
6281
+ metadata: {
6282
+ blockHash: hyperbridgeFinality.blockHash,
6283
+ blockNumber: hyperbridgeFinality.height,
6284
+ transactionHash: hyperbridgeFinality.transactionHash,
6285
+ timestamp: hyperbridgeFinality.timestamp,
6286
+ calldata
6287
+ }
6288
+ });
6289
+ } catch (error) {
6290
+ this.logger.trace("Could not generate HYPERBRIDGE_FINALIZED event for GET request:", error);
6291
+ }
6292
+ return addFinalityEvents(request);
6293
+ }
6112
6294
  /**
6113
6295
  * Create a Stream of status updates for a post request.
6114
6296
  * Stream ends when either the request reaches the destination or times out.
@@ -11373,8 +11555,6 @@ var UNISWAP_V4_QUOTER_ABI = [
11373
11555
  inputs: [{ name: "revertData", type: "bytes", internalType: "bytes" }]
11374
11556
  }
11375
11557
  ];
11376
-
11377
- // src/protocols/intents.ts
11378
11558
  var IntentGateway = class {
11379
11559
  /**
11380
11560
  * Creates a new IntentGateway instance for cross-chain operations.
@@ -11402,14 +11582,10 @@ var IntentGateway = class {
11402
11582
  from: this.source.config.getIntentGatewayAddress(order.destChain),
11403
11583
  to: this.source.config.getIntentGatewayAddress(order.sourceChain)
11404
11584
  };
11405
- const { decimals: sourceChainFeeTokenDecimals, address: sourceChainFeeTokenAddress } = await this.source.getFeeTokenWithDecimals();
11585
+ const { decimals: sourceChainFeeTokenDecimals } = await this.source.getFeeTokenWithDecimals();
11406
11586
  const { address: destChainFeeTokenAddress, decimals: destChainFeeTokenDecimals } = await this.dest.getFeeTokenWithDecimals();
11407
11587
  const { gas: postGasEstimate, postRequestCalldata } = await this.source.estimateGas(postRequest);
11408
- const postGasEstimateInSourceFeeToken = await this.convertGasToFeeToken(
11409
- postGasEstimate,
11410
- this.source.client,
11411
- sourceChainFeeTokenDecimals
11412
- );
11588
+ const postGasEstimateInSourceFeeToken = await this.convertGasToFeeToken(postGasEstimate, "source");
11413
11589
  const relayerFeeInSourceFeeToken = postGasEstimateInSourceFeeToken + 25n * 10n ** BigInt(sourceChainFeeTokenDecimals - 2);
11414
11590
  const relayerFeeInDestFeeToken = adjustFeeDecimals(
11415
11591
  relayerFeeInSourceFeeToken,
@@ -11464,6 +11640,7 @@ var IntentGateway = class {
11464
11640
  }))
11465
11641
  ];
11466
11642
  let destChainFillGas = 0n;
11643
+ let filledWithNativeToken = false;
11467
11644
  try {
11468
11645
  let protocolFeeInNativeToken = await this.quoteNative(postRequest, relayerFeeInDestFeeToken);
11469
11646
  protocolFeeInNativeToken = protocolFeeInNativeToken + protocolFeeInNativeToken * 50n / 10000n;
@@ -11476,6 +11653,7 @@ var IntentGateway = class {
11476
11653
  value: totalEthValue + protocolFeeInNativeToken,
11477
11654
  stateOverride: stateOverrides
11478
11655
  });
11656
+ filledWithNativeToken = true;
11479
11657
  } catch {
11480
11658
  console.warn(
11481
11659
  `Could not estimate gas for fill order with native token as fees for chain ${order.destChain}, now trying with fee token as fees`
@@ -11510,28 +11688,25 @@ var IntentGateway = class {
11510
11688
  stateOverride: stateOverrides
11511
11689
  });
11512
11690
  }
11513
- const fillGasInSourceFeeToken = await this.convertGasToFeeToken(
11514
- destChainFillGas,
11515
- this.dest.client,
11691
+ const fillGasInDestFeeToken = await this.convertGasToFeeToken(destChainFillGas, "dest");
11692
+ const fillGasInSourceFeeToken = adjustFeeDecimals(
11693
+ fillGasInDestFeeToken,
11694
+ destChainFeeTokenDecimals,
11516
11695
  sourceChainFeeTokenDecimals
11517
11696
  );
11518
11697
  const protocolFeeInSourceFeeToken = adjustFeeDecimals(
11519
- // Following baseIsmpModule.sol, the protocol fee is added to the relayer fee
11520
- await this.dest.quote(postRequest) + relayerFeeInDestFeeToken,
11698
+ await this.dest.quote(postRequest),
11521
11699
  destChainFeeTokenDecimals,
11522
11700
  sourceChainFeeTokenDecimals
11523
11701
  );
11524
- const totalEstimate = fillGasInSourceFeeToken + protocolFeeInSourceFeeToken + relayerFeeInSourceFeeToken;
11525
- const SWAP_OPERATIONS_BPS = 3500n;
11526
- const swapOperationsInFeeToken = totalEstimate * SWAP_OPERATIONS_BPS / 10000n;
11527
- const totalFeeTokenAmount = totalEstimate + swapOperationsInFeeToken;
11528
- const totalNativeTokenAmount = await this.convertFeeTokenToNative(
11529
- totalFeeTokenAmount,
11530
- this.source.client,
11531
- sourceChainFeeTokenDecimals
11532
- );
11702
+ let totalEstimateInSourceFeeToken = fillGasInSourceFeeToken + protocolFeeInSourceFeeToken + relayerFeeInSourceFeeToken;
11703
+ if (!filledWithNativeToken) {
11704
+ totalEstimateInSourceFeeToken = totalEstimateInSourceFeeToken + totalEstimateInSourceFeeToken * 200n / 10000n;
11705
+ }
11706
+ let totalNativeTokenAmount = await this.convertFeeTokenToNative(totalEstimateInSourceFeeToken, "source");
11707
+ totalNativeTokenAmount = totalNativeTokenAmount + totalNativeTokenAmount * 200n / 10000n;
11533
11708
  return {
11534
- feeTokenAmount: totalFeeTokenAmount,
11709
+ feeTokenAmount: totalEstimateInSourceFeeToken,
11535
11710
  nativeTokenAmount: totalNativeTokenAmount,
11536
11711
  postRequestCalldata
11537
11712
  };
@@ -11541,47 +11716,82 @@ var IntentGateway = class {
11541
11716
  * Uses USD pricing to convert between fee token amounts and native token costs.
11542
11717
  *
11543
11718
  * @param feeTokenAmount - The amount in fee token (DAI)
11544
- * @param publicClient - The client for the chain to get native token info
11545
- * @param feeTokenDecimals - The decimal places of the fee token
11719
+ * @param getQuoteIn - Whether to use "source" or "dest" chain for the conversion
11546
11720
  * @returns The fee token amount converted to native token amount
11547
11721
  * @private
11548
11722
  */
11549
- async convertFeeTokenToNative(feeTokenAmount, publicClient, feeTokenDecimals) {
11550
- const nativeToken = publicClient.chain?.nativeCurrency;
11551
- const chainId = publicClient.chain?.id;
11552
- if (!nativeToken?.symbol || !nativeToken?.decimals) {
11553
- throw new Error("Chain native currency information not available");
11723
+ async convertFeeTokenToNative(feeTokenAmount, getQuoteIn) {
11724
+ const client = this[getQuoteIn].client;
11725
+ const evmChainID = `EVM-${client.chain?.id}`;
11726
+ const wethAsset = this[getQuoteIn].config.getWrappedNativeAssetWithDecimals(evmChainID).asset;
11727
+ const feeToken = await this[getQuoteIn].getFeeTokenWithDecimals();
11728
+ try {
11729
+ const { amountOut } = await this.findBestProtocolWithAmountIn(
11730
+ getQuoteIn,
11731
+ feeToken.address,
11732
+ wethAsset,
11733
+ feeTokenAmount,
11734
+ "v2"
11735
+ );
11736
+ if (amountOut === 0n) {
11737
+ throw new Error();
11738
+ }
11739
+ return amountOut;
11740
+ } catch {
11741
+ const nativeCurrency = client.chain?.nativeCurrency;
11742
+ const chainId = client.chain?.id;
11743
+ const feeTokenAmountDecimal = new Decimal(formatUnits(feeTokenAmount, feeToken.decimals));
11744
+ const nativeTokenPriceUsd = new Decimal(await fetchPrice(nativeCurrency?.symbol, chainId));
11745
+ const totalCostInNativeToken = feeTokenAmountDecimal.dividedBy(nativeTokenPriceUsd);
11746
+ return parseUnits(totalCostInNativeToken.toFixed(nativeCurrency?.decimals), nativeCurrency?.decimals);
11554
11747
  }
11555
- const feeTokenAmountNumber = Number(feeTokenAmount) / Math.pow(10, feeTokenDecimals);
11556
- const nativeTokenPriceUsd = await fetchPrice(nativeToken.symbol, chainId);
11557
- const totalCostInNativeToken = feeTokenAmountNumber / nativeTokenPriceUsd;
11558
- return BigInt(Math.floor(totalCostInNativeToken * Math.pow(10, nativeToken.decimals)));
11559
11748
  }
11560
11749
  /**
11561
11750
  * Converts gas costs to the equivalent amount in the fee token (DAI).
11562
11751
  * Uses USD pricing to convert between native token gas costs and fee token amounts.
11563
11752
  *
11564
11753
  * @param gasEstimate - The estimated gas units
11565
- * @param publicClient - The client for the chain to get gas prices
11566
- * @param targetDecimals - The decimal places of the target fee token
11754
+ * @param gasEstimateIn - Whether to use "source" or "dest" chain for the conversion
11567
11755
  * @returns The gas cost converted to fee token amount
11568
11756
  * @private
11569
11757
  */
11570
- async convertGasToFeeToken(gasEstimate, publicClient, targetDecimals) {
11571
- const gasPrice = await publicClient.getGasPrice();
11758
+ async convertGasToFeeToken(gasEstimate, gasEstimateIn) {
11759
+ const client = this[gasEstimateIn].client;
11760
+ const gasPrice = await client.getGasPrice();
11572
11761
  const gasCostInWei = gasEstimate * gasPrice;
11573
- const nativeToken = publicClient.chain?.nativeCurrency;
11574
- const chainId = publicClient.chain?.id;
11575
- if (!nativeToken?.symbol || !nativeToken?.decimals) {
11576
- throw new Error("Chain native currency information not available");
11762
+ const evmChainID = `EVM-${client.chain?.id}`;
11763
+ const wethAddr = this[gasEstimateIn].config.getWrappedNativeAssetWithDecimals(evmChainID).asset;
11764
+ const feeToken = await this[gasEstimateIn].getFeeTokenWithDecimals();
11765
+ try {
11766
+ const { amountOut } = await this.findBestProtocolWithAmountIn(
11767
+ gasEstimateIn,
11768
+ wethAddr,
11769
+ feeToken.address,
11770
+ gasCostInWei,
11771
+ "v2"
11772
+ );
11773
+ if (amountOut === 0n) {
11774
+ throw new Error();
11775
+ }
11776
+ return amountOut;
11777
+ } catch {
11778
+ const nativeCurrency = client.chain?.nativeCurrency;
11779
+ const chainId = client.chain?.id;
11780
+ const gasCostInToken = new Decimal(formatUnits(gasCostInWei, nativeCurrency?.decimals));
11781
+ const tokenPriceUsd = await fetchPrice(nativeCurrency?.symbol, chainId);
11782
+ const gasCostUsd = gasCostInToken.times(tokenPriceUsd);
11783
+ const feeTokenPriceUsd = new Decimal(1);
11784
+ const gasCostInFeeToken = gasCostUsd.dividedBy(feeTokenPriceUsd);
11785
+ return parseUnits(gasCostInFeeToken.toFixed(feeToken.decimals), feeToken.decimals);
11577
11786
  }
11578
- const gasCostInToken = Number(gasCostInWei) / Math.pow(10, nativeToken.decimals);
11579
- const tokenPriceUsd = await fetchPrice(nativeToken.symbol, chainId);
11580
- const gasCostUsd = gasCostInToken * tokenPriceUsd;
11581
- const feeTokenPriceUsd = await fetchPrice("DAI");
11582
- const gasCostInFeeToken = gasCostUsd / feeTokenPriceUsd;
11583
- return BigInt(Math.floor(gasCostInFeeToken * Math.pow(10, targetDecimals)));
11584
11787
  }
11788
+ /**
11789
+ * Gets a quote for the native token cost of dispatching a post request.
11790
+ *
11791
+ * @param postRequest - The post request to quote
11792
+ * @param fee - The fee amount in fee token
11793
+ * @returns The native token amount required
11794
+ */
11585
11795
  async quoteNative(postRequest, fee) {
11586
11796
  const dispatchPost = {
11587
11797
  dest: toHex(postRequest.dest),
@@ -11603,37 +11813,38 @@ var IntentGateway = class {
11603
11813
  * Finds the best Uniswap protocol (V2, V3, or V4) for swapping tokens given a desired output amount.
11604
11814
  * Compares liquidity and pricing across different protocols and fee tiers.
11605
11815
  *
11606
- * @param chain - The chain identifier where the swap will occur
11816
+ * @param getQuoteIn - Whether to use "source" or "dest" chain for the swap
11607
11817
  * @param tokenIn - The address of the input token
11608
11818
  * @param tokenOut - The address of the output token
11609
11819
  * @param amountOut - The desired output amount
11610
11820
  * @returns Object containing the best protocol, required input amount, and fee tier (for V3/V4)
11611
11821
  */
11612
- async findBestProtocolWithAmountOut(chain, tokenIn, tokenOut, amountOut) {
11613
- const destClient = this.dest.client;
11822
+ async findBestProtocolWithAmountOut(getQuoteIn, tokenIn, tokenOut, amountOut) {
11823
+ const client = this[getQuoteIn].client;
11824
+ const evmChainID = `EVM-${client.chain?.id}`;
11614
11825
  let amountInV2 = maxUint256;
11615
11826
  let amountInV3 = maxUint256;
11616
11827
  let amountInV4 = maxUint256;
11617
11828
  let bestV3Fee = 0;
11618
11829
  let bestV4Fee = 0;
11619
11830
  const commonFees = [100, 500, 3e3, 1e4];
11620
- const v2Router = this.source.config.getUniswapRouterV2Address(chain);
11621
- const v2Factory = this.source.config.getUniswapV2FactoryAddress(chain);
11622
- const v3Factory = this.source.config.getUniswapV3FactoryAddress(chain);
11623
- const v3Quoter = this.source.config.getUniswapV3QuoterAddress(chain);
11624
- const v4Quoter = this.source.config.getUniswapV4QuoterAddress(chain);
11625
- const wethAsset = this.source.config.getWrappedNativeAssetWithDecimals(chain).asset;
11831
+ const v2Router = this.source.config.getUniswapRouterV2Address(evmChainID);
11832
+ const v2Factory = this.source.config.getUniswapV2FactoryAddress(evmChainID);
11833
+ const v3Factory = this.source.config.getUniswapV3FactoryAddress(evmChainID);
11834
+ const v3Quoter = this.source.config.getUniswapV3QuoterAddress(evmChainID);
11835
+ const v4Quoter = this.source.config.getUniswapV4QuoterAddress(evmChainID);
11836
+ const wethAsset = this.source.config.getWrappedNativeAssetWithDecimals(evmChainID).asset;
11626
11837
  const tokenInForQuote = tokenIn === ADDRESS_ZERO ? wethAsset : tokenIn;
11627
11838
  const tokenOutForQuote = tokenOut === ADDRESS_ZERO ? wethAsset : tokenOut;
11628
11839
  try {
11629
- const v2PairExists = await destClient.readContract({
11840
+ const v2PairExists = await client.readContract({
11630
11841
  address: v2Factory,
11631
11842
  abi: uniswapV2Factory_default.ABI,
11632
11843
  functionName: "getPair",
11633
11844
  args: [tokenInForQuote, tokenOutForQuote]
11634
11845
  });
11635
11846
  if (v2PairExists !== ADDRESS_ZERO) {
11636
- const v2AmountIn = await destClient.readContract({
11847
+ const v2AmountIn = await client.readContract({
11637
11848
  address: v2Router,
11638
11849
  abi: uniswapRouterV2_default.ABI,
11639
11850
  functionName: "getAmountsIn",
@@ -11647,20 +11858,20 @@ var IntentGateway = class {
11647
11858
  let bestV3AmountIn = maxUint256;
11648
11859
  for (const fee of commonFees) {
11649
11860
  try {
11650
- const pool = await destClient.readContract({
11861
+ const pool = await client.readContract({
11651
11862
  address: v3Factory,
11652
11863
  abi: uniswapV3Factory_default.ABI,
11653
11864
  functionName: "getPool",
11654
11865
  args: [tokenInForQuote, tokenOutForQuote, fee]
11655
11866
  });
11656
11867
  if (pool !== ADDRESS_ZERO) {
11657
- const liquidity = await destClient.readContract({
11868
+ const liquidity = await client.readContract({
11658
11869
  address: pool,
11659
11870
  abi: uniswapV3Pool_default.ABI,
11660
11871
  functionName: "liquidity"
11661
11872
  });
11662
11873
  if (liquidity > BigInt(0)) {
11663
- const quoteResult = (await destClient.simulateContract({
11874
+ const quoteResult = (await client.simulateContract({
11664
11875
  address: v3Quoter,
11665
11876
  abi: uniswapV3Quoter_default.ABI,
11666
11877
  functionName: "quoteExactOutputSingle",
@@ -11700,7 +11911,7 @@ var IntentGateway = class {
11700
11911
  hooks: ADDRESS_ZERO
11701
11912
  // No hooks
11702
11913
  };
11703
- const quoteResult = (await destClient.simulateContract({
11914
+ const quoteResult = (await client.simulateContract({
11704
11915
  address: v4Quoter,
11705
11916
  abi: UNISWAP_V4_QUOTER_ABI,
11706
11917
  functionName: "quoteExactOutputSingle",
@@ -11767,43 +11978,48 @@ var IntentGateway = class {
11767
11978
  * Finds the best Uniswap protocol (V2, V3, or V4) for swapping tokens given an input amount.
11768
11979
  * Compares liquidity and pricing across different protocols and fee tiers.
11769
11980
  *
11770
- * @param chain - The chain identifier where the swap will occur
11981
+ * @param getQuoteIn - Whether to use "source" or "dest" chain for the swap
11771
11982
  * @param tokenIn - The address of the input token
11772
11983
  * @param tokenOut - The address of the output token
11773
11984
  * @param amountIn - The input amount to swap
11985
+ * @param selectedProtocol - Optional specific protocol to use ("v2", "v3", or "v4")
11774
11986
  * @returns Object containing the best protocol, expected output amount, and fee tier (for V3/V4)
11775
11987
  */
11776
- async findBestProtocolWithAmountIn(chain, tokenIn, tokenOut, amountIn) {
11777
- const destClient = this.dest.client;
11988
+ async findBestProtocolWithAmountIn(getQuoteIn, tokenIn, tokenOut, amountIn, selectedProtocol) {
11989
+ const client = this[getQuoteIn].client;
11990
+ const evmChainID = `EVM-${client.chain?.id}`;
11778
11991
  let amountOutV2 = BigInt(0);
11779
11992
  let amountOutV3 = BigInt(0);
11780
11993
  let amountOutV4 = BigInt(0);
11781
11994
  let bestV3Fee = 0;
11782
11995
  let bestV4Fee = 0;
11783
11996
  const commonFees = [100, 500, 3e3, 1e4];
11784
- const v2Router = this.source.config.getUniswapRouterV2Address(chain);
11785
- const v2Factory = this.source.config.getUniswapV2FactoryAddress(chain);
11786
- const v3Factory = this.source.config.getUniswapV3FactoryAddress(chain);
11787
- const v3Quoter = this.source.config.getUniswapV3QuoterAddress(chain);
11788
- const v4Quoter = this.source.config.getUniswapV4QuoterAddress(chain);
11789
- const wethAsset = this.source.config.getWrappedNativeAssetWithDecimals(chain).asset;
11997
+ const v2Router = this.source.config.getUniswapRouterV2Address(evmChainID);
11998
+ const v2Factory = this.source.config.getUniswapV2FactoryAddress(evmChainID);
11999
+ const v3Factory = this.source.config.getUniswapV3FactoryAddress(evmChainID);
12000
+ const v3Quoter = this.source.config.getUniswapV3QuoterAddress(evmChainID);
12001
+ const v4Quoter = this.source.config.getUniswapV4QuoterAddress(evmChainID);
12002
+ const wethAsset = this.source.config.getWrappedNativeAssetWithDecimals(evmChainID).asset;
11790
12003
  const tokenInForQuote = tokenIn === ADDRESS_ZERO ? wethAsset : tokenIn;
11791
12004
  const tokenOutForQuote = tokenOut === ADDRESS_ZERO ? wethAsset : tokenOut;
11792
12005
  try {
11793
- const v2PairExists = await destClient.readContract({
12006
+ const v2PairExists = await client.readContract({
11794
12007
  address: v2Factory,
11795
12008
  abi: uniswapV2Factory_default.ABI,
11796
12009
  functionName: "getPair",
11797
12010
  args: [tokenInForQuote, tokenOutForQuote]
11798
12011
  });
11799
12012
  if (v2PairExists !== ADDRESS_ZERO) {
11800
- const v2AmountOut = await destClient.readContract({
12013
+ const v2AmountOut = await client.readContract({
11801
12014
  address: v2Router,
11802
12015
  abi: uniswapRouterV2_default.ABI,
11803
12016
  functionName: "getAmountsOut",
11804
12017
  args: [amountIn, [tokenInForQuote, tokenOutForQuote]]
11805
12018
  });
11806
12019
  amountOutV2 = v2AmountOut[1];
12020
+ if (selectedProtocol === "v2") {
12021
+ return { protocol: "v2", amountOut: amountOutV2 };
12022
+ }
11807
12023
  }
11808
12024
  } catch (error) {
11809
12025
  console.warn("V2 quote failed:", error);
@@ -11811,20 +12027,20 @@ var IntentGateway = class {
11811
12027
  let bestV3AmountOut = BigInt(0);
11812
12028
  for (const fee of commonFees) {
11813
12029
  try {
11814
- const pool = await destClient.readContract({
12030
+ const pool = await client.readContract({
11815
12031
  address: v3Factory,
11816
12032
  abi: uniswapV3Factory_default.ABI,
11817
12033
  functionName: "getPool",
11818
12034
  args: [tokenInForQuote, tokenOutForQuote, fee]
11819
12035
  });
11820
12036
  if (pool !== ADDRESS_ZERO) {
11821
- const liquidity = await destClient.readContract({
12037
+ const liquidity = await client.readContract({
11822
12038
  address: pool,
11823
12039
  abi: uniswapV3Pool_default.ABI,
11824
12040
  functionName: "liquidity"
11825
12041
  });
11826
12042
  if (liquidity > BigInt(0)) {
11827
- const quoteResult = (await destClient.simulateContract({
12043
+ const quoteResult = (await client.simulateContract({
11828
12044
  address: v3Quoter,
11829
12045
  abi: uniswapV3Quoter_default.ABI,
11830
12046
  functionName: "quoteExactInputSingle",
@@ -11850,6 +12066,9 @@ var IntentGateway = class {
11850
12066
  }
11851
12067
  }
11852
12068
  amountOutV3 = bestV3AmountOut;
12069
+ if (selectedProtocol === "v3") {
12070
+ return { protocol: "v3", amountOut: amountOutV3, fee: bestV3Fee };
12071
+ }
11853
12072
  let bestV4AmountOut = BigInt(0);
11854
12073
  for (const fee of commonFees) {
11855
12074
  try {
@@ -11864,7 +12083,7 @@ var IntentGateway = class {
11864
12083
  hooks: ADDRESS_ZERO
11865
12084
  // No hooks
11866
12085
  };
11867
- const quoteResult = (await destClient.simulateContract({
12086
+ const quoteResult = (await client.simulateContract({
11868
12087
  address: v4Quoter,
11869
12088
  abi: UNISWAP_V4_QUOTER_ABI,
11870
12089
  functionName: "quoteExactInputSingle",
@@ -11888,6 +12107,9 @@ var IntentGateway = class {
11888
12107
  }
11889
12108
  }
11890
12109
  amountOutV4 = bestV4AmountOut;
12110
+ if (selectedProtocol === "v4") {
12111
+ return { protocol: "v4", amountOut: amountOutV4, fee: bestV4Fee };
12112
+ }
11891
12113
  if (amountOutV2 === BigInt(0) && amountOutV3 === BigInt(0) && amountOutV4 === BigInt(0)) {
11892
12114
  return {
11893
12115
  protocol: null,