@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.
@@ -2682,7 +2682,8 @@ var chainConfigs = {
2682
2682
  ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2683
2683
  EURC: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
2684
2684
  XSGD: "0x70e8dE73cE538DA2bEEd35d14187F6959a8ecA96",
2685
- TRYB: "0x2C537E5624e4af88A7ae4060C022609376C8D0EB"
2685
+ TRYB: "0x2C537E5624e4af88A7ae4060C022609376C8D0EB",
2686
+ USDR: "0x9623DfB044D5612Ce0c0F1606973CCAEFd03CD05"
2686
2687
  },
2687
2688
  tokenDecimals: {
2688
2689
  USDC: 6,
@@ -2721,6 +2722,10 @@ var chainConfigs = {
2721
2722
  consensusStateId: "ETH0",
2722
2723
  coingeckoId: "ethereum",
2723
2724
  layerZeroEid: 30101,
2725
+ erc4626Vaults: [
2726
+ { label: "Aave stataUSDC", address: "0xD4fa2D31b7968E448877f69A96DE69f5de8cD23E", asset: "USDC" },
2727
+ { label: "Aave stataUSDT", address: "0x7Bc3485026Ac48b6cf9BaF0A377477Fff5703Af8", asset: "USDT" }
2728
+ ],
2724
2729
  popularTokens: [
2725
2730
  "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
2726
2731
  "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
@@ -2775,6 +2780,10 @@ var chainConfigs = {
2775
2780
  defaultRpcUrl: "https://binance.llamarpc.com",
2776
2781
  consensusStateId: "BSC0",
2777
2782
  coingeckoId: "binance-smart-chain",
2783
+ erc4626Vaults: [
2784
+ { label: "Aave stataUSDC", address: "0x3906cDdfb781f02B21f21BD81ed7Fd8DC37075E1", asset: "USDC" },
2785
+ { label: "Aave stataUSDT", address: "0x0471D185cc7Be61E154277cAB2396cD397663da6", asset: "USDT" }
2786
+ ],
2778
2787
  popularTokens: [
2779
2788
  "0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82",
2780
2789
  "0x000Ae314E2A2172a039B26378814C252734f556A",
@@ -2834,6 +2843,10 @@ var chainConfigs = {
2834
2843
  consensusStateId: "ETH0",
2835
2844
  coingeckoId: "arbitrum-one",
2836
2845
  layerZeroEid: 30110,
2846
+ erc4626Vaults: [
2847
+ { label: "Aave stataUSDC", address: "0x7F6501d3B98eE91f9b9535E4b0ac710Fb0f9e0bc", asset: "USDC" },
2848
+ { label: "Aave stataUSDT", address: "0xa6D12574eFB239FC1D2099732bd8b5dC6306897F", asset: "USDT" }
2849
+ ],
2837
2850
  popularTokens: [
2838
2851
  "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
2839
2852
  "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
@@ -2854,7 +2867,8 @@ var chainConfigs = {
2854
2867
  EXT: "0x0e668E5127087e236578893a0e01E41837A28469",
2855
2868
  cNGN: "0x46C85152bFe9f96829aA94755D9f915F9B10EF5F",
2856
2869
  ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2857
- EURC: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42"
2870
+ EURC: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
2871
+ USDR: "0x3B5F2810fB2168FfA9C73160F97BF9f2461fFa5c"
2858
2872
  },
2859
2873
  tokenDecimals: {
2860
2874
  USDC: 6,
@@ -2896,6 +2910,10 @@ var chainConfigs = {
2896
2910
  coingeckoId: "base",
2897
2911
  layerZeroEid: 30184,
2898
2912
  uniswapV4Pools: [{ tokens: ["USDC", "cNGN"], fee: 1500, tickSpacing: 30 }],
2913
+ erc4626Vaults: [
2914
+ { label: "Aave stataUSDC", address: "0xC768c589647798a6EE01A91FdE98EF2ed046DBD6", asset: "USDC" },
2915
+ { label: "Yield Bearing cNGN", address: "0xa82A3531021317240Fb32E67f9c7bC091F737D3b", asset: "cNGN" }
2916
+ ],
2899
2917
  popularTokens: [
2900
2918
  "0x4200000000000000000000000000000000000006",
2901
2919
  "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
@@ -2916,7 +2934,8 @@ var chainConfigs = {
2916
2934
  EXT: "0x7C8c11ADb8EF7cd3CFa718008Ea048445C6E7209",
2917
2935
  cNGN: "0x52828daa48C1a9A06F37500882b42daf0bE04C3B",
2918
2936
  ZARP: "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
2919
- XSGD: "0xDC3326e71D45186F113a2F448984CA0e8D201995"
2937
+ XSGD: "0xDC3326e71D45186F113a2F448984CA0e8D201995",
2938
+ USDR: "0x3B5F2810fB2168FfA9C73160F97BF9f2461fFa5c"
2920
2939
  },
2921
2940
  tokenDecimals: {
2922
2941
  USDC: 6,
@@ -2956,6 +2975,10 @@ var chainConfigs = {
2956
2975
  consensusStateId: "POLY",
2957
2976
  coingeckoId: "polygon-pos",
2958
2977
  layerZeroEid: 30109,
2978
+ erc4626Vaults: [
2979
+ { label: "Aave stataUSDC", address: "0x79261231698B26Ed9085b59ae89d59843Ae925a8", asset: "USDC" },
2980
+ { label: "Aave stataUSDT", address: "0x2eaD203C5C1C00612B1DdbBb20e4180dA822d6ff", asset: "USDT" }
2981
+ ],
2959
2982
  popularTokens: [
2960
2983
  "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
2961
2984
  "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
@@ -3002,7 +3025,7 @@ var chainConfigs = {
3002
3025
  WETH: "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9",
3003
3026
  //wmatic, change it to wpol
3004
3027
  DAI: "0x0000000000000000000000000000000000000000",
3005
- USDC: "0x693b854d6965ffeaae21c74049dea644b56fcacb",
3028
+ USDC: "0xBE97E73126D66188d72fbF99029126D0340a7f18",
3006
3029
  USDT: "0x0000000000000000000000000000000000000000"
3007
3030
  },
3008
3031
  tokenDecimals: {
@@ -3011,7 +3034,7 @@ var chainConfigs = {
3011
3034
  },
3012
3035
  tokenStorageSlots: {
3013
3036
  USDT: { balanceSlot: 0, allowanceSlot: 1 },
3014
- USDC: { balanceSlot: 1, allowanceSlot: 2 }
3037
+ USDC: { balanceSlot: 0, allowanceSlot: 1 }
3015
3038
  },
3016
3039
  addresses: {
3017
3040
  IntentGateway: "0x6CF42FA9BecbC5b6a26884964956b113530f7cFA",
@@ -5177,6 +5200,19 @@ var ChainConfigService = class {
5177
5200
  getExtDecimals(chain) {
5178
5201
  return this.getConfig(chain)?.tokenDecimals?.EXT;
5179
5202
  }
5203
+ /** Configured exotic (non-USD) tokens on a chain, for selection UIs. EXT is a test asset and is not offered. */
5204
+ getKnownExoticTokens(chain) {
5205
+ const config = this.getConfig(chain);
5206
+ const tokens = [];
5207
+ if (config?.assets?.cNGN) {
5208
+ tokens.push({ symbol: "cNGN", address: config.assets.cNGN, decimals: config.tokenDecimals?.cNGN });
5209
+ }
5210
+ return tokens;
5211
+ }
5212
+ /** Known ERC-4626 treasury vaults on a chain, for selection UIs. */
5213
+ getKnownVaults(chain) {
5214
+ return this.getConfig(chain)?.erc4626Vaults ?? [];
5215
+ }
5180
5216
  getChainId(chain) {
5181
5217
  return this.getConfig(chain)?.chainId ?? 0;
5182
5218
  }
@@ -7391,29 +7427,28 @@ var SubstrateChain = class _SubstrateChain {
7391
7427
  return BigInt(latestHeight);
7392
7428
  }
7393
7429
  /**
7394
- * Get the state machine update time for a given state machine height.
7430
+ * Get the state machine update time for a given state machine height. Reads the
7431
+ * `BoundedStateMachineUpdateTime` map in pallet-ismp directly, so the height is either
7432
+ * still retained on-chain or it has been evicted — there's no intermediate RPC to
7433
+ * reinterpret the absence.
7395
7434
  * @param {StateMachineHeight} stateMachineHeight - The state machine height.
7396
7435
  * @returns {Promise<bigint>} The statemachine update time in seconds.
7397
7436
  */
7398
7437
  async stateMachineUpdateTime(stateMachineHeight) {
7399
- const state_id = convertStateIdToStateMachineId(stateMachineHeight.id.stateId);
7400
- const stateMachineId = {
7401
- state_id,
7402
- consensus_state_id: stateMachineHeight.id.consensusStateId
7403
- };
7404
- const payload = {
7405
- id: stateMachineId,
7406
- height: Number(stateMachineHeight.height)
7438
+ if (!this.api) throw new Error("API not initialized");
7439
+ const id = {
7440
+ stateId: stateMachineHeight.id.stateId,
7441
+ // on-chain StateMachineId encodes consensusStateId as [u8; 4]
7442
+ consensusStateId: toHex(toBytes(stateMachineHeight.id.consensusStateId))
7407
7443
  };
7408
- try {
7409
- const updateTime = await this.rpcClient.call("ismp_queryStateMachineUpdateTime", [payload]);
7410
- return BigInt(updateTime);
7411
- } catch (error) {
7412
- if (MissingConsensusUpdateTimeError.isError(error)) {
7413
- throw new MissingConsensusUpdateTimeError(MISSING_CONSENSUS_UPDATE_TIME_MESSAGE, { cause: error });
7414
- }
7415
- throw error;
7444
+ const updateTime = await this.api.query.ismp.boundedStateMachineUpdateTime(
7445
+ id,
7446
+ Number(stateMachineHeight.height)
7447
+ );
7448
+ if (updateTime.isNone) {
7449
+ throw new MissingConsensusUpdateTimeError(MISSING_CONSENSUS_UPDATE_TIME_MESSAGE);
7416
7450
  }
7451
+ return BigInt(updateTime.unwrap().toString());
7417
7452
  }
7418
7453
  /**
7419
7454
  * Get the challenge period for a given state machine id.
@@ -7467,17 +7502,30 @@ var SubstrateChain = class _SubstrateChain {
7467
7502
  rotationMap.set(BigInt(setId.toString()), BigInt(height.toString()));
7468
7503
  }
7469
7504
  const proofs = [];
7470
- let epoch = currentEpoch - 1n;
7505
+ let epoch = currentEpoch + 1n;
7471
7506
  while (rotationMap.has(epoch)) {
7472
- const height = rotationMap.get(epoch);
7473
- const key = beefyOffchainKey(height);
7474
- const proof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", key]);
7507
+ const key = rotationOffchainKey(epoch);
7508
+ let proof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", key]);
7509
+ if (!proof) {
7510
+ const legacyKey = messagingOffchainKey(rotationMap.get(epoch));
7511
+ proof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", legacyKey]);
7512
+ }
7475
7513
  if (!proof) return void 0;
7476
7514
  proofs.push(proof);
7477
7515
  epoch++;
7478
7516
  }
7479
- const messagingKey = beefyOffchainKey(lastProvenHeight);
7480
- const messagingProof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", messagingKey]);
7517
+ const messagingKey = messagingOffchainKey(lastProvenHeight);
7518
+ let messagingProof = await this.rpcClient.call("offchain_localStorageGet", ["PERSISTENT", messagingKey]);
7519
+ if (!messagingProof) {
7520
+ for (const [setId, height] of rotationMap) {
7521
+ if (height !== lastProvenHeight) continue;
7522
+ messagingProof = await this.rpcClient.call("offchain_localStorageGet", [
7523
+ "PERSISTENT",
7524
+ rotationOffchainKey(setId)
7525
+ ]);
7526
+ break;
7527
+ }
7528
+ }
7481
7529
  if (!messagingProof) return void 0;
7482
7530
  proofs.push(messagingProof);
7483
7531
  return { proofs, provenHeight: lastProvenHeight };
@@ -7494,11 +7542,17 @@ var SubstrateChain = class _SubstrateChain {
7494
7542
  throw new Error(`${name} not found in runtime`);
7495
7543
  }
7496
7544
  };
7497
- function beefyOffchainKey(provenHeight) {
7498
- const prefix = new TextEncoder().encode("beefy_consensus_proofs::");
7499
- const heightBytes = new Uint8Array(8);
7500
- new DataView(heightBytes.buffer).setBigUint64(0, provenHeight, false);
7501
- return toHex(new Uint8Array([...prefix, ...heightBytes]));
7545
+ function beefyOffchainKey(prefix, id) {
7546
+ const prefixBytes = new TextEncoder().encode(prefix);
7547
+ const idBytes = new Uint8Array(8);
7548
+ new DataView(idBytes.buffer).setBigUint64(0, id, false);
7549
+ return toHex(new Uint8Array([...prefixBytes, ...idBytes]));
7550
+ }
7551
+ function messagingOffchainKey(provenHeight) {
7552
+ return beefyOffchainKey("beefy_consensus_proofs::", provenHeight);
7553
+ }
7554
+ function rotationOffchainKey(setId) {
7555
+ return beefyOffchainKey("beefy_consensus_proofs::rotation::", setId);
7502
7556
  }
7503
7557
  function requestCommitmentStorageKey(key) {
7504
7558
  const prefix = new TextEncoder().encode("RequestCommitments");
@@ -15386,14 +15440,21 @@ function orderCommitment(order) {
15386
15440
  const encoded = encodeAbiParameters([orderType], [transformOrderForContract(order)]);
15387
15441
  return keccak256(encoded);
15388
15442
  }
15389
- async function convertGasToFeeToken(ctx, gasEstimate, gasEstimateIn, evmChainID, gasPriceOverride) {
15443
+ function bumpGasPrice(gasPrice, bumpPercent) {
15444
+ if (bumpPercent < 0n) {
15445
+ throw new Error("Gas price bump percent cannot be negative");
15446
+ }
15447
+ return (gasPrice * (100n + bumpPercent) + 99n) / 100n;
15448
+ }
15449
+ async function convertGasToFeeToken(ctx, gasEstimate, gasEstimateIn, evmChainID, gasPriceOverride, gasPriceBumpPercent = 0n) {
15390
15450
  if (TESTNET_CHAINS.has(evmChainID)) return 1n;
15391
15451
  const chain = ctx[gasEstimateIn];
15392
15452
  const client = chain.client;
15393
- const gasPrice = gasPriceOverride ?? await retryPromise(() => client.getGasPrice(), {
15453
+ const baseGasPrice = gasPriceOverride ?? await retryPromise(() => client.getGasPrice(), {
15394
15454
  maxRetries: 3,
15395
15455
  backoffMs: 250
15396
15456
  });
15457
+ const gasPrice = bumpGasPrice(baseGasPrice, gasPriceBumpPercent);
15397
15458
  const gasCostInWei = gasEstimate * gasPrice;
15398
15459
  const wethAddr = chain.configService.getWrappedNativeAssetWithDecimals(evmChainID).asset;
15399
15460
  const feeToken = await getFeeToken(ctx, evmChainID, chain);
@@ -15894,6 +15955,12 @@ var OrderCanceller = class _OrderCanceller {
15894
15955
  static DEFAULT_MAX_RECOVERY_RESTARTS = 1;
15895
15956
  static PROOF_FRESHNESS_MAX_RETRIES = 3;
15896
15957
  static PROOF_FRESHNESS_BACKOFF_MS = 500;
15958
+ /**
15959
+ * Gas budget used to size the relayer fee for a cross-chain cancellation
15960
+ * message (the GET response or RefundEscrow POST executed on the source
15961
+ * chain), priced at the source chain's gas price.
15962
+ */
15963
+ static CANCEL_MESSAGE_GAS = 800000n;
15897
15964
  logger = createConsola({
15898
15965
  level: LogLevels.info,
15899
15966
  formatOptions: { columns: 80, colors: true, compact: true, date: false }
@@ -15952,9 +16019,14 @@ var OrderCanceller = class _OrderCanceller {
15952
16019
  timeoutTimestamp: 0n,
15953
16020
  context
15954
16021
  };
15955
- const feeInSourceFeeToken = await convertGasToFeeToken(this.ctx, 400000n, "source", sourceStateMachine);
16022
+ const feeInSourceFeeToken = await convertGasToFeeToken(
16023
+ this.ctx,
16024
+ _OrderCanceller.CANCEL_MESSAGE_GAS,
16025
+ "source",
16026
+ sourceStateMachine
16027
+ );
15956
16028
  const relayerFee = feeInSourceFeeToken * 1005n / 1000n;
15957
- const nativeValue = await this.ctx.source.quoteNative(getRequest, relayerFee);
16029
+ const nativeValue = await this.ctx.source.quoteNative(getRequest, relayerFee) * 101n / 100n;
15958
16030
  return { nativeValue, relayerFee };
15959
16031
  }
15960
16032
  /**
@@ -16213,7 +16285,7 @@ var OrderCanceller = class _OrderCanceller {
16213
16285
  body,
16214
16286
  timeoutTimestamp: 0n
16215
16287
  };
16216
- const nativeValue = await this.ctx.dest.quoteNative(postRequest, relayerFee);
16288
+ const nativeValue = await this.ctx.dest.quoteNative(postRequest, relayerFee) * 101n / 100n;
16217
16289
  return { nativeValue, relayerFee };
16218
16290
  }
16219
16291
  /**
@@ -16543,8 +16615,12 @@ var OrderCanceller = class _OrderCanceller {
16543
16615
  * Converts estimated gas on the source chain into the dest chain's fee token.
16544
16616
  */
16545
16617
  async estimateRelayerFee(sourceChainId, destChainId) {
16546
- const POST_REQUEST_GAS = 400000n;
16547
- const feeInSourceFeeToken = await convertGasToFeeToken(this.ctx, POST_REQUEST_GAS, "source", sourceChainId);
16618
+ const feeInSourceFeeToken = await convertGasToFeeToken(
16619
+ this.ctx,
16620
+ _OrderCanceller.CANCEL_MESSAGE_GAS,
16621
+ "source",
16622
+ sourceChainId
16623
+ );
16548
16624
  const sourceFeeToken = await getFeeToken(this.ctx, sourceChainId, this.ctx.source);
16549
16625
  const destFeeToken = await getFeeToken(this.ctx, destChainId, this.ctx.dest);
16550
16626
  const feeInDestFeeToken = adjustDecimals(feeInSourceFeeToken, sourceFeeToken.decimals, destFeeToken.decimals);
@@ -17303,12 +17379,15 @@ var GasEstimator = class {
17303
17379
  *
17304
17380
  * @param params - Parameters including the order to estimate and optional
17305
17381
  * percentage bumps for `maxPriorityFeePerGas` and `maxFeePerGas`.
17382
+ * @param pricingOptions - Internal fee-pricing policy. Direct estimates use
17383
+ * the default zero gas-price bump; SDK order-fee quotes opt into headroom.
17306
17384
  * @returns A {@link FillOrderEstimate} containing all gas components,
17307
17385
  * EIP-1559 fee values, total cost in wei, and total cost in the source
17308
17386
  * chain's fee token.
17309
17387
  */
17310
- async estimateFillOrder(params) {
17388
+ async estimateFillOrder(params, pricingOptions = {}) {
17311
17389
  const { order } = params;
17390
+ const orderFeeGasPriceBumpPercent = pricingOptions.orderFeeGasPriceBumpPercent ?? 0n;
17312
17391
  const solverPrivateKey = generatePrivateKey();
17313
17392
  const solverAccountAddress = privateKeyToAddress(solverPrivateKey);
17314
17393
  const souceStateMachineId = isHex(order.source) ? hexToString$1(order.source) : order.source;
@@ -17339,7 +17418,12 @@ var GasEstimator = class {
17339
17418
  intentGatewayV2Address,
17340
17419
  entryPointAddress
17341
17420
  }),
17342
- isSameChain ? Promise.resolve({ postRequestFee: 0n, relayerFeeInSourceFeeToken: 0n }) : this.estimateCrossChainFees(sourceFeeToken, destFeeToken, souceStateMachineId)
17421
+ isSameChain ? Promise.resolve({ postRequestFee: 0n, relayerFeeInSourceFeeToken: 0n }) : this.estimateCrossChainFees(
17422
+ sourceFeeToken,
17423
+ destFeeToken,
17424
+ souceStateMachineId,
17425
+ orderFeeGasPriceBumpPercent
17426
+ )
17343
17427
  ]);
17344
17428
  const { viem: stateOverrides, bundler: bundlerStateOverrides } = stateOverridesResult;
17345
17429
  const fillOptions = {
@@ -17504,7 +17588,8 @@ var GasEstimator = class {
17504
17588
  totalGas,
17505
17589
  "dest",
17506
17590
  destStateMachineId,
17507
- gasPrice
17591
+ gasPrice,
17592
+ orderFeeGasPriceBumpPercent
17508
17593
  );
17509
17594
  const totalGasInSourceFeeToken = isSameChain ? totalGasInDestFeeToken : adjustDecimals(totalGasInDestFeeToken, destFeeToken.decimals, sourceFeeToken.decimals);
17510
17595
  const totalGasCostWei = isSameChain ? rawTotalGasCostWei : await convertFeeTokenToWei(this.ctx, totalGasInSourceFeeToken, "source", souceStateMachineId);
@@ -17528,12 +17613,14 @@ var GasEstimator = class {
17528
17613
  * The dispatch is always paid in the fee token — the native rail was
17529
17614
  * removed because it silently drew on a native balance nothing guarantees.
17530
17615
  */
17531
- async estimateCrossChainFees(sourceFeeToken, destFeeToken, sourceChainId) {
17616
+ async estimateCrossChainFees(sourceFeeToken, destFeeToken, sourceChainId, orderFeeGasPriceBumpPercent) {
17532
17617
  const postRequestFeeInSourceFeeToken = await convertGasToFeeToken(
17533
17618
  this.ctx,
17534
17619
  RELAYER_MESSAGE_GAS,
17535
17620
  "source",
17536
- sourceChainId
17621
+ sourceChainId,
17622
+ void 0,
17623
+ orderFeeGasPriceBumpPercent
17537
17624
  );
17538
17625
  const postRequestFeeInDestFeeToken = adjustDecimals(
17539
17626
  postRequestFeeInSourceFeeToken,
@@ -18303,6 +18390,7 @@ function isConfiguredAddress(address) {
18303
18390
  }
18304
18391
 
18305
18392
  // src/protocols/intents/IntentGateway.ts
18393
+ var CROSS_CHAIN_ORDER_FEE_GAS_PRICE_BUMP_PERCENT = 10n;
18306
18394
  var IntentGateway = class _IntentGateway {
18307
18395
  /** EVM chain on which orders are placed and escrowed. */
18308
18396
  source;
@@ -18489,14 +18577,22 @@ var IntentGateway = class _IntentGateway {
18489
18577
  * placement, fee estimation, bid collection, and execution.
18490
18578
  *
18491
18579
  * **Yield/receive protocol:**
18492
- * 1. If `order.fees` is unset or zero, estimates gas on an internal copy and
18493
- * sets same-chain fees to twice the estimate. Cross-chain orders attach
18494
- * (fill gas + a settlement-message uplift of `RELAYER_MESSAGE_GAS`, the
18495
- * same gas budget the solver's relayer fee uses) with a 5% buffer over
18496
- * the whole sum — strictly above the solver's unpadded requirement. The
18497
- * wei cost used for the `value` field receives a 2% buffer.
18498
- * 2. Yields `AWAITING_PLACE_ORDER` with `{ to, data, value, sessionPrivateKey }`.
18499
- * The caller must sign the transaction and pass it back via `gen.next(signedTx)`.
18580
+ * 1. If `order.fees` is unset or zero, prices the fee on an internal copy
18581
+ * via {@link quoteOrderFees}: same-chain fees are twice the fill-gas
18582
+ * estimate without a gas-price bump; cross-chain gas is priced 10% above
18583
+ * the live price before attaching (fill gas + the settlement relayer fee)
18584
+ * with a further 5% buffer over the whole sum — strictly above the solver's
18585
+ * unpadded requirement. Direct solver estimates remain unbumped. The wei
18586
+ * cost used for the `value` field receives a 2% buffer.
18587
+ * 2. Yields `AWAITING_PLACE_ORDER` with `{ to, data, value, nativeFee,
18588
+ * feeTokenAmount, feeTokenAddress, sessionPrivateKey }`. `value` carries
18589
+ * only the order's native-token input amounts; `nativeFee` is the native
18590
+ * amount that funds `order.fees` (`0n` when the caller set `order.fees`);
18591
+ * `feeTokenAmount`/`feeTokenAddress` are the exact fee encoded in `data`
18592
+ * and the source-chain token it is charged in. To pay the fee in native
18593
+ * token, sign the transaction with `value + nativeFee`; with a fee-token
18594
+ * allowance, `value` alone. Pass the signed transaction back via
18595
+ * `gen.next(signedTx)`.
18500
18596
  * 3. Yields `ORDER_PLACED` with the finalised order and transaction hash once
18501
18597
  * the `OrderPlaced` event is confirmed.
18502
18598
  * 4. Delegates to {@link OrderExecutor.executeOrder} and forwards all
@@ -18517,33 +18613,33 @@ var IntentGateway = class _IntentGateway {
18517
18613
  */
18518
18614
  async *execute(order, graffiti = DEFAULT_GRAFFITI, options) {
18519
18615
  const executionOrder = { ...order };
18520
- let value;
18616
+ let nativeFee = 0n;
18521
18617
  if (!executionOrder.fees || executionOrder.fees === 0n) {
18522
- const estimate = await this.gasEstimator.estimateFillOrder({
18523
- order: executionOrder,
18618
+ const feesQuote = await this.quoteOrderFees(executionOrder, {
18524
18619
  maxPriorityFeePerGasBumpPercent: options?.maxPriorityFeePerGasBumpPercent,
18525
18620
  maxFeePerGasBumpPercent: options?.maxFeePerGasBumpPercent
18526
18621
  });
18527
- if (estimate.totalGasCostWei === 0n || estimate.totalGasInFeeToken === 0n) {
18528
- throw new Error("Gas estimation failed");
18529
- }
18530
- const isSameChain = this.source.config.stateMachineId === this.dest.config.stateMachineId;
18531
- value = estimate.totalGasCostWei + estimate.totalGasCostWei * 2n / 100n;
18532
- const crossChainFeeBump = isSameChain ? 0n : await convertGasToFeeToken(
18533
- this.ctx,
18534
- RELAYER_MESSAGE_GAS,
18535
- "source",
18536
- this.source.config.stateMachineId
18537
- );
18538
- executionOrder.fees = isSameChain ? estimate.totalGasInFeeToken * 2n : (estimate.totalGasInFeeToken + crossChainFeeBump) * 105n / 100n;
18622
+ nativeFee = feesQuote.nativeValue;
18623
+ executionOrder.fees = feesQuote.fees;
18539
18624
  }
18625
+ const value = executionOrder.inputs.filter((input) => bytes32ToBytes20(input.token) === ADDRESS_ZERO2).reduce((sum, input) => sum + input.amount, 0n);
18540
18626
  const placeOrderGen = this.orderPlacer.placeOrder(executionOrder, graffiti);
18541
18627
  const placeOrderFirst = await placeOrderGen.next();
18542
18628
  if (placeOrderFirst.done) {
18543
18629
  throw new Error("placeOrder generator completed without yielding");
18544
18630
  }
18545
18631
  const { to, data, sessionPrivateKey } = placeOrderFirst.value;
18546
- const signedTransaction = yield { status: "AWAITING_PLACE_ORDER", to, data, value, sessionPrivateKey };
18632
+ const { address: feeTokenAddress } = await getFeeToken(this.ctx, this.source.config.stateMachineId, this.source);
18633
+ const signedTransaction = yield {
18634
+ status: "AWAITING_PLACE_ORDER",
18635
+ to,
18636
+ data,
18637
+ value,
18638
+ nativeFee,
18639
+ sessionPrivateKey,
18640
+ feeTokenAmount: executionOrder.fees,
18641
+ feeTokenAddress
18642
+ };
18547
18643
  const placeOrderSecond = await placeOrderGen.next(signedTransaction);
18548
18644
  if (placeOrderSecond.done === false) {
18549
18645
  throw new Error("placeOrder generator yielded unexpectedly after signing");
@@ -18843,6 +18939,50 @@ var IntentGateway = class _IntentGateway {
18843
18939
  async estimateFillOrder(params) {
18844
18940
  return this.gasEstimator.estimateFillOrder(params);
18845
18941
  }
18942
+ /**
18943
+ * Quotes the solver fee for an order using the same policy {@link execute} /
18944
+ * {@link executeBest} apply when `order.fees` is `0n`.
18945
+ *
18946
+ * Use this before placing to display the fee, or to check what the user can
18947
+ * afford: pay `fees` in the source-chain fee token (check balance and
18948
+ * allowance, then set `order.fees` and submit with `value: 0`), or leave
18949
+ * `order.fees` at `0n` and let the SDK attach `nativeValue` to the placement
18950
+ * transaction (check the native balance).
18951
+ *
18952
+ * @param order - The order to quote. `order.fees` is ignored and not mutated.
18953
+ * Gas prices used to derive cross-chain `fees` receive 10% SDK-only headroom.
18954
+ * Same-chain quotes and direct calls to {@link estimateFillOrder}, including
18955
+ * Simplex solver estimates, remain unbumped.
18956
+ *
18957
+ * @param options - Optional transaction gas-price bump percentages, as in {@link execute}.
18958
+ * @returns An {@link OrderFeesQuote} with the fee-token amount, the native
18959
+ * value for the native rail, and the underlying estimate.
18960
+ * @throws If gas estimation returns zero.
18961
+ */
18962
+ async quoteOrderFees(order, options) {
18963
+ const isSameChain = this.source.config.stateMachineId === this.dest.config.stateMachineId;
18964
+ const estimate = await this.gasEstimator.estimateFillOrder(
18965
+ {
18966
+ order,
18967
+ maxPriorityFeePerGasBumpPercent: options?.maxPriorityFeePerGasBumpPercent,
18968
+ maxFeePerGasBumpPercent: options?.maxFeePerGasBumpPercent
18969
+ },
18970
+ {
18971
+ orderFeeGasPriceBumpPercent: isSameChain ? 0n : CROSS_CHAIN_ORDER_FEE_GAS_PRICE_BUMP_PERCENT
18972
+ }
18973
+ );
18974
+ if (estimate.totalGasCostWei === 0n || estimate.totalGasInFeeToken === 0n) {
18975
+ throw new Error("Gas estimation failed");
18976
+ }
18977
+ const fees = isSameChain ? estimate.totalGasInFeeToken * 2n : (estimate.totalGasInFeeToken + estimate.relayerFeeInSourceFeeToken) * 105n / 100n;
18978
+ const { address: feeToken } = await this.source.getFeeTokenWithDecimals();
18979
+ return {
18980
+ fees,
18981
+ nativeValue: estimate.totalGasCostWei + estimate.totalGasCostWei * 2n / 100n,
18982
+ feeToken,
18983
+ estimate
18984
+ };
18985
+ }
18846
18986
  /**
18847
18987
  * Encodes a list of calls into ERC-7821 `execute` calldata using
18848
18988
  * single-batch mode.