@hyperbridge/sdk 2.6.0 → 2.7.2

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.
@@ -2732,7 +2732,8 @@ var chainConfigs = {
2732
2732
  ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2733
2733
  EURC: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
2734
2734
  XSGD: "0x70e8dE73cE538DA2bEEd35d14187F6959a8ecA96",
2735
- TRYB: "0x2C537E5624e4af88A7ae4060C022609376C8D0EB"
2735
+ TRYB: "0x2C537E5624e4af88A7ae4060C022609376C8D0EB",
2736
+ USDR: "0x9623DfB044D5612Ce0c0F1606973CCAEFd03CD05"
2736
2737
  },
2737
2738
  tokenDecimals: {
2738
2739
  USDC: 6,
@@ -2771,6 +2772,10 @@ var chainConfigs = {
2771
2772
  consensusStateId: "ETH0",
2772
2773
  coingeckoId: "ethereum",
2773
2774
  layerZeroEid: 30101,
2775
+ erc4626Vaults: [
2776
+ { label: "Aave stataUSDC", address: "0xD4fa2D31b7968E448877f69A96DE69f5de8cD23E", asset: "USDC" },
2777
+ { label: "Aave stataUSDT", address: "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8", asset: "USDT" }
2778
+ ],
2774
2779
  popularTokens: [
2775
2780
  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
2776
2781
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
@@ -2825,6 +2830,10 @@ var chainConfigs = {
2825
2830
  defaultRpcUrl: "https://binance.llamarpc.com",
2826
2831
  consensusStateId: "BSC0",
2827
2832
  coingeckoId: "binance-smart-chain",
2833
+ erc4626Vaults: [
2834
+ { label: "Aave stataUSDC", address: "0x3906cDdfb781f02B21f21BD81ed7Fd8DC37075E1", asset: "USDC" },
2835
+ { label: "Aave stataUSDT", address: "0x0471D185cc7Be61E154277cAB2396cD397663da6", asset: "USDT" }
2836
+ ],
2828
2837
  popularTokens: [
2829
2838
  "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82",
2830
2839
  "0x000Ae314E2A2172a039B26378814C252734f556A",
@@ -2884,6 +2893,10 @@ var chainConfigs = {
2884
2893
  consensusStateId: "ETH0",
2885
2894
  coingeckoId: "arbitrum-one",
2886
2895
  layerZeroEid: 30110,
2896
+ erc4626Vaults: [
2897
+ { label: "Aave stataUSDC", address: "0x7F6501d3B98eE91f9b9535E4b0ac710Fb0f9e0bc", asset: "USDC" },
2898
+ { label: "Aave stataUSDT", address: "0xa6D12574eFB239FC1D2099732bd8b5dC6306897F", asset: "USDT" }
2899
+ ],
2887
2900
  popularTokens: [
2888
2901
  "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
2889
2902
  "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
@@ -2904,7 +2917,8 @@ var chainConfigs = {
2904
2917
  EXT: "0x0e668E5127087e236578893a0e01E41837A28469",
2905
2918
  cNGN: "0x46C85152bFe9f96829aA94755D9f915F9B10EF5F",
2906
2919
  ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2907
- EURC: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
2920
+ EURC: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
2921
+ USDR: "0x3B5F2810fB2168FfA9C73160F97BF9f2461fFa5c"
2908
2922
  },
2909
2923
  tokenDecimals: {
2910
2924
  USDC: 6,
@@ -2946,6 +2960,10 @@ var chainConfigs = {
2946
2960
  coingeckoId: "base",
2947
2961
  layerZeroEid: 30184,
2948
2962
  uniswapV4Pools: [{ tokens: ["USDC", "cNGN"], fee: 1500, tickSpacing: 30 }],
2963
+ erc4626Vaults: [
2964
+ { label: "Aave stataUSDC", address: "0xC768c589647798a6EE01A91FdE98EF2ed046DBD6", asset: "USDC" },
2965
+ { label: "Yield Bearing cNGN", address: "0xa82A3531021317240Fb32E67f9c7bC091F737D3b", asset: "cNGN" }
2966
+ ],
2949
2967
  popularTokens: [
2950
2968
  "0x4200000000000000000000000000000000000006",
2951
2969
  "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
@@ -2966,7 +2984,8 @@ var chainConfigs = {
2966
2984
  EXT: "0x7C8c11ADb8EF7cd3CFa718008Ea048445C6E7209",
2967
2985
  cNGN: "0x52828daa48C1a9A06F37500882b42daf0bE04C3B",
2968
2986
  ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2969
- XSGD: "0xDC3326e71D45186F113a2F448984CA0e8D201995"
2987
+ XSGD: "0xDC3326e71D45186F113a2F448984CA0e8D201995",
2988
+ USDR: "0x3B5F2810fB2168FfA9C73160F97BF9f2461fFa5c"
2970
2989
  },
2971
2990
  tokenDecimals: {
2972
2991
  USDC: 6,
@@ -3006,6 +3025,10 @@ var chainConfigs = {
3006
3025
  consensusStateId: "POLY",
3007
3026
  coingeckoId: "polygon-pos",
3008
3027
  layerZeroEid: 30109,
3028
+ erc4626Vaults: [
3029
+ { label: "Aave stataUSDC", address: "0x79261231698B26Ed9085b59ae89d59843Ae925a8", asset: "USDC" },
3030
+ { label: "Aave stataUSDT", address: "0x2eaD203C5C1C00612B1DdbBb20e4180dA822d6ff", asset: "USDT" }
3031
+ ],
3009
3032
  popularTokens: [
3010
3033
  "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
3011
3034
  "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
@@ -3052,7 +3075,7 @@ var chainConfigs = {
3052
3075
  WETH: "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9",
3053
3076
  //wmatic, change it to wpol
3054
3077
  DAI: "0x0000000000000000000000000000000000000000",
3055
- USDC: "0x693b854d6965ffeaae21c74049dea644b56fcacb",
3078
+ USDC: "0xBE97E73126D66188d72fbF99029126D0340a7f18",
3056
3079
  USDT: "0x0000000000000000000000000000000000000000"
3057
3080
  },
3058
3081
  tokenDecimals: {
@@ -3061,7 +3084,7 @@ var chainConfigs = {
3061
3084
  },
3062
3085
  tokenStorageSlots: {
3063
3086
  USDT: { balanceSlot: 0, allowanceSlot: 1 },
3064
- USDC: { balanceSlot: 1, allowanceSlot: 2 }
3087
+ USDC: { balanceSlot: 0, allowanceSlot: 1 }
3065
3088
  },
3066
3089
  addresses: {
3067
3090
  IntentGateway: "0x6CF42FA9BecbC5b6a26884964956b113530f7cFA",
@@ -5227,6 +5250,19 @@ var ChainConfigService = class {
5227
5250
  getExtDecimals(chain) {
5228
5251
  return this.getConfig(chain)?.tokenDecimals?.EXT;
5229
5252
  }
5253
+ /** Configured exotic (non-USD) tokens on a chain, for selection UIs. EXT is a test asset and is not offered. */
5254
+ getKnownExoticTokens(chain) {
5255
+ const config = this.getConfig(chain);
5256
+ const tokens = [];
5257
+ if (config?.assets?.cNGN) {
5258
+ tokens.push({ symbol: "cNGN", address: config.assets.cNGN, decimals: config.tokenDecimals?.cNGN });
5259
+ }
5260
+ return tokens;
5261
+ }
5262
+ /** Known ERC-4626 treasury vaults on a chain, for selection UIs. */
5263
+ getKnownVaults(chain) {
5264
+ return this.getConfig(chain)?.erc4626Vaults ?? [];
5265
+ }
5230
5266
  getChainId(chain) {
5231
5267
  return this.getConfig(chain)?.chainId ?? 0;
5232
5268
  }
@@ -7441,29 +7477,28 @@ var SubstrateChain = class _SubstrateChain {
7441
7477
  return BigInt(latestHeight);
7442
7478
  }
7443
7479
  /**
7444
- * Get the state machine update time for a given state machine height.
7480
+ * Get the state machine update time for a given state machine height. Reads the
7481
+ * `BoundedStateMachineUpdateTime` map in pallet-ismp directly, so the height is either
7482
+ * still retained on-chain or it has been evicted — there's no intermediate RPC to
7483
+ * reinterpret the absence.
7445
7484
  * @param {StateMachineHeight} stateMachineHeight - The state machine height.
7446
7485
  * @returns {Promise<bigint>} The statemachine update time in seconds.
7447
7486
  */
7448
7487
  async stateMachineUpdateTime(stateMachineHeight) {
7449
- const state_id = convertStateIdToStateMachineId(stateMachineHeight.id.stateId);
7450
- const stateMachineId = {
7451
- state_id,
7452
- consensus_state_id: stateMachineHeight.id.consensusStateId
7453
- };
7454
- const payload = {
7455
- id: stateMachineId,
7456
- height: Number(stateMachineHeight.height)
7488
+ if (!this.api) throw new Error("API not initialized");
7489
+ const id = {
7490
+ stateId: stateMachineHeight.id.stateId,
7491
+ // on-chain StateMachineId encodes consensusStateId as [u8; 4]
7492
+ consensusStateId: toHex(toBytes(stateMachineHeight.id.consensusStateId))
7457
7493
  };
7458
- try {
7459
- const updateTime = await this.rpcClient.call("ismp_queryStateMachineUpdateTime", [payload]);
7460
- return BigInt(updateTime);
7461
- } catch (error) {
7462
- if (MissingConsensusUpdateTimeError.isError(error)) {
7463
- throw new MissingConsensusUpdateTimeError(MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, { cause: error });
7464
- }
7465
- throw error;
7494
+ const updateTime = await this.api.query.ismp.boundedStateMachineUpdateTime(
7495
+ id,
7496
+ Number(stateMachineHeight.height)
7497
+ );
7498
+ if (updateTime.isNone) {
7499
+ throw new MissingConsensusUpdateTimeError(MISSING_CONSENSUS_UPDATE_TIME_MESSAGE);
7466
7500
  }
7501
+ return BigInt(updateTime.unwrap().toString());
7467
7502
  }
7468
7503
  /**
7469
7504
  * Get the challenge period for a given state machine id.
@@ -7517,17 +7552,30 @@ var SubstrateChain = class _SubstrateChain {
7517
7552
  rotationMap.set(BigInt(setId.toString()), BigInt(height.toString()));
7518
7553
  }
7519
7554
  const proofs = [];
7520
- let epoch = currentEpoch - 1n;
7555
+ let epoch = currentEpoch + 1n;
7521
7556
  while (rotationMap.has(epoch)) {
7522
- const height = rotationMap.get(epoch);
7523
- const key = beefyOffchainKey(height);
7524
- const proof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", key]);
7557
+ const key = rotationOffchainKey(epoch);
7558
+ let proof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", key]);
7559
+ if (!proof) {
7560
+ const legacyKey = messagingOffchainKey(rotationMap.get(epoch));
7561
+ proof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", legacyKey]);
7562
+ }
7525
7563
  if (!proof) return void 0;
7526
7564
  proofs.push(proof);
7527
7565
  epoch++;
7528
7566
  }
7529
- const messagingKey = beefyOffchainKey(lastProvenHeight);
7530
- const messagingProof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", messagingKey]);
7567
+ const messagingKey = messagingOffchainKey(lastProvenHeight);
7568
+ let messagingProof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", messagingKey]);
7569
+ if (!messagingProof) {
7570
+ for (const [setId, height] of rotationMap) {
7571
+ if (height !== lastProvenHeight) continue;
7572
+ messagingProof = await this.rpcClient.call("offchain_localStorageGet", [
7573
+ "PERSISTENT",
7574
+ rotationOffchainKey(setId)
7575
+ ]);
7576
+ break;
7577
+ }
7578
+ }
7531
7579
  if (!messagingProof) return void 0;
7532
7580
  proofs.push(messagingProof);
7533
7581
  return { proofs, provenHeight: lastProvenHeight };
@@ -7544,11 +7592,17 @@ var SubstrateChain = class _SubstrateChain {
7544
7592
  throw new Error(`${name} not found in runtime`);
7545
7593
  }
7546
7594
  };
7547
- function beefyOffchainKey(provenHeight) {
7548
- const prefix = new TextEncoder().encode("beefy_consensus_proofs::");
7549
- const heightBytes = new Uint8Array(8);
7550
- new DataView(heightBytes.buffer).setBigUint64(0, provenHeight, false);
7551
- return toHex(new Uint8Array([...prefix, ...heightBytes]));
7595
+ function beefyOffchainKey(prefix, id) {
7596
+ const prefixBytes = new TextEncoder().encode(prefix);
7597
+ const idBytes = new Uint8Array(8);
7598
+ new DataView(idBytes.buffer).setBigUint64(0, id, false);
7599
+ return toHex(new Uint8Array([...prefixBytes, ...idBytes]));
7600
+ }
7601
+ function messagingOffchainKey(provenHeight) {
7602
+ return beefyOffchainKey("beefy_consensus_proofs::", provenHeight);
7603
+ }
7604
+ function rotationOffchainKey(setId) {
7605
+ return beefyOffchainKey("beefy_consensus_proofs::rotation::", setId);
7552
7606
  }
7553
7607
  function requestCommitmentStorageKey(key) {
7554
7608
  const prefix = new TextEncoder().encode("RequestCommitments");
@@ -15446,14 +15500,21 @@ function orderCommitment(order) {
15446
15500
  const encoded = encodeAbiParameters([orderType], [transformOrderForContract(order)]);
15447
15501
  return keccak256(encoded);
15448
15502
  }
15449
- async function convertGasToFeeToken(ctx, gasEstimate, gasEstimateIn, evmChainID, gasPriceOverride) {
15503
+ function bumpGasPrice(gasPrice, bumpPercent) {
15504
+ if (bumpPercent < 0n) {
15505
+ throw new Error("Gas price bump percent cannot be negative");
15506
+ }
15507
+ return (gasPrice * (100n + bumpPercent) + 99n) / 100n;
15508
+ }
15509
+ async function convertGasToFeeToken(ctx, gasEstimate, gasEstimateIn, evmChainID, gasPriceOverride, gasPriceBumpPercent = 0n) {
15450
15510
  if (TESTNET_CHAINS.has(evmChainID)) return 1n;
15451
15511
  const chain = ctx[gasEstimateIn];
15452
15512
  const client = chain.client;
15453
- const gasPrice = gasPriceOverride ?? await retryPromise(() => client.getGasPrice(), {
15513
+ const baseGasPrice = gasPriceOverride ?? await retryPromise(() => client.getGasPrice(), {
15454
15514
  maxRetries: 3,
15455
15515
  backoffMs: 250
15456
15516
  });
15517
+ const gasPrice = bumpGasPrice(baseGasPrice, gasPriceBumpPercent);
15457
15518
  const gasCostInWei = gasEstimate * gasPrice;
15458
15519
  const wethAddr = chain.configService.getWrappedNativeAssetWithDecimals(evmChainID).asset;
15459
15520
  const feeToken = await getFeeToken(ctx, evmChainID, chain);
@@ -15954,6 +16015,12 @@ var OrderCanceller = class _OrderCanceller {
15954
16015
  static DEFAULT_MAX_RECOVERY_RESTARTS = 1;
15955
16016
  static PROOF_FRESHNESS_MAX_RETRIES = 3;
15956
16017
  static PROOF_FRESHNESS_BACKOFF_MS = 500;
16018
+ /**
16019
+ * Gas budget used to size the relayer fee for a cross-chain cancellation
16020
+ * message (the GET response or RefundEscrow POST executed on the source
16021
+ * chain), priced at the source chain's gas price.
16022
+ */
16023
+ static CANCEL_MESSAGE_GAS = 800000n;
15957
16024
  logger = createConsola({
15958
16025
  level: LogLevels.info,
15959
16026
  formatOptions: { columns: 80, colors: true, compact: true, date: false }
@@ -16012,9 +16079,14 @@ var OrderCanceller = class _OrderCanceller {
16012
16079
  timeoutTimestamp: 0n,
16013
16080
  context
16014
16081
  };
16015
- const feeInSourceFeeToken = await convertGasToFeeToken(this.ctx, 400000n, "source", sourceStateMachine);
16082
+ const feeInSourceFeeToken = await convertGasToFeeToken(
16083
+ this.ctx,
16084
+ _OrderCanceller.CANCEL_MESSAGE_GAS,
16085
+ "source",
16086
+ sourceStateMachine
16087
+ );
16016
16088
  const relayerFee = feeInSourceFeeToken * 1005n / 1000n;
16017
- const nativeValue = await this.ctx.source.quoteNative(getRequest, relayerFee);
16089
+ const nativeValue = await this.ctx.source.quoteNative(getRequest, relayerFee) * 101n / 100n;
16018
16090
  return { nativeValue, relayerFee };
16019
16091
  }
16020
16092
  /**
@@ -16273,7 +16345,7 @@ var OrderCanceller = class _OrderCanceller {
16273
16345
  body,
16274
16346
  timeoutTimestamp: 0n
16275
16347
  };
16276
- const nativeValue = await this.ctx.dest.quoteNative(postRequest, relayerFee);
16348
+ const nativeValue = await this.ctx.dest.quoteNative(postRequest, relayerFee) * 101n / 100n;
16277
16349
  return { nativeValue, relayerFee };
16278
16350
  }
16279
16351
  /**
@@ -16603,8 +16675,12 @@ var OrderCanceller = class _OrderCanceller {
16603
16675
  * Converts estimated gas on the source chain into the dest chain's fee token.
16604
16676
  */
16605
16677
  async estimateRelayerFee(sourceChainId, destChainId) {
16606
- const POST_REQUEST_GAS = 400000n;
16607
- const feeInSourceFeeToken = await convertGasToFeeToken(this.ctx, POST_REQUEST_GAS, "source", sourceChainId);
16678
+ const feeInSourceFeeToken = await convertGasToFeeToken(
16679
+ this.ctx,
16680
+ _OrderCanceller.CANCEL_MESSAGE_GAS,
16681
+ "source",
16682
+ sourceChainId
16683
+ );
16608
16684
  const sourceFeeToken = await getFeeToken(this.ctx, sourceChainId, this.ctx.source);
16609
16685
  const destFeeToken = await getFeeToken(this.ctx, destChainId, this.ctx.dest);
16610
16686
  const feeInDestFeeToken = adjustDecimals(feeInSourceFeeToken, sourceFeeToken.decimals, destFeeToken.decimals);
@@ -17363,12 +17439,15 @@ var GasEstimator = class {
17363
17439
  *
17364
17440
  * @param params - Parameters including the order to estimate and optional
17365
17441
  * percentage bumps for `maxPriorityFeePerGas` and `maxFeePerGas`.
17442
+ * @param pricingOptions - Internal fee-pricing policy. Direct estimates use
17443
+ * the default zero gas-price bump; SDK order-fee quotes opt into headroom.
17366
17444
  * @returns A {@link FillOrderEstimate} containing all gas components,
17367
17445
  * EIP-1559 fee values, total cost in wei, and total cost in the source
17368
17446
  * chain's fee token.
17369
17447
  */
17370
- async estimateFillOrder(params) {
17448
+ async estimateFillOrder(params, pricingOptions = {}) {
17371
17449
  const { order } = params;
17450
+ const orderFeeGasPriceBumpPercent = pricingOptions.orderFeeGasPriceBumpPercent ?? 0n;
17372
17451
  const solverPrivateKey = generatePrivateKey();
17373
17452
  const solverAccountAddress = privateKeyToAddress(solverPrivateKey);
17374
17453
  const souceStateMachineId = isHex(order.source) ? hexToString$1(order.source) : order.source;
@@ -17399,7 +17478,12 @@ var GasEstimator = class {
17399
17478
  intentGatewayV2Address,
17400
17479
  entryPointAddress
17401
17480
  }),
17402
- isSameChain ? Promise.resolve({ postRequestFee: 0n, relayerFeeInSourceFeeToken: 0n }) : this.estimateCrossChainFees(sourceFeeToken, destFeeToken, souceStateMachineId)
17481
+ isSameChain ? Promise.resolve({ postRequestFee: 0n, relayerFeeInSourceFeeToken: 0n }) : this.estimateCrossChainFees(
17482
+ sourceFeeToken,
17483
+ destFeeToken,
17484
+ souceStateMachineId,
17485
+ orderFeeGasPriceBumpPercent
17486
+ )
17403
17487
  ]);
17404
17488
  const { viem: stateOverrides, bundler: bundlerStateOverrides } = stateOverridesResult;
17405
17489
  const fillOptions = {
@@ -17564,7 +17648,8 @@ var GasEstimator = class {
17564
17648
  totalGas,
17565
17649
  "dest",
17566
17650
  destStateMachineId,
17567
- gasPrice
17651
+ gasPrice,
17652
+ orderFeeGasPriceBumpPercent
17568
17653
  );
17569
17654
  const totalGasInSourceFeeToken = isSameChain ? totalGasInDestFeeToken : adjustDecimals(totalGasInDestFeeToken, destFeeToken.decimals, sourceFeeToken.decimals);
17570
17655
  const totalGasCostWei = isSameChain ? rawTotalGasCostWei : await convertFeeTokenToWei(this.ctx, totalGasInSourceFeeToken, "source", souceStateMachineId);
@@ -17588,12 +17673,14 @@ var GasEstimator = class {
17588
17673
  * The dispatch is always paid in the fee token — the native rail was
17589
17674
  * removed because it silently drew on a native balance nothing guarantees.
17590
17675
  */
17591
- async estimateCrossChainFees(sourceFeeToken, destFeeToken, sourceChainId) {
17676
+ async estimateCrossChainFees(sourceFeeToken, destFeeToken, sourceChainId, orderFeeGasPriceBumpPercent) {
17592
17677
  const postRequestFeeInSourceFeeToken = await convertGasToFeeToken(
17593
17678
  this.ctx,
17594
17679
  RELAYER_MESSAGE_GAS,
17595
17680
  "source",
17596
- sourceChainId
17681
+ sourceChainId,
17682
+ void 0,
17683
+ orderFeeGasPriceBumpPercent
17597
17684
  );
17598
17685
  const postRequestFeeInDestFeeToken = adjustDecimals(
17599
17686
  postRequestFeeInSourceFeeToken,
@@ -18363,6 +18450,7 @@ function isConfiguredAddress(address) {
18363
18450
  }
18364
18451
 
18365
18452
  // src/protocols/intents/IntentGateway.ts
18453
+ var CROSS_CHAIN_ORDER_FEE_GAS_PRICE_BUMP_PERCENT = 10n;
18366
18454
  var IntentGateway = class _IntentGateway {
18367
18455
  /** EVM chain on which orders are placed and escrowed. */
18368
18456
  source;
@@ -18549,14 +18637,22 @@ var IntentGateway = class _IntentGateway {
18549
18637
  * placement, fee estimation, bid collection, and execution.
18550
18638
  *
18551
18639
  * **Yield/receive protocol:**
18552
- * 1. If `order.fees` is unset or zero, estimates gas on an internal copy and
18553
- * sets same-chain fees to twice the estimate. Cross-chain orders attach
18554
- * (fill gas + a settlement-message uplift of `RELAYER_MESSAGE_GAS`, the
18555
- * same gas budget the solver's relayer fee uses) with a 5% buffer over
18556
- * the whole sum — strictly above the solver's unpadded requirement. The
18557
- * wei cost used for the `value` field receives a 2% buffer.
18558
- * 2. Yields `AWAITING_PLACE_ORDER` with `{ to, data, value, sessionPrivateKey }`.
18559
- * The caller must sign the transaction and pass it back via `gen.next(signedTx)`.
18640
+ * 1. If `order.fees` is unset or zero, prices the fee on an internal copy
18641
+ * via {@link quoteOrderFees}: same-chain fees are twice the fill-gas
18642
+ * estimate without a gas-price bump; cross-chain gas is priced 10% above
18643
+ * the live price before attaching (fill gas + the settlement relayer fee)
18644
+ * with a further 5% buffer over the whole sum — strictly above the solver's
18645
+ * unpadded requirement. Direct solver estimates remain unbumped. The wei
18646
+ * cost used for the `value` field receives a 2% buffer.
18647
+ * 2. Yields `AWAITING_PLACE_ORDER` with `{ to, data, value, nativeFee,
18648
+ * feeTokenAmount, feeTokenAddress, sessionPrivateKey }`. `value` carries
18649
+ * only the order's native-token input amounts; `nativeFee` is the native
18650
+ * amount that funds `order.fees` (`0n` when the caller set `order.fees`);
18651
+ * `feeTokenAmount`/`feeTokenAddress` are the exact fee encoded in `data`
18652
+ * and the source-chain token it is charged in. To pay the fee in native
18653
+ * token, sign the transaction with `value + nativeFee`; with a fee-token
18654
+ * allowance, `value` alone. Pass the signed transaction back via
18655
+ * `gen.next(signedTx)`.
18560
18656
  * 3. Yields `ORDER_PLACED` with the finalised order and transaction hash once
18561
18657
  * the `OrderPlaced` event is confirmed.
18562
18658
  * 4. Delegates to {@link OrderExecutor.executeOrder} and forwards all
@@ -18577,33 +18673,33 @@ var IntentGateway = class _IntentGateway {
18577
18673
  */
18578
18674
  async *execute(order, graffiti = DEFAULT_GRAFFITI, options) {
18579
18675
  const executionOrder = { ...order };
18580
- let value;
18676
+ let nativeFee = 0n;
18581
18677
  if (!executionOrder.fees || executionOrder.fees === 0n) {
18582
- const estimate = await this.gasEstimator.estimateFillOrder({
18583
- order: executionOrder,
18678
+ const feesQuote = await this.quoteOrderFees(executionOrder, {
18584
18679
  maxPriorityFeePerGasBumpPercent: options?.maxPriorityFeePerGasBumpPercent,
18585
18680
  maxFeePerGasBumpPercent: options?.maxFeePerGasBumpPercent
18586
18681
  });
18587
- if (estimate.totalGasCostWei === 0n || estimate.totalGasInFeeToken === 0n) {
18588
- throw new Error("Gas estimation failed");
18589
- }
18590
- const isSameChain = this.source.config.stateMachineId === this.dest.config.stateMachineId;
18591
- value = estimate.totalGasCostWei + estimate.totalGasCostWei * 2n / 100n;
18592
- const crossChainFeeBump = isSameChain ? 0n : await convertGasToFeeToken(
18593
- this.ctx,
18594
- RELAYER_MESSAGE_GAS,
18595
- "source",
18596
- this.source.config.stateMachineId
18597
- );
18598
- executionOrder.fees = isSameChain ? estimate.totalGasInFeeToken * 2n : (estimate.totalGasInFeeToken + crossChainFeeBump) * 105n / 100n;
18682
+ nativeFee = feesQuote.nativeValue;
18683
+ executionOrder.fees = feesQuote.fees;
18599
18684
  }
18685
+ const value = executionOrder.inputs.filter((input) => bytes32ToBytes20(input.token) === ADDRESS_ZERO2).reduce((sum, input) => sum + input.amount, 0n);
18600
18686
  const placeOrderGen = this.orderPlacer.placeOrder(executionOrder, graffiti);
18601
18687
  const placeOrderFirst = await placeOrderGen.next();
18602
18688
  if (placeOrderFirst.done) {
18603
18689
  throw new Error("placeOrder generator completed without yielding");
18604
18690
  }
18605
18691
  const { to, data, sessionPrivateKey } = placeOrderFirst.value;
18606
- const signedTransaction = yield { status: "AWAITING_PLACE_ORDER", to, data, value, sessionPrivateKey };
18692
+ const { address: feeTokenAddress } = await getFeeToken(this.ctx, this.source.config.stateMachineId, this.source);
18693
+ const signedTransaction = yield {
18694
+ status: "AWAITING_PLACE_ORDER",
18695
+ to,
18696
+ data,
18697
+ value,
18698
+ nativeFee,
18699
+ sessionPrivateKey,
18700
+ feeTokenAmount: executionOrder.fees,
18701
+ feeTokenAddress
18702
+ };
18607
18703
  const placeOrderSecond = await placeOrderGen.next(signedTransaction);
18608
18704
  if (placeOrderSecond.done === false) {
18609
18705
  throw new Error("placeOrder generator yielded unexpectedly after signing");
@@ -18903,6 +18999,50 @@ var IntentGateway = class _IntentGateway {
18903
18999
  async estimateFillOrder(params) {
18904
19000
  return this.gasEstimator.estimateFillOrder(params);
18905
19001
  }
19002
+ /**
19003
+ * Quotes the solver fee for an order using the same policy {@link execute} /
19004
+ * {@link executeBest} apply when `order.fees` is `0n`.
19005
+ *
19006
+ * Use this before placing to display the fee, or to check what the user can
19007
+ * afford: pay `fees` in the source-chain fee token (check balance and
19008
+ * allowance, then set `order.fees` and submit with `value: 0`), or leave
19009
+ * `order.fees` at `0n` and let the SDK attach `nativeValue` to the placement
19010
+ * transaction (check the native balance).
19011
+ *
19012
+ * @param order - The order to quote. `order.fees` is ignored and not mutated.
19013
+ * Gas prices used to derive cross-chain `fees` receive 10% SDK-only headroom.
19014
+ * Same-chain quotes and direct calls to {@link estimateFillOrder}, including
19015
+ * Simplex solver estimates, remain unbumped.
19016
+ *
19017
+ * @param options - Optional transaction gas-price bump percentages, as in {@link execute}.
19018
+ * @returns An {@link OrderFeesQuote} with the fee-token amount, the native
19019
+ * value for the native rail, and the underlying estimate.
19020
+ * @throws If gas estimation returns zero.
19021
+ */
19022
+ async quoteOrderFees(order, options) {
19023
+ const isSameChain = this.source.config.stateMachineId === this.dest.config.stateMachineId;
19024
+ const estimate = await this.gasEstimator.estimateFillOrder(
19025
+ {
19026
+ order,
19027
+ maxPriorityFeePerGasBumpPercent: options?.maxPriorityFeePerGasBumpPercent,
19028
+ maxFeePerGasBumpPercent: options?.maxFeePerGasBumpPercent
19029
+ },
19030
+ {
19031
+ orderFeeGasPriceBumpPercent: isSameChain ? 0n : CROSS_CHAIN_ORDER_FEE_GAS_PRICE_BUMP_PERCENT
19032
+ }
19033
+ );
19034
+ if (estimate.totalGasCostWei === 0n || estimate.totalGasInFeeToken === 0n) {
19035
+ throw new Error("Gas estimation failed");
19036
+ }
19037
+ const fees = isSameChain ? estimate.totalGasInFeeToken * 2n : (estimate.totalGasInFeeToken + estimate.relayerFeeInSourceFeeToken) * 105n / 100n;
19038
+ const { address: feeToken } = await this.source.getFeeTokenWithDecimals();
19039
+ return {
19040
+ fees,
19041
+ nativeValue: estimate.totalGasCostWei + estimate.totalGasCostWei * 2n / 100n,
19042
+ feeToken,
19043
+ estimate
19044
+ };
19045
+ }
18906
19046
  /**
18907
19047
  * Encodes a list of calls into ERC-7821 `execute` calldata using
18908
19048
  * single-batch mode.