@hyperbridge/sdk 2.3.1 → 2.3.3

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.
@@ -175,9 +175,9 @@ declare class EvmChain implements IChain {
175
175
  quoteNative(request: IPostRequest | IGetRequest, fee: bigint): Promise<bigint>;
176
176
  /**
177
177
  * Given a desired output amount of a token, returns how much native is needed as input.
178
- * Uses the chain's Uniswap V2 router: WETH → tokenOut path.
178
+ * Uses the chain's Uniswap V2 router (or `router` when provided): WETH → tokenOut path.
179
179
  */
180
- getAmountsIn(amountOut: bigint, tokenOutForQuote: HexString, chain?: string): Promise<bigint>;
180
+ getAmountsIn(amountOut: bigint, tokenOutForQuote: HexString, chain?: string, router?: HexString): Promise<bigint>;
181
181
  /**
182
182
  * Given an input amount of native token, returns how much of the output token you get.
183
183
  * Uses the chain's Uniswap V2 router: WETH → tokenOut path.
@@ -175,9 +175,9 @@ declare class EvmChain implements IChain {
175
175
  quoteNative(request: IPostRequest | IGetRequest, fee: bigint): Promise<bigint>;
176
176
  /**
177
177
  * Given a desired output amount of a token, returns how much native is needed as input.
178
- * Uses the chain's Uniswap V2 router: WETH → tokenOut path.
178
+ * Uses the chain's Uniswap V2 router (or `router` when provided): WETH → tokenOut path.
179
179
  */
180
- getAmountsIn(amountOut: bigint, tokenOutForQuote: HexString, chain?: string): Promise<bigint>;
180
+ getAmountsIn(amountOut: bigint, tokenOutForQuote: HexString, chain?: string, router?: HexString): Promise<bigint>;
181
181
  /**
182
182
  * Given an input amount of native token, returns how much of the output token you get.
183
183
  * Uses the chain's Uniswap V2 router: WETH → tokenOut path.
@@ -6775,15 +6775,20 @@ var EvmChain = class _EvmChain {
6775
6775
  async quoteNative(request, fee) {
6776
6776
  const totalFee = await this.quote(request) + fee;
6777
6777
  const feeToken = await this.getFeeTokenWithDecimals();
6778
- return this.getAmountsIn(totalFee, feeToken.address, request.source);
6778
+ const hostRouter = await this.publicClient.readContract({
6779
+ address: this.params.host,
6780
+ abi: evmHost_default.ABI,
6781
+ functionName: "uniswapV2Router"
6782
+ });
6783
+ return this.getAmountsIn(totalFee, feeToken.address, request.source, hostRouter);
6779
6784
  }
6780
6785
  /**
6781
6786
  * Given a desired output amount of a token, returns how much native is needed as input.
6782
- * Uses the chain's Uniswap V2 router: WETH → tokenOut path.
6787
+ * Uses the chain's Uniswap V2 router (or `router` when provided): WETH → tokenOut path.
6783
6788
  */
6784
- async getAmountsIn(amountOut, tokenOutForQuote, chain) {
6789
+ async getAmountsIn(amountOut, tokenOutForQuote, chain, router) {
6785
6790
  const chainId = chain ?? `EVM-${this.params.chainId}`;
6786
- const v2Router = this.configService.getUniswapRouterV2Address(chainId);
6791
+ const v2Router = router ?? this.configService.getUniswapRouterV2Address(chainId);
6787
6792
  const WETH = this.configService.getWrappedNativeAssetWithDecimals(chainId).asset;
6788
6793
  const v2AmountIn = await this.publicClient.simulateContract({
6789
6794
  address: v2Router,
@@ -7028,6 +7033,11 @@ var ExpectedError = class _ExpectedError extends Error {
7028
7033
  return error instanceof _ExpectedError;
7029
7034
  }
7030
7035
  };
7036
+
7037
+ // src/configs/constants.ts
7038
+ var ISMP_PREFIX = ":child_storage:default:ISMPv2";
7039
+
7040
+ // src/chains/substrate.ts
7031
7041
  var HttpRpcClient = class {
7032
7042
  constructor(url) {
7033
7043
  this.url = url;
@@ -7151,7 +7161,7 @@ var SubstrateChain = class _SubstrateChain {
7151
7161
  * @returns {Promise<HexString | undefined>} The commitment data if found, undefined otherwise.
7152
7162
  */
7153
7163
  async queryRequestCommitment(commitment) {
7154
- const prefix = toHex(":child_storage:default:ISMP");
7164
+ const prefix = toHex(ISMP_PREFIX);
7155
7165
  const key = this.requestCommitmentKey(commitment);
7156
7166
  const item = await this.rpcClient.call("childstate_getStorage", [prefix, key]);
7157
7167
  return item;
@@ -7162,7 +7172,7 @@ var SubstrateChain = class _SubstrateChain {
7162
7172
  * @returns {Promise<HexString | undefined>} The relayer address responsible for delivering the request.
7163
7173
  */
7164
7174
  async queryRequestReceipt(commitment) {
7165
- const prefix = toHex(":child_storage:default:ISMP");
7175
+ const prefix = toHex(ISMP_PREFIX);
7166
7176
  const key = this.requestReceiptKey(commitment);
7167
7177
  const item = await this.rpcClient.call("childstate_getStorage", [prefix, key]);
7168
7178
  return item;
@@ -7186,7 +7196,7 @@ var SubstrateChain = class _SubstrateChain {
7186
7196
  * @returns {Promise<HexString | undefined>} The receipt data if present, otherwise undefined.
7187
7197
  */
7188
7198
  async queryResponseReceipt(commitment) {
7189
- const prefix = toHex(":child_storage:default:ISMP");
7199
+ const prefix = toHex(ISMP_PREFIX);
7190
7200
  const key = this.responseReceiptKey(commitment);
7191
7201
  const item = await this.rpcClient.call("childstate_getStorage", [prefix, key]);
7192
7202
  return item;
@@ -15294,7 +15304,11 @@ var OrderExecutor = class {
15294
15304
  */
15295
15305
  async fetchBids(params) {
15296
15306
  const { commitment, solver, solverLockStartTime } = params;
15297
- const fetchedBids = await this.ctx.intentsCoprocessor.getBidsForOrder(commitment);
15307
+ const intentsCoprocessor = this.ctx.intentsCoprocessor;
15308
+ if (!intentsCoprocessor) {
15309
+ throw new Error("IntentsCoprocessor required for order execution");
15310
+ }
15311
+ const fetchedBids = await intentsCoprocessor.getBidsForOrder(commitment);
15298
15312
  if (solver) {
15299
15313
  const { address, timeoutMs } = solver;
15300
15314
  const solverLockActive = Date.now() - solverLockStartTime < timeoutMs;
@@ -15383,7 +15397,7 @@ var OrderExecutor = class {
15383
15397
  * → (`FILLED` | `PARTIAL_FILL`)* → (`FILLED` | `EXPIRED`)
15384
15398
  *
15385
15399
  * **Cross-chain:** `AWAITING_BIDS` → `BIDS_RECEIVED` → `BID_SELECTED`
15386
- * (terminates — settlement is confirmed async via Hyperbridge)
15400
+ * `FILLED`
15387
15401
  */
15388
15402
  async *executeOrder(options) {
15389
15403
  const { order, sessionPrivateKey, auctionTimeMs, pollIntervalMs = DEFAULT_POLL_INTERVAL, solver } = options;
@@ -15522,7 +15536,13 @@ var OrderExecutor = class {
15522
15536
  userOp: result.userOp,
15523
15537
  transactionHash: result.txnHash
15524
15538
  };
15525
- const fill = this.processFillResult(result, commitment, targetAssets, totalFilledAssets, remainingAssets);
15539
+ const fill = this.processFillResult(
15540
+ result,
15541
+ commitment,
15542
+ targetAssets,
15543
+ totalFilledAssets,
15544
+ remainingAssets
15545
+ );
15526
15546
  totalFilledAssets = fill.totalFilledAssets;
15527
15547
  remainingAssets = fill.remainingAssets;
15528
15548
  if (fill.update) {
@@ -15548,6 +15568,10 @@ var OrderCanceller = class {
15548
15568
  this.ctx = ctx;
15549
15569
  }
15550
15570
  ctx;
15571
+ logger = createConsola({
15572
+ level: LogLevels.info,
15573
+ formatOptions: { columns: 80, colors: true, compact: true, date: false }
15574
+ }).withTag("[OrderCanceller]");
15551
15575
  /**
15552
15576
  * Returns both the native token cost and the relayer fee for cancelling an
15553
15577
  * order. Frontends can use `relayerFee` to approve the ERC-20 spend before
@@ -15963,32 +15987,55 @@ var OrderCanceller = class {
15963
15987
  }
15964
15988
  /**
15965
15989
  * Submits an unsigned GET request message to Hyperbridge and waits until
15966
- * the request receipt is confirmed on-chain.
15990
+ * the GET response receipt is confirmed on-chain.
15967
15991
  *
15968
- * If the initial submission fails, the method waits 30 seconds and then
15969
- * retries querying for the receipt up to 10 times with 5-second back-off.
15992
+ * GET handling on Hyperbridge creates a response receipt keyed by the
15993
+ * request commitment. That receipt is the durable delivery signal, so a
15994
+ * duplicate unsigned submission is considered successful only if the
15995
+ * response receipt can be observed.
15970
15996
  *
15971
15997
  * @param hyperbridge - Hyperbridge Substrate chain client.
15972
15998
  * @param commitment - The GET request commitment hash used to poll for the receipt.
15973
15999
  * @param message - The fully constructed GET request message to submit.
15974
16000
  */
15975
16001
  async submitAndConfirmReceipt(hyperbridge, commitment, message) {
15976
- let storageValue = await hyperbridge.queryRequestReceipt(commitment);
15977
- if (!storageValue) {
15978
- try {
15979
- await hyperbridge.submitUnsigned(message);
15980
- } catch {
15981
- }
16002
+ this.logger.info(`Checking GET response receipt before Hyperbridge delivery (${commitment})`);
16003
+ if (await this.queryDeliveredReceipt(hyperbridge, commitment)) {
16004
+ this.logger.info(`GET ${commitment} already delivered to Hyperbridge; skipping unsigned submission`);
16005
+ return;
16006
+ }
16007
+ try {
16008
+ this.logger.info(`Submitting unsigned GET ${commitment} to Hyperbridge`);
16009
+ await hyperbridge.submitUnsigned(message);
16010
+ this.logger.info(`Unsigned GET ${commitment} submitted; waiting for Hyperbridge response receipt`);
15982
16011
  await sleep(3e4);
15983
- storageValue = await retryPromise(
15984
- async () => {
15985
- const value = await hyperbridge.queryRequestReceipt(commitment);
15986
- if (!value) throw new Error("Receipt not found");
15987
- return value;
15988
- },
15989
- { maxRetries: 10, backoffMs: 5e3, logMessage: "Checking for receipt" }
16012
+ } catch (error) {
16013
+ this.logger.warn(
16014
+ `Unsigned GET submit failed for ${commitment}; polling response receipt before failing: ${String(error)}`
15990
16015
  );
15991
16016
  }
16017
+ try {
16018
+ await this.pollDeliveredReceipt(hyperbridge, commitment);
16019
+ this.logger.info(`Confirmed Hyperbridge GET delivery for ${commitment}`);
16020
+ } catch (error) {
16021
+ const message2 = `Failed to deliver GET request to Hyperbridge; no response receipt found for ${commitment}: ${String(error)}`;
16022
+ this.logger.error(message2);
16023
+ throw new Error(message2);
16024
+ }
16025
+ }
16026
+ async queryDeliveredReceipt(hyperbridge, commitment) {
16027
+ return hyperbridge.queryResponseReceipt(commitment);
16028
+ }
16029
+ async pollDeliveredReceipt(hyperbridge, commitment) {
16030
+ this.logger.info(`Polling Hyperbridge GET response receipt for ${commitment}`);
16031
+ return retryPromise(
16032
+ async () => {
16033
+ const value = await this.queryDeliveredReceipt(hyperbridge, commitment);
16034
+ if (!value) throw new Error(`GET response receipt not found for ${commitment}`);
16035
+ return value;
16036
+ },
16037
+ { maxRetries: 10, backoffMs: 5e3, logMessage: `Checking GET response receipt ${commitment}` }
16038
+ );
15992
16039
  }
15993
16040
  /**
15994
16041
  * Estimates the relayer fee for delivering a POST from dest to source.
@@ -16059,7 +16106,7 @@ var BidImpl = class {
16059
16106
  const sessionKeyAddress = this.order.session;
16060
16107
  const sessionKeyData = this.sessionPrivateKey ? { privateKey: this.sessionPrivateKey } : await this.ctx.sessionKeyStorage.getSessionKeyByAddress(sessionKeyAddress);
16061
16108
  if (!sessionKeyData) {
16062
- throw new Error("SessionKey not found for commitment: " + commitment);
16109
+ throw new Error(`SessionKey not found for commitment: ${commitment}`);
16063
16110
  }
16064
16111
  const signature = await CryptoUtils.signSolverSelection(
16065
16112
  commitment,
@@ -16119,8 +16166,8 @@ var BidImpl = class {
16119
16166
  /**
16120
16167
  * Signs the `SelectSolver` message with the session key, appends it to the
16121
16168
  * solver's existing UserOp signature, and submits the UserOperation to the
16122
- * bundler. For same-chain orders, waits for the receipt and reads
16123
- * `OrderFilled` / `PartialFill` logs to determine fill status.
16169
+ * bundler. Waits for the receipt and reads `OrderFilled` / `PartialFill`
16170
+ * logs to determine fill status.
16124
16171
  *
16125
16172
  * @returns A {@link SelectBidResult} with the submitted UserOperation, its hash,
16126
16173
  * the solver address, transaction hash, and fill status.
@@ -16160,33 +16207,31 @@ var BidImpl = class {
16160
16207
  { maxRetries: 5, backoffMs: 2e3, logMessage: "Fetching user operation receipt" }
16161
16208
  );
16162
16209
  txnHash = receipt.receipt.transactionHash;
16163
- if (this.order.source === this.order.destination) {
16164
- try {
16165
- const chainReceipt = await this.ctx.dest.client.waitForTransactionReceipt({
16166
- hash: txnHash,
16167
- confirmations: 1
16168
- });
16169
- const events = parseEventLogs({
16170
- abi: ABI3,
16171
- logs: chainReceipt.logs,
16172
- eventName: ["OrderFilled", "PartialFill"]
16173
- });
16174
- const matched = events.find((e) => {
16175
- if (e.eventName === "OrderFilled")
16176
- return e.args.commitment.toLowerCase() === commitment.toLowerCase();
16177
- if (e.eventName === "PartialFill")
16178
- return e.args.commitment.toLowerCase() === commitment.toLowerCase();
16179
- return false;
16180
- });
16181
- if (matched?.eventName === "OrderFilled") {
16182
- fillStatus = "full";
16183
- } else if (matched?.eventName === "PartialFill") {
16184
- fillStatus = "partial";
16185
- filledAssets = matched.args.outputs ?? [];
16186
- }
16187
- } catch {
16188
- throw new Error("Failed to determine fill status from logs");
16210
+ try {
16211
+ const chainReceipt = await this.ctx.dest.client.waitForTransactionReceipt({
16212
+ hash: txnHash,
16213
+ confirmations: 1
16214
+ });
16215
+ const events = parseEventLogs({
16216
+ abi: ABI3,
16217
+ logs: chainReceipt.logs,
16218
+ eventName: ["OrderFilled", "PartialFill"]
16219
+ });
16220
+ const matched = events.find((e) => {
16221
+ if (e.eventName === "OrderFilled")
16222
+ return e.args.commitment.toLowerCase() === commitment.toLowerCase();
16223
+ if (e.eventName === "PartialFill")
16224
+ return e.args.commitment.toLowerCase() === commitment.toLowerCase();
16225
+ return false;
16226
+ });
16227
+ if (matched?.eventName === "OrderFilled") {
16228
+ fillStatus = "full";
16229
+ } else if (matched?.eventName === "PartialFill") {
16230
+ fillStatus = "partial";
16231
+ filledAssets = matched.args.outputs ?? [];
16189
16232
  }
16233
+ } catch {
16234
+ throw new Error("Failed to determine fill status from logs");
16190
16235
  }
16191
16236
  } catch (err) {
16192
16237
  throw new Error(`Failed to execute bid: ${err instanceof Error ? err.message : String(err)}`);
@@ -16988,9 +17033,9 @@ var GasEstimator = class {
16988
17033
  from: this.ctx.source.configService.getIntentGatewayAddress(destChainId),
16989
17034
  to: this.ctx.source.configService.getIntentGatewayAddress(sourceChainId)
16990
17035
  };
17036
+ postRequestFeeInDestFeeToken = postRequestFeeInDestFeeToken * 1005n / 1000n;
16991
17037
  let protocolFeeInNativeToken = await this.ctx.dest.quoteNative(postRequest, postRequestFeeInDestFeeToken).catch(() => 0n);
16992
17038
  protocolFeeInNativeToken = protocolFeeInNativeToken * 1005n / 1000n;
16993
- postRequestFeeInDestFeeToken = postRequestFeeInDestFeeToken * 1005n / 1000n;
16994
17039
  return { postRequestFee: postRequestFeeInDestFeeToken, protocolFee: protocolFeeInNativeToken };
16995
17040
  }
16996
17041
  /**