@hyperbridge/sdk 2.5.0 → 2.6.1

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.
@@ -905,6 +905,10 @@ interface ChainConfigData {
905
905
  USDT: string;
906
906
  cNGN?: string;
907
907
  EXT?: string;
908
+ ZARP?: string;
909
+ EURC?: string;
910
+ XSGD?: string;
911
+ TRYB?: string;
908
912
  };
909
913
  tokenDecimals?: {
910
914
  USDC: number;
@@ -1005,6 +1009,13 @@ declare class ChainConfigService {
1005
1009
  getUsdcDecimals(chain: string): number;
1006
1010
  getUsdtDecimals(chain: string): number;
1007
1011
  getCNgnAsset(chain: string): HexString | undefined;
1012
+ /**
1013
+ * Address of `symbol` on `chain` from the per-chain asset table, matched
1014
+ * case-insensitively ("cNGN" ≡ "CNGN"). This table is the single source of
1015
+ * truth for token addresses — the simplex asset registry resolves through
1016
+ * it, so a new asset is added once in `chain.ts` and nowhere else.
1017
+ */
1018
+ getAssetBySymbol(chain: string, symbol: string): HexString | undefined;
1008
1019
  getCNgnDecimals(chain: string): number | undefined;
1009
1020
  getExtAsset(chain: string): HexString | undefined;
1010
1021
  getExtDecimals(chain: string): number | undefined;
@@ -1180,7 +1191,10 @@ declare class SubstrateChain implements IChain {
1180
1191
  */
1181
1192
  latestStateMachineHeight(stateMachineId: StateMachineIdParams): Promise<bigint>;
1182
1193
  /**
1183
- * Get the state machine update time for a given state machine height.
1194
+ * Get the state machine update time for a given state machine height. Reads the
1195
+ * `BoundedStateMachineUpdateTime` map in pallet-ismp directly, so the height is either
1196
+ * still retained on-chain or it has been evicted — there's no intermediate RPC to
1197
+ * reinterpret the absence.
1184
1198
  * @param {StateMachineHeight} stateMachineHeight - The state machine height.
1185
1199
  * @returns {Promise<bigint>} The statemachine update time in seconds.
1186
1200
  */
@@ -2830,6 +2844,13 @@ interface FillOrderEstimate {
2830
2844
  maxPriorityFeePerGas: bigint;
2831
2845
  totalGasCostWei: bigint;
2832
2846
  totalGasInFeeToken: bigint;
2847
+ /**
2848
+ * Relayer fee for the cross-chain settlement message, denominated in the
2849
+ * SOURCE fee token (same unit as `Order.fees`). This is `RELAYER_MESSAGE_GAS`
2850
+ * priced on the source chain; it is 0 for same-chain fills. A filler's
2851
+ * `order.fees` must cover `totalGasInFeeToken + relayerFeeInSourceFeeToken`.
2852
+ */
2853
+ relayerFeeInSourceFeeToken: bigint;
2833
2854
  }
2834
2855
  /**
2835
2856
  * Result of submitting a bid to Hyperbridge
@@ -905,6 +905,10 @@ interface ChainConfigData {
905
905
  USDT: string;
906
906
  cNGN?: string;
907
907
  EXT?: string;
908
+ ZARP?: string;
909
+ EURC?: string;
910
+ XSGD?: string;
911
+ TRYB?: string;
908
912
  };
909
913
  tokenDecimals?: {
910
914
  USDC: number;
@@ -1005,6 +1009,13 @@ declare class ChainConfigService {
1005
1009
  getUsdcDecimals(chain: string): number;
1006
1010
  getUsdtDecimals(chain: string): number;
1007
1011
  getCNgnAsset(chain: string): HexString | undefined;
1012
+ /**
1013
+ * Address of `symbol` on `chain` from the per-chain asset table, matched
1014
+ * case-insensitively ("cNGN" ≡ "CNGN"). This table is the single source of
1015
+ * truth for token addresses — the simplex asset registry resolves through
1016
+ * it, so a new asset is added once in `chain.ts` and nowhere else.
1017
+ */
1018
+ getAssetBySymbol(chain: string, symbol: string): HexString | undefined;
1008
1019
  getCNgnDecimals(chain: string): number | undefined;
1009
1020
  getExtAsset(chain: string): HexString | undefined;
1010
1021
  getExtDecimals(chain: string): number | undefined;
@@ -1180,7 +1191,10 @@ declare class SubstrateChain implements IChain {
1180
1191
  */
1181
1192
  latestStateMachineHeight(stateMachineId: StateMachineIdParams): Promise<bigint>;
1182
1193
  /**
1183
- * Get the state machine update time for a given state machine height.
1194
+ * Get the state machine update time for a given state machine height. Reads the
1195
+ * `BoundedStateMachineUpdateTime` map in pallet-ismp directly, so the height is either
1196
+ * still retained on-chain or it has been evicted — there's no intermediate RPC to
1197
+ * reinterpret the absence.
1184
1198
  * @param {StateMachineHeight} stateMachineHeight - The state machine height.
1185
1199
  * @returns {Promise<bigint>} The statemachine update time in seconds.
1186
1200
  */
@@ -2830,6 +2844,13 @@ interface FillOrderEstimate {
2830
2844
  maxPriorityFeePerGas: bigint;
2831
2845
  totalGasCostWei: bigint;
2832
2846
  totalGasInFeeToken: bigint;
2847
+ /**
2848
+ * Relayer fee for the cross-chain settlement message, denominated in the
2849
+ * SOURCE fee token (same unit as `Order.fees`). This is `RELAYER_MESSAGE_GAS`
2850
+ * priced on the source chain; it is 0 for same-chain fills. A filler's
2851
+ * `order.fees` must cover `totalGasInFeeToken + relayerFeeInSourceFeeToken`.
2852
+ */
2853
+ relayerFeeInSourceFeeToken: bigint;
2833
2854
  }
2834
2855
  /**
2835
2856
  * Result of submitting a bid to Hyperbridge
@@ -2689,7 +2689,11 @@ var chainConfigs = {
2689
2689
  DAI: "0x6b175474e89094c44da98b954eedeac495271d0f",
2690
2690
  USDC: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
2691
2691
  USDT: "0xdac17f958d2ee523a2206206994597c13d831ec7",
2692
- cNGN: "0x17CDB2a01e7a34CbB3DD4b83260B05d0274C8dab"
2692
+ cNGN: "0x17CDB2a01e7a34CbB3DD4b83260B05d0274C8dab",
2693
+ ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2694
+ EURC: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
2695
+ XSGD: "0x70e8dE73cE538DA2bEEd35d14187F6959a8ecA96",
2696
+ TRYB: "0x2C537E5624e4af88A7ae4060C022609376C8D0EB"
2693
2697
  },
2694
2698
  tokenDecimals: {
2695
2699
  USDC: 6,
@@ -2859,7 +2863,9 @@ var chainConfigs = {
2859
2863
  USDC: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
2860
2864
  USDT: "0xfde4c96c8593536e31f229ea8f37b2ada2699bb2",
2861
2865
  EXT: "0x0e668E5127087e236578893a0e01E41837A28469",
2862
- cNGN: "0x46C85152bFe9f96829aA94755D9f915F9B10EF5F"
2866
+ cNGN: "0x46C85152bFe9f96829aA94755D9f915F9B10EF5F",
2867
+ ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2868
+ EURC: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
2863
2869
  },
2864
2870
  tokenDecimals: {
2865
2871
  USDC: 6,
@@ -2919,7 +2925,9 @@ var chainConfigs = {
2919
2925
  USDC: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359",
2920
2926
  USDT: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
2921
2927
  EXT: "0x7C8c11ADb8EF7cd3CFa718008Ea048445C6E7209",
2922
- cNGN: "0x52828daa48C1a9A06F37500882b42daf0bE04C3B"
2928
+ cNGN: "0x52828daa48C1a9A06F37500882b42daf0bE04C3B",
2929
+ ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2930
+ XSGD: "0xDC3326e71D45186F113a2F448984CA0e8D201995"
2923
2931
  },
2924
2932
  tokenDecimals: {
2925
2933
  USDC: 6,
@@ -5156,6 +5164,21 @@ var ChainConfigService = class {
5156
5164
  getCNgnAsset(chain) {
5157
5165
  return this.getConfig(chain)?.assets?.cNGN;
5158
5166
  }
5167
+ /**
5168
+ * Address of `symbol` on `chain` from the per-chain asset table, matched
5169
+ * case-insensitively ("cNGN" ≡ "CNGN"). This table is the single source of
5170
+ * truth for token addresses — the simplex asset registry resolves through
5171
+ * it, so a new asset is added once in `chain.ts` and nowhere else.
5172
+ */
5173
+ getAssetBySymbol(chain, symbol) {
5174
+ const assets = this.getConfig(chain)?.assets;
5175
+ if (!assets) return void 0;
5176
+ const target = symbol.trim().toUpperCase();
5177
+ for (const [key, address] of Object.entries(assets)) {
5178
+ if (key.toUpperCase() === target) return address;
5179
+ }
5180
+ return void 0;
5181
+ }
5159
5182
  getCNgnDecimals(chain) {
5160
5183
  return this.getConfig(chain)?.tokenDecimals?.cNGN;
5161
5184
  }
@@ -7379,29 +7402,28 @@ var SubstrateChain = class _SubstrateChain {
7379
7402
  return BigInt(latestHeight);
7380
7403
  }
7381
7404
  /**
7382
- * Get the state machine update time for a given state machine height.
7405
+ * Get the state machine update time for a given state machine height. Reads the
7406
+ * `BoundedStateMachineUpdateTime` map in pallet-ismp directly, so the height is either
7407
+ * still retained on-chain or it has been evicted — there's no intermediate RPC to
7408
+ * reinterpret the absence.
7383
7409
  * @param {StateMachineHeight} stateMachineHeight - The state machine height.
7384
7410
  * @returns {Promise<bigint>} The statemachine update time in seconds.
7385
7411
  */
7386
7412
  async stateMachineUpdateTime(stateMachineHeight) {
7387
- const state_id = convertStateIdToStateMachineId(stateMachineHeight.id.stateId);
7388
- const stateMachineId = {
7389
- state_id,
7390
- consensus_state_id: stateMachineHeight.id.consensusStateId
7391
- };
7392
- const payload = {
7393
- id: stateMachineId,
7394
- height: Number(stateMachineHeight.height)
7413
+ if (!this.api) throw new Error("API not initialized");
7414
+ const id = {
7415
+ stateId: stateMachineHeight.id.stateId,
7416
+ // on-chain StateMachineId encodes consensusStateId as [u8; 4]
7417
+ consensusStateId: viem.toHex(viem.toBytes(stateMachineHeight.id.consensusStateId))
7395
7418
  };
7396
- try {
7397
- const updateTime = await this.rpcClient.call("ismp_queryStateMachineUpdateTime", [payload]);
7398
- return BigInt(updateTime);
7399
- } catch (error) {
7400
- if (MissingConsensusUpdateTimeError.isError(error)) {
7401
- throw new MissingConsensusUpdateTimeError(MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, { cause: error });
7402
- }
7403
- throw error;
7419
+ const updateTime = await this.api.query.ismp.boundedStateMachineUpdateTime(
7420
+ id,
7421
+ Number(stateMachineHeight.height)
7422
+ );
7423
+ if (updateTime.isNone) {
7424
+ throw new MissingConsensusUpdateTimeError(MISSING_CONSENSUS_UPDATE_TIME_MESSAGE);
7404
7425
  }
7426
+ return BigInt(updateTime.unwrap().toString());
7405
7427
  }
7406
7428
  /**
7407
7429
  * Get the challenge period for a given state machine id.
@@ -17252,6 +17274,7 @@ var BidManager = class {
17252
17274
  }
17253
17275
  }
17254
17276
  };
17277
+ var RELAYER_MESSAGE_GAS = 1000000n;
17255
17278
  var GasEstimator = class {
17256
17279
  /**
17257
17280
  * @param ctx - Shared IntentsV2 context providing the source and destination
@@ -17272,7 +17295,12 @@ var GasEstimator = class {
17272
17295
  *
17273
17296
  * **Cross-chain orders:** also estimates the ISMP POST request fee required
17274
17297
  * for the solver to trigger source-chain escrow redemption after filling, and
17275
- * includes it in `fillOptions.relayerFee` and `fillOptions.nativeDispatchFee`.
17298
+ * includes it in `fillOptions.relayerFee`. The dispatch is always paid in the
17299
+ * fee token — `nativeDispatchFee` is fixed at 0. The native rail would draw
17300
+ * from the solver account's native balance, which nothing guarantees, and a
17301
+ * shortfall is invisible to estimation (the account balance is overridden
17302
+ * during simulation) — it would only surface as a reverted execution that
17303
+ * still bills the paymaster.
17276
17304
  *
17277
17305
  * **Bundler path:** constructs a mock `PackedUserOperation` signed by an
17278
17306
  * ephemeral keypair, applies state overrides, and calls
@@ -17321,24 +17349,19 @@ var GasEstimator = class {
17321
17349
  intentGatewayV2Address,
17322
17350
  entryPointAddress
17323
17351
  }),
17324
- isSameChain ? Promise.resolve({ postRequestFee: 0n, protocolFee: 0n }) : this.estimateCrossChainFees(
17325
- sourceFeeToken,
17326
- destFeeToken,
17327
- souceStateMachineId,
17328
- destStateMachineId,
17329
- order
17330
- )
17352
+ isSameChain ? Promise.resolve({ postRequestFee: 0n, relayerFeeInSourceFeeToken: 0n }) : this.estimateCrossChainFees(sourceFeeToken, destFeeToken, souceStateMachineId)
17331
17353
  ]);
17332
17354
  const { viem: stateOverrides, bundler: bundlerStateOverrides } = stateOverridesResult;
17333
17355
  const fillOptions = {
17334
17356
  relayerFee: crossChainFees.postRequestFee,
17335
- nativeDispatchFee: crossChainFees.protocolFee,
17357
+ // Always dispatch with the fee token (see the method docs).
17358
+ nativeDispatchFee: 0n,
17336
17359
  outputs: order.output.assets.map((asset) => ({
17337
17360
  ...asset,
17338
17361
  token: normalizeAddressForEvmBytes32(asset.token)
17339
17362
  }))
17340
17363
  };
17341
- const totalNativeValue = totalEthValue + fillOptions.nativeDispatchFee;
17364
+ const totalNativeValue = totalEthValue;
17342
17365
  const priorityFeeBumpPercent = params.maxPriorityFeePerGasBumpPercent ?? 8;
17343
17366
  const maxFeeBumpPercent = params.maxFeePerGasBumpPercent ?? 10;
17344
17367
  let maxPriorityFeePerGas = gasPrice + gasPrice * BigInt(priorityFeeBumpPercent) / 100n;
@@ -17505,37 +17528,32 @@ var GasEstimator = class {
17505
17528
  maxPriorityFeePerGas,
17506
17529
  totalGasCostWei,
17507
17530
  totalGasInFeeToken: totalGasInSourceFeeToken,
17531
+ relayerFeeInSourceFeeToken: crossChainFees.relayerFeeInSourceFeeToken,
17508
17532
  fillOptions
17509
17533
  };
17510
17534
  }
17511
17535
  /**
17512
- * Estimates cross-chain ISMP POST request fees by running the gas-to-fee-token
17513
- * conversion and host nonce fetch in parallel, then quoting the native dispatch cost.
17514
- */
17515
- async estimateCrossChainFees(sourceFeeToken, destFeeToken, sourceChainId, destChainId, order) {
17516
- const postRequestGas = 400000n;
17517
- const [postRequestFeeInSourceFeeToken, nonce] = await Promise.all([
17518
- convertGasToFeeToken(this.ctx, postRequestGas, "source", sourceChainId),
17519
- this.ctx.dest.getHostNonce()
17520
- ]);
17521
- let postRequestFeeInDestFeeToken = adjustDecimals(
17536
+ * Estimates the cross-chain ISMP POST request fee (the relayer fee for the
17537
+ * RedeemEscrow message), in both the source and destination fee tokens.
17538
+ * The dispatch is always paid in the fee token — the native rail was
17539
+ * removed because it silently drew on a native balance nothing guarantees.
17540
+ */
17541
+ async estimateCrossChainFees(sourceFeeToken, destFeeToken, sourceChainId) {
17542
+ const postRequestFeeInSourceFeeToken = await convertGasToFeeToken(
17543
+ this.ctx,
17544
+ RELAYER_MESSAGE_GAS,
17545
+ "source",
17546
+ sourceChainId
17547
+ );
17548
+ const postRequestFeeInDestFeeToken = adjustDecimals(
17522
17549
  postRequestFeeInSourceFeeToken,
17523
17550
  sourceFeeToken.decimals,
17524
17551
  destFeeToken.decimals
17525
17552
  );
17526
- const postRequest = {
17527
- source: destChainId,
17528
- dest: sourceChainId,
17529
- body: constructRedeemEscrowRequestBody({ ...order, id: orderCommitment(order) }, MOCK_ADDRESS),
17530
- timeoutTimestamp: 0n,
17531
- nonce,
17532
- from: this.ctx.source.configService.getIntentGatewayAddress(destChainId),
17533
- to: this.ctx.source.configService.getIntentGatewayAddress(sourceChainId)
17553
+ return {
17554
+ postRequestFee: postRequestFeeInDestFeeToken,
17555
+ relayerFeeInSourceFeeToken: postRequestFeeInSourceFeeToken
17534
17556
  };
17535
- postRequestFeeInDestFeeToken = postRequestFeeInDestFeeToken * 1005n / 1000n;
17536
- let protocolFeeInNativeToken = await this.ctx.dest.quoteNative(postRequest, postRequestFeeInDestFeeToken).catch(() => 0n);
17537
- protocolFeeInNativeToken = protocolFeeInNativeToken * 1005n / 1000n;
17538
- return { postRequestFee: postRequestFeeInDestFeeToken, protocolFee: protocolFeeInNativeToken };
17539
17557
  }
17540
17558
  /**
17541
17559
  * Builds EVM state override objects for gas estimation of the `fillOrder`
@@ -18295,7 +18313,6 @@ function isConfiguredAddress(address) {
18295
18313
  }
18296
18314
 
18297
18315
  // src/protocols/intents/IntentGateway.ts
18298
- var CROSS_CHAIN_ORDER_FEE_GAS_BUMP = 600000n;
18299
18316
  var IntentGateway = class _IntentGateway {
18300
18317
  /** EVM chain on which orders are placed and escrowed. */
18301
18318
  source;
@@ -18482,10 +18499,12 @@ var IntentGateway = class _IntentGateway {
18482
18499
  * placement, fee estimation, bid collection, and execution.
18483
18500
  *
18484
18501
  * **Yield/receive protocol:**
18485
- * 1. If `order.fees` is unset or zero, estimates gas on an internal copy and sets
18486
- * same-chain fees to twice the estimate. Cross-chain orders retain a 1% buffer
18487
- * and include an additional 600k-gas fee uplift, while the wei cost used for
18488
- * the `value` field receives a 2% buffer.
18502
+ * 1. If `order.fees` is unset or zero, estimates gas on an internal copy and
18503
+ * sets same-chain fees to twice the estimate. Cross-chain orders attach
18504
+ * (fill gas + a settlement-message uplift of `RELAYER_MESSAGE_GAS`, the
18505
+ * same gas budget the solver's relayer fee uses) with a 5% buffer over
18506
+ * the whole sum — strictly above the solver's unpadded requirement. The
18507
+ * wei cost used for the `value` field receives a 2% buffer.
18489
18508
  * 2. Yields `AWAITING_PLACE_ORDER` with `{ to, data, value, sessionPrivateKey }`.
18490
18509
  * The caller must sign the transaction and pass it back via `gen.next(signedTx)`.
18491
18510
  * 3. Yields `ORDER_PLACED` with the finalised order and transaction hash once
@@ -18522,11 +18541,11 @@ var IntentGateway = class _IntentGateway {
18522
18541
  value = estimate.totalGasCostWei + estimate.totalGasCostWei * 2n / 100n;
18523
18542
  const crossChainFeeBump = isSameChain ? 0n : await convertGasToFeeToken(
18524
18543
  this.ctx,
18525
- CROSS_CHAIN_ORDER_FEE_GAS_BUMP,
18544
+ RELAYER_MESSAGE_GAS,
18526
18545
  "source",
18527
18546
  this.source.config.stateMachineId
18528
18547
  );
18529
- executionOrder.fees = isSameChain ? estimate.totalGasInFeeToken * 2n : estimate.totalGasInFeeToken + (crossChainFeeBump + estimate.totalGasInFeeToken * 1n / 100n);
18548
+ executionOrder.fees = isSameChain ? estimate.totalGasInFeeToken * 2n : (estimate.totalGasInFeeToken + crossChainFeeBump) * 105n / 100n;
18530
18549
  }
18531
18550
  const placeOrderGen = this.orderPlacer.placeOrder(executionOrder, graffiti);
18532
18551
  const placeOrderFirst = await placeOrderGen.next();