@hyperbridge/sdk 2.8.13 → 2.8.17

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.
@@ -6,14 +6,14 @@ var util$1 = require('util');
6
6
  var consola = require('consola');
7
7
  var viem = require('viem');
8
8
  var chains$1 = require('viem/chains');
9
- var tronweb = require('tronweb');
9
+ var utilCrypto = require('@polkadot/util-crypto');
10
10
  var lodashEs = require('lodash-es');
11
11
  var tsPattern = require('ts-pattern');
12
12
  var api = require('@polkadot/api');
13
13
  var scaleTs = require('scale-ts');
14
- var utilCrypto = require('@polkadot/util-crypto');
15
14
  var util = require('@polkadot/util');
16
15
  var PQueue = require('p-queue');
16
+ var tronweb = require('tronweb');
17
17
  var stdEnv = require('std-env');
18
18
  var mergeRace = require('@async-generator/merge-race');
19
19
  var graphqlRequest = require('graphql-request');
@@ -280,6 +280,7 @@ var OrderStatus = /* @__PURE__ */ ((OrderStatus2) => {
280
280
  OrderStatus2["FILLED"] = "FILLED";
281
281
  OrderStatus2["REDEEMED"] = "REDEEMED";
282
282
  OrderStatus2["REFUNDED"] = "REFUNDED";
283
+ OrderStatus2["CANCELLED"] = "CANCELLED";
283
284
  return OrderStatus2;
284
285
  })(OrderStatus || {});
285
286
  var TeleportStatus = /* @__PURE__ */ ((TeleportStatus2) => {
@@ -294,6 +295,8 @@ var RequestKind = /* @__PURE__ */ ((RequestKind2) => {
294
295
  RequestKind2[RequestKind2["UpdateParams"] = 2] = "UpdateParams";
295
296
  RequestKind2[RequestKind2["SweepDust"] = 3] = "SweepDust";
296
297
  RequestKind2[RequestKind2["RefundEscrow"] = 4] = "RefundEscrow";
298
+ RequestKind2[RequestKind2["Execute"] = 5] = "Execute";
299
+ RequestKind2[RequestKind2["RedeemEscrowPartial"] = 6] = "RedeemEscrowPartial";
297
300
  return RequestKind2;
298
301
  })(RequestKind || {});
299
302
  var IntentOrderStatus = Object.freeze({
@@ -306,6 +309,7 @@ var IntentOrderStatus = Object.freeze({
306
309
  BID_SELECTED: "BID_SELECTED",
307
310
  FILLED: "FILLED",
308
311
  PARTIAL_FILL: "PARTIAL_FILL",
312
+ CANCELLED: "CANCELLED",
309
313
  EXPIRED: "EXPIRED",
310
314
  FAILED: "FAILED"
311
315
  });
@@ -2488,7 +2492,12 @@ var ABI2 = [
2488
2492
  ];
2489
2493
  var handlerV2_default = { ABI: ABI2 };
2490
2494
  function tronAddress(base58) {
2491
- return `0x${tronweb.TronWeb.address.toHex(base58).slice(2)}`;
2495
+ const decoded = utilCrypto.base58Decode(base58);
2496
+ if (decoded.length !== 25 || decoded[0] !== 65) {
2497
+ throw new Error(`Not a Tron base58 address: ${base58}`);
2498
+ }
2499
+ const hex = Array.from(decoded.slice(1, 21), (byte) => byte.toString(16).padStart(2, "0")).join("");
2500
+ return `0x${hex}`;
2492
2501
  }
2493
2502
  var Chains = /* @__PURE__ */ ((Chains2) => {
2494
2503
  Chains2["BSC_CHAPEL"] = "EVM-97";
@@ -2575,11 +2584,13 @@ var chainConfigs = {
2575
2584
  WETH: "0xae13d989dac2f0debff460ac112a837c89baa7cd",
2576
2585
  DAI: "0x1938165569a5463327fb206be06d8d9253aa06b7",
2577
2586
  USDC: "0xA801da100bF16D07F668F4A49E1f71fc54D05177",
2578
- USDT: "0xc043f483373072f7f27420d6e7d7ad269c018e18"
2587
+ USDT: "0xc043f483373072f7f27420d6e7d7ad269c018e18",
2588
+ cNGN: "0x2bbbd701cfc25d37f18127e51df0933566d5778a"
2579
2589
  },
2580
2590
  tokenDecimals: {
2581
2591
  USDC: 18,
2582
- USDT: 18
2592
+ USDT: 18,
2593
+ cNGN: 6
2583
2594
  },
2584
2595
  tokenStorageSlots: {
2585
2596
  USDC: { balanceSlot: 1, allowanceSlot: 2 },
@@ -2596,7 +2607,7 @@ var chainConfigs = {
2596
2607
  UniswapV3Quoter: "0x0000000000000000000000000000000000000000",
2597
2608
  UniswapV4Quoter: "0x0000000000000000000000000000000000000000",
2598
2609
  EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
2599
- SolverAccount: "0x110C7E1814c923127469Ca8939a12dAa70B96a17"
2610
+ SolverAccount: "0x153DB990FE3b761B54ad71D0f1A0987dF11740DB"
2600
2611
  },
2601
2612
  rpcEnvKey: "BSC_CHAPEL",
2602
2613
  defaultRpcUrl: "https://bnb-testnet.api.onfinality.io/public",
@@ -2722,7 +2733,7 @@ var chainConfigs = {
2722
2733
  },
2723
2734
  addresses: {
2724
2735
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
2725
- SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
2736
+ SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
2726
2737
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
2727
2738
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
2728
2739
  UniswapRouter02: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
@@ -2789,7 +2800,7 @@ var chainConfigs = {
2789
2800
  },
2790
2801
  addresses: {
2791
2802
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
2792
- SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
2803
+ SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
2793
2804
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
2794
2805
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
2795
2806
  UniswapRouter02: "0x10ED43C718714eb63d5aA57B78B54704E256024E",
@@ -2850,7 +2861,7 @@ var chainConfigs = {
2850
2861
  },
2851
2862
  addresses: {
2852
2863
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
2853
- SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
2864
+ SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
2854
2865
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
2855
2866
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
2856
2867
  UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
@@ -2924,7 +2935,7 @@ var chainConfigs = {
2924
2935
  },
2925
2936
  addresses: {
2926
2937
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
2927
- SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
2938
+ SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
2928
2939
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
2929
2940
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
2930
2941
  UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
@@ -2949,7 +2960,6 @@ var chainConfigs = {
2949
2960
  consensusStateId: "ETH0",
2950
2961
  coingeckoId: "base",
2951
2962
  layerZeroEid: 30184,
2952
- uniswapV4Pools: [{ tokens: ["USDC", "cNGN"], fee: 1500, tickSpacing: 30 }],
2953
2963
  erc4626Vaults: [
2954
2964
  { label: "Aave stataUSDC", address: "0xC768c589647798a6EE01A91FdE98EF2ed046DBD6", asset: "USDC" },
2955
2965
  { label: "Yield Bearing cNGN", address: "0xa82A3531021317240Fb32E67f9c7bC091F737D3b", asset: "cNGN" }
@@ -2999,7 +3009,7 @@ var chainConfigs = {
2999
3009
  },
3000
3010
  addresses: {
3001
3011
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
3002
- SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
3012
+ SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
3003
3013
  TokenGateway: "0x8b536105b6Fae2aE9199f5146D3C57Dfe53b614E",
3004
3014
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
3005
3015
  UniswapRouter02: "0xd2f9496824951D5237cC71245D659E48d0d5f9E8",
@@ -3074,11 +3084,13 @@ var chainConfigs = {
3074
3084
  //wmatic, change it to wpol
3075
3085
  DAI: "0x0000000000000000000000000000000000000000",
3076
3086
  USDC: "0xBE97E73126D66188d72fbF99029126D0340a7f18",
3077
- USDT: "0x0000000000000000000000000000000000000000"
3087
+ USDT: "0x0000000000000000000000000000000000000000",
3088
+ cNGN: "0xe4ff5d2ae65c10f530c000215029919961e8a758"
3078
3089
  },
3079
3090
  tokenDecimals: {
3080
3091
  USDC: 18,
3081
- USDT: 18
3092
+ USDT: 18,
3093
+ cNGN: 6
3082
3094
  },
3083
3095
  tokenStorageSlots: {
3084
3096
  USDT: { balanceSlot: 0, allowanceSlot: 1 },
@@ -3091,7 +3103,7 @@ var chainConfigs = {
3091
3103
  Calldispatcher: "0x876F1891982E260026630c233A4897160A281Fb8",
3092
3104
  Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
3093
3105
  EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
3094
- SolverAccount: "0x110C7E1814c923127469Ca8939a12dAa70B96a17"
3106
+ SolverAccount: "0x153DB990FE3b761B54ad71D0f1A0987dF11740DB"
3095
3107
  },
3096
3108
  rpcEnvKey: "POLYGON_AMOY",
3097
3109
  defaultRpcUrl: "https://rpc-amoy.polygon.technology",
@@ -3115,7 +3127,7 @@ var chainConfigs = {
3115
3127
  },
3116
3128
  addresses: {
3117
3129
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
3118
- SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
3130
+ SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
3119
3131
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
3120
3132
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
3121
3133
  UniswapRouter02: "0x4A7b5Da61326A6379179b40d00F57E5bbDC962c2",
@@ -3150,7 +3162,7 @@ var chainConfigs = {
3150
3162
  },
3151
3163
  addresses: {
3152
3164
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
3153
- SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
3165
+ SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
3154
3166
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
3155
3167
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
3156
3168
  UniswapRouter02: "0xB2e26652e4BAd1e56055A051f922E06760cA0BFE",
@@ -3306,7 +3318,7 @@ var chainConfigs = {
3306
3318
  },
3307
3319
  addresses: {
3308
3320
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
3309
- SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
3321
+ SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
3310
3322
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
3311
3323
  Calldispatcher: "0xE2C7e576E26E0bE7aC97c6fE925bcDAbD87c4bEd"
3312
3324
  },
@@ -3353,7 +3365,12 @@ var ABI3 = [
3353
3365
  type: "constructor",
3354
3366
  inputs: [
3355
3367
  {
3356
- name: "owner",
3368
+ name: "intrinsic",
3369
+ type: "address",
3370
+ internalType: "address"
3371
+ },
3372
+ {
3373
+ name: "extrinsic",
3357
3374
  type: "address",
3358
3375
  internalType: "address"
3359
3376
  }
@@ -3452,8 +3469,8 @@ var ABI3 = [
3452
3469
  },
3453
3470
  {
3454
3471
  name: "",
3455
- type: "address",
3456
- internalType: "address"
3472
+ type: "uint256",
3473
+ internalType: "uint256"
3457
3474
  }
3458
3475
  ],
3459
3476
  outputs: [
@@ -3467,20 +3484,31 @@ var ABI3 = [
3467
3484
  },
3468
3485
  {
3469
3486
  type: "function",
3470
- name: "_owner",
3471
- inputs: [],
3487
+ name: "_partialFills",
3488
+ inputs: [
3489
+ {
3490
+ name: "",
3491
+ type: "bytes32",
3492
+ internalType: "bytes32"
3493
+ },
3494
+ {
3495
+ name: "",
3496
+ type: "uint256",
3497
+ internalType: "uint256"
3498
+ }
3499
+ ],
3472
3500
  outputs: [
3473
3501
  {
3474
3502
  name: "",
3475
- type: "address",
3476
- internalType: "address"
3503
+ type: "uint256",
3504
+ internalType: "uint256"
3477
3505
  }
3478
3506
  ],
3479
3507
  stateMutability: "view"
3480
3508
  },
3481
3509
  {
3482
3510
  type: "function",
3483
- name: "_partialFills",
3511
+ name: "_protocolFees",
3484
3512
  inputs: [
3485
3513
  {
3486
3514
  name: "",
@@ -3489,19 +3517,31 @@ var ABI3 = [
3489
3517
  },
3490
3518
  {
3491
3519
  name: "",
3492
- type: "bytes32",
3493
- internalType: "bytes32"
3520
+ type: "uint256",
3521
+ internalType: "uint256"
3494
3522
  }
3495
3523
  ],
3496
3524
  outputs: [
3497
3525
  {
3498
- name: "",
3526
+ name: "amount",
3527
+ type: "uint256",
3528
+ internalType: "uint256"
3529
+ },
3530
+ {
3531
+ name: "committed",
3499
3532
  type: "uint256",
3500
3533
  internalType: "uint256"
3501
3534
  }
3502
3535
  ],
3503
3536
  stateMutability: "view"
3504
3537
  },
3538
+ {
3539
+ type: "function",
3540
+ name: "acceptOwnership",
3541
+ inputs: [],
3542
+ outputs: [],
3543
+ stateMutability: "nonpayable"
3544
+ },
3505
3545
  {
3506
3546
  type: "function",
3507
3547
  name: "calculateCommitmentSlotHash",
@@ -3711,6 +3751,19 @@ var ABI3 = [
3711
3751
  ],
3712
3752
  stateMutability: "view"
3713
3753
  },
3754
+ {
3755
+ type: "function",
3756
+ name: "extrinsicModule",
3757
+ inputs: [],
3758
+ outputs: [
3759
+ {
3760
+ name: "",
3761
+ type: "address",
3762
+ internalType: "address"
3763
+ }
3764
+ ],
3765
+ stateMutability: "view"
3766
+ },
3714
3767
  {
3715
3768
  type: "function",
3716
3769
  name: "fillOrder",
@@ -3873,6 +3926,23 @@ var ABI3 = [
3873
3926
  internalType: "uint256"
3874
3927
  }
3875
3928
  ]
3929
+ },
3930
+ {
3931
+ name: "inputs",
3932
+ type: "tuple[]",
3933
+ internalType: "struct TokenInfo[]",
3934
+ components: [
3935
+ {
3936
+ name: "token",
3937
+ type: "bytes32",
3938
+ internalType: "bytes32"
3939
+ },
3940
+ {
3941
+ name: "amount",
3942
+ type: "uint256",
3943
+ internalType: "uint256"
3944
+ }
3945
+ ]
3876
3946
  }
3877
3947
  ]
3878
3948
  }
@@ -3898,51 +3968,63 @@ var ABI3 = [
3898
3968
  name: "initialize",
3899
3969
  inputs: [
3900
3970
  {
3901
- name: "p",
3971
+ name: "init",
3902
3972
  type: "tuple",
3903
- internalType: "struct Params",
3973
+ internalType: "struct InitParams",
3904
3974
  components: [
3905
3975
  {
3906
- name: "host",
3907
- type: "address",
3908
- internalType: "address"
3909
- },
3910
- {
3911
- name: "dispatcher",
3912
- type: "address",
3913
- internalType: "address"
3914
- },
3915
- {
3916
- name: "solverSelection",
3917
- type: "bool",
3918
- internalType: "bool"
3976
+ name: "params",
3977
+ type: "tuple",
3978
+ internalType: "struct Params",
3979
+ components: [
3980
+ {
3981
+ name: "host",
3982
+ type: "address",
3983
+ internalType: "address"
3984
+ },
3985
+ {
3986
+ name: "dispatcher",
3987
+ type: "address",
3988
+ internalType: "address"
3989
+ },
3990
+ {
3991
+ name: "solverSelection",
3992
+ type: "bool",
3993
+ internalType: "bool"
3994
+ },
3995
+ {
3996
+ name: "surplusShareBps",
3997
+ type: "uint256",
3998
+ internalType: "uint256"
3999
+ },
4000
+ {
4001
+ name: "protocolFeeBps",
4002
+ type: "uint256",
4003
+ internalType: "uint256"
4004
+ },
4005
+ {
4006
+ name: "priceOracle",
4007
+ type: "address",
4008
+ internalType: "address"
4009
+ }
4010
+ ]
3919
4011
  },
3920
4012
  {
3921
- name: "surplusShareBps",
3922
- type: "uint256",
3923
- internalType: "uint256"
4013
+ name: "peerChains",
4014
+ type: "bytes[]",
4015
+ internalType: "bytes[]"
3924
4016
  },
3925
4017
  {
3926
- name: "protocolFeeBps",
3927
- type: "uint256",
3928
- internalType: "uint256"
4018
+ name: "relayer",
4019
+ type: "address",
4020
+ internalType: "address"
3929
4021
  },
3930
4022
  {
3931
- name: "priceOracle",
4023
+ name: "owner",
3932
4024
  type: "address",
3933
4025
  internalType: "address"
3934
4026
  }
3935
4027
  ]
3936
- },
3937
- {
3938
- name: "peerChains",
3939
- type: "bytes[]",
3940
- internalType: "bytes[]"
3941
- },
3942
- {
3943
- name: "relayer",
3944
- type: "address",
3945
- internalType: "address"
3946
4028
  }
3947
4029
  ],
3948
4030
  outputs: [],
@@ -3967,12 +4049,25 @@ var ABI3 = [
3967
4049
  ],
3968
4050
  stateMutability: "view"
3969
4051
  },
4052
+ {
4053
+ type: "function",
4054
+ name: "intrinsicModule",
4055
+ inputs: [],
4056
+ outputs: [
4057
+ {
4058
+ name: "",
4059
+ type: "address",
4060
+ internalType: "address"
4061
+ }
4062
+ ],
4063
+ stateMutability: "view"
4064
+ },
3970
4065
  {
3971
4066
  type: "function",
3972
4067
  name: "migrate",
3973
4068
  inputs: [
3974
4069
  {
3975
- name: "relayer",
4070
+ name: "owner_",
3976
4071
  type: "address",
3977
4072
  internalType: "address"
3978
4073
  }
@@ -4047,7 +4142,7 @@ var ABI3 = [
4047
4142
  name: "onGetResponse",
4048
4143
  inputs: [
4049
4144
  {
4050
- name: "incoming",
4145
+ name: "",
4051
4146
  type: "tuple",
4052
4147
  internalType: "struct IncomingGetResponse",
4053
4148
  components: [
@@ -4262,6 +4357,19 @@ var ABI3 = [
4262
4357
  outputs: [],
4263
4358
  stateMutability: "nonpayable"
4264
4359
  },
4360
+ {
4361
+ type: "function",
4362
+ name: "owner",
4363
+ inputs: [],
4364
+ outputs: [
4365
+ {
4366
+ name: "",
4367
+ type: "address",
4368
+ internalType: "address"
4369
+ }
4370
+ ],
4371
+ stateMutability: "view"
4372
+ },
4265
4373
  {
4266
4374
  type: "function",
4267
4375
  name: "params",
@@ -4307,6 +4415,39 @@ var ABI3 = [
4307
4415
  ],
4308
4416
  stateMutability: "view"
4309
4417
  },
4418
+ {
4419
+ type: "function",
4420
+ name: "pause",
4421
+ inputs: [],
4422
+ outputs: [],
4423
+ stateMutability: "nonpayable"
4424
+ },
4425
+ {
4426
+ type: "function",
4427
+ name: "paused",
4428
+ inputs: [],
4429
+ outputs: [
4430
+ {
4431
+ name: "",
4432
+ type: "bool",
4433
+ internalType: "bool"
4434
+ }
4435
+ ],
4436
+ stateMutability: "view"
4437
+ },
4438
+ {
4439
+ type: "function",
4440
+ name: "pendingOwner",
4441
+ inputs: [],
4442
+ outputs: [
4443
+ {
4444
+ name: "",
4445
+ type: "address",
4446
+ internalType: "address"
4447
+ }
4448
+ ],
4449
+ stateMutability: "view"
4450
+ },
4310
4451
  {
4311
4452
  type: "function",
4312
4453
  name: "placeOrder",
@@ -4562,6 +4703,13 @@ var ABI3 = [
4562
4703
  ],
4563
4704
  stateMutability: "view"
4564
4705
  },
4706
+ {
4707
+ type: "function",
4708
+ name: "renounceOwnership",
4709
+ inputs: [],
4710
+ outputs: [],
4711
+ stateMutability: "nonpayable"
4712
+ },
4565
4713
  {
4566
4714
  type: "function",
4567
4715
  name: "select",
@@ -4600,10 +4748,10 @@ var ABI3 = [
4600
4748
  },
4601
4749
  {
4602
4750
  type: "function",
4603
- name: "setRelayer",
4751
+ name: "transferOwnership",
4604
4752
  inputs: [
4605
4753
  {
4606
- name: "relayer",
4754
+ name: "newOwner",
4607
4755
  type: "address",
4608
4756
  internalType: "address"
4609
4757
  }
@@ -4613,19 +4761,8 @@ var ABI3 = [
4613
4761
  },
4614
4762
  {
4615
4763
  type: "function",
4616
- name: "upgradeToAndCall",
4617
- inputs: [
4618
- {
4619
- name: "newImplementation",
4620
- type: "address",
4621
- internalType: "address"
4622
- },
4623
- {
4624
- name: "data",
4625
- type: "bytes",
4626
- internalType: "bytes"
4627
- }
4628
- ],
4764
+ name: "unpause",
4765
+ inputs: [],
4629
4766
  outputs: [],
4630
4767
  stateMutability: "nonpayable"
4631
4768
  },
@@ -4771,6 +4908,12 @@ var ABI3 = [
4771
4908
  indexed: true,
4772
4909
  internalType: "bytes32"
4773
4910
  },
4911
+ {
4912
+ name: "solver",
4913
+ type: "address",
4914
+ indexed: false,
4915
+ internalType: "address"
4916
+ },
4774
4917
  {
4775
4918
  name: "tokens",
4776
4919
  type: "tuple[]",
@@ -5008,37 +5151,75 @@ var ABI3 = [
5008
5151
  },
5009
5152
  {
5010
5153
  type: "event",
5011
- name: "ParamsUpdated",
5154
+ name: "OwnershipTransferStarted",
5012
5155
  inputs: [
5013
5156
  {
5014
- name: "previous",
5015
- type: "tuple",
5016
- indexed: false,
5017
- internalType: "struct Params",
5018
- components: [
5019
- {
5020
- name: "host",
5021
- type: "address",
5022
- internalType: "address"
5023
- },
5024
- {
5025
- name: "dispatcher",
5026
- type: "address",
5027
- internalType: "address"
5028
- },
5029
- {
5030
- name: "solverSelection",
5031
- type: "bool",
5032
- internalType: "bool"
5033
- },
5034
- {
5035
- name: "surplusShareBps",
5036
- type: "uint256",
5037
- internalType: "uint256"
5038
- },
5039
- {
5040
- name: "protocolFeeBps",
5041
- type: "uint256",
5157
+ name: "previousOwner",
5158
+ type: "address",
5159
+ indexed: true,
5160
+ internalType: "address"
5161
+ },
5162
+ {
5163
+ name: "newOwner",
5164
+ type: "address",
5165
+ indexed: true,
5166
+ internalType: "address"
5167
+ }
5168
+ ],
5169
+ anonymous: false
5170
+ },
5171
+ {
5172
+ type: "event",
5173
+ name: "OwnershipTransferred",
5174
+ inputs: [
5175
+ {
5176
+ name: "previousOwner",
5177
+ type: "address",
5178
+ indexed: true,
5179
+ internalType: "address"
5180
+ },
5181
+ {
5182
+ name: "newOwner",
5183
+ type: "address",
5184
+ indexed: true,
5185
+ internalType: "address"
5186
+ }
5187
+ ],
5188
+ anonymous: false
5189
+ },
5190
+ {
5191
+ type: "event",
5192
+ name: "ParamsUpdated",
5193
+ inputs: [
5194
+ {
5195
+ name: "previous",
5196
+ type: "tuple",
5197
+ indexed: false,
5198
+ internalType: "struct Params",
5199
+ components: [
5200
+ {
5201
+ name: "host",
5202
+ type: "address",
5203
+ internalType: "address"
5204
+ },
5205
+ {
5206
+ name: "dispatcher",
5207
+ type: "address",
5208
+ internalType: "address"
5209
+ },
5210
+ {
5211
+ name: "solverSelection",
5212
+ type: "bool",
5213
+ internalType: "bool"
5214
+ },
5215
+ {
5216
+ name: "surplusShareBps",
5217
+ type: "uint256",
5218
+ internalType: "uint256"
5219
+ },
5220
+ {
5221
+ name: "protocolFeeBps",
5222
+ type: "uint256",
5042
5223
  internalType: "uint256"
5043
5224
  },
5044
5225
  {
@@ -5146,46 +5327,73 @@ var ABI3 = [
5146
5327
  },
5147
5328
  {
5148
5329
  type: "event",
5149
- name: "RelayerUpdated",
5330
+ name: "Paused",
5150
5331
  inputs: [
5151
5332
  {
5152
- name: "previous",
5333
+ name: "account",
5153
5334
  type: "address",
5154
5335
  indexed: false,
5155
5336
  internalType: "address"
5337
+ }
5338
+ ],
5339
+ anonymous: false
5340
+ },
5341
+ {
5342
+ type: "event",
5343
+ name: "ProtocolFeeRefunded",
5344
+ inputs: [
5345
+ {
5346
+ name: "commitment",
5347
+ type: "bytes32",
5348
+ indexed: true,
5349
+ internalType: "bytes32"
5156
5350
  },
5157
5351
  {
5158
- name: "current",
5352
+ name: "token",
5159
5353
  type: "address",
5160
- indexed: false,
5354
+ indexed: true,
5161
5355
  internalType: "address"
5356
+ },
5357
+ {
5358
+ name: "amount",
5359
+ type: "uint256",
5360
+ indexed: false,
5361
+ internalType: "uint256"
5162
5362
  }
5163
5363
  ],
5164
5364
  anonymous: false
5165
5365
  },
5166
5366
  {
5167
5367
  type: "event",
5168
- name: "Upgraded",
5368
+ name: "RelayerUpdated",
5169
5369
  inputs: [
5170
5370
  {
5171
- name: "implementation",
5371
+ name: "previous",
5172
5372
  type: "address",
5173
- indexed: true,
5373
+ indexed: false,
5374
+ internalType: "address"
5375
+ },
5376
+ {
5377
+ name: "current",
5378
+ type: "address",
5379
+ indexed: false,
5174
5380
  internalType: "address"
5175
5381
  }
5176
5382
  ],
5177
5383
  anonymous: false
5178
5384
  },
5179
5385
  {
5180
- type: "error",
5181
- name: "AddressEmptyCode",
5386
+ type: "event",
5387
+ name: "Unpaused",
5182
5388
  inputs: [
5183
5389
  {
5184
- name: "target",
5390
+ name: "account",
5185
5391
  type: "address",
5392
+ indexed: false,
5186
5393
  internalType: "address"
5187
5394
  }
5188
- ]
5395
+ ],
5396
+ anonymous: false
5189
5397
  },
5190
5398
  {
5191
5399
  type: "error",
@@ -5221,28 +5429,17 @@ var ABI3 = [
5221
5429
  },
5222
5430
  {
5223
5431
  type: "error",
5224
- name: "ERC1967InvalidImplementation",
5225
- inputs: [
5226
- {
5227
- name: "implementation",
5228
- type: "address",
5229
- internalType: "address"
5230
- }
5231
- ]
5232
- },
5233
- {
5234
- type: "error",
5235
- name: "ERC1967NonPayable",
5432
+ name: "EnforcedPause",
5236
5433
  inputs: []
5237
5434
  },
5238
5435
  {
5239
5436
  type: "error",
5240
- name: "Expired",
5437
+ name: "ExpectedPause",
5241
5438
  inputs: []
5242
5439
  },
5243
5440
  {
5244
5441
  type: "error",
5245
- name: "FailedCall",
5442
+ name: "Expired",
5246
5443
  inputs: []
5247
5444
  },
5248
5445
  {
@@ -5285,11 +5482,43 @@ var ABI3 = [
5285
5482
  name: "NotInitializing",
5286
5483
  inputs: []
5287
5484
  },
5485
+ {
5486
+ type: "error",
5487
+ name: "OwnableInvalidOwner",
5488
+ inputs: [
5489
+ {
5490
+ name: "owner",
5491
+ type: "address",
5492
+ internalType: "address"
5493
+ }
5494
+ ]
5495
+ },
5496
+ {
5497
+ type: "error",
5498
+ name: "OwnableUnauthorizedAccount",
5499
+ inputs: [
5500
+ {
5501
+ name: "account",
5502
+ type: "address",
5503
+ internalType: "address"
5504
+ }
5505
+ ]
5506
+ },
5288
5507
  {
5289
5508
  type: "error",
5290
5509
  name: "PartialFillNotAllowed",
5291
5510
  inputs: []
5292
5511
  },
5512
+ {
5513
+ type: "error",
5514
+ name: "RateBelowOrder",
5515
+ inputs: []
5516
+ },
5517
+ {
5518
+ type: "error",
5519
+ name: "RateFillTooSmall",
5520
+ inputs: []
5521
+ },
5293
5522
  {
5294
5523
  type: "error",
5295
5524
  name: "ReentrancyGuardReentrantCall",
@@ -5517,9 +5746,6 @@ var ChainConfigService = class {
5517
5746
  getUniswapV4StateViewAddress(chain) {
5518
5747
  return this.getConfig(chain)?.addresses.UniswapV4StateView ?? "0x";
5519
5748
  }
5520
- getUniswapV4PoolConfigs(chain) {
5521
- return this.getConfig(chain)?.uniswapV4Pools ?? [];
5522
- }
5523
5749
  getPermit2Address(chain) {
5524
5750
  return this.getConfig(chain)?.addresses.Permit2 ?? "0x";
5525
5751
  }
@@ -8256,9 +8482,7 @@ Failed HTTP Request: ${JSON.stringify(
8256
8482
  };
8257
8483
 
8258
8484
  // src/chains/intentsCoprocessor.ts
8259
- var SYSTEM_EVENTS_KEY = util.u8aToHex(util.u8aConcat(utilCrypto.xxhashAsU8a("System", 128), utilCrypto.xxhashAsU8a("Events", 128)));
8260
8485
  var OFFCHAIN_BID_PREFIX = new TextEncoder().encode("intents::bid::");
8261
- var OFFCHAIN_PHANTOM_PREFIX = new TextEncoder().encode("intents::phantom::order::");
8262
8486
  var HYPERBRIDGE_TYPES_BUNDLE = {
8263
8487
  spec: {
8264
8488
  nexus: { hasher: utilCrypto.keccakAsU8a },
@@ -8268,11 +8492,7 @@ var HYPERBRIDGE_TYPES_BUNDLE = {
8268
8492
  var BASE_TIP = 1000000000n;
8269
8493
  var HTTP_CONNECT_TIMEOUT_MS = 2e4;
8270
8494
  var INCLUSION_TIMEOUT_MS = 2e4;
8271
- var PHANTOM_POLL_INTERVAL_MS = 15e3;
8272
- var GARGANTUA_PHANTOM_POLL_INTERVAL_MS = 6e3;
8273
8495
  var DEFAULT_RPC_MAX_RPS = 8;
8274
- var DEFAULT_MAX_BLOCKS_PER_POLL = 10;
8275
- var MAX_RATE_LIMIT_BACKOFF_TICKS = 8;
8276
8496
  var rpcLimiters = /* @__PURE__ */ new Map();
8277
8497
  function limiterFor(httpUrl) {
8278
8498
  const key = new URL(httpUrl).origin;
@@ -8288,45 +8508,6 @@ function configuredRpcMaxRps() {
8288
8508
  const parsed = raw === void 0 ? Number.NaN : Number(raw);
8289
8509
  return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_RPC_MAX_RPS;
8290
8510
  }
8291
- function isRateLimited(err) {
8292
- const message = err instanceof Error ? err.message : String(err);
8293
- return message.includes("[429]") || /too many requests/i.test(message);
8294
- }
8295
- function isMethodUnavailable(err) {
8296
- const message = err instanceof Error ? err.message : String(err);
8297
- const code = err?.code;
8298
- return code === -32601 || /method not found|unsafe to be called externally/i.test(message);
8299
- }
8300
- var EventDecodeError = class extends Error {
8301
- };
8302
- function phantomOrdersFrom(records) {
8303
- if (records == null || typeof records[Symbol.iterator] !== "function") {
8304
- throw new EventDecodeError(`Expected a decoded event vector, got ${typeof records}`);
8305
- }
8306
- const orders = [];
8307
- for (const record of records) {
8308
- if (typeof record !== "object" || record === null || !("event" in record)) {
8309
- throw new EventDecodeError(
8310
- "system.events did not decode to event records \u2014 the storage entry's metadata was probably not carried through, leaving the value as raw bytes"
8311
- );
8312
- }
8313
- const { event } = record;
8314
- if (event.section !== "intentsCoprocessor" || event.method !== "PhantomOrderRegistered") continue;
8315
- const [commitment, chain, createdAt, legs] = event.data;
8316
- orders.push({
8317
- commitment: commitment.toHex(),
8318
- chain: new TextDecoder().decode(util.hexToU8a(chain.toHex())),
8319
- createdAt: createdAt.toNumber(),
8320
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8321
- legs: legs.map((leg) => ({
8322
- tokenA: leg.tokenA.toHex(),
8323
- tokenB: leg.tokenB.toHex(),
8324
- standardAmount: BigInt(leg.standardAmount.toString())
8325
- }))
8326
- });
8327
- }
8328
- return orders;
8329
- }
8330
8511
  function rejectAfter(ms, message) {
8331
8512
  return new Promise((_resolve, reject) => {
8332
8513
  const timer = setTimeout(() => reject(new Error(message)), ms);
@@ -8398,19 +8579,16 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8398
8579
  ownsConnection;
8399
8580
  /** Cached result of whether the node exposes intents_* RPC methods */
8400
8581
  hasIntentsRpc = null;
8401
- /** The pallet's phantom timings, read once. Cleared on failure so the read retries. */
8402
- phantomTimingsRead = null;
8403
8582
  /** The HTTP-backed api, connected on first use. Cleared after a failed attempt so it retries. */
8404
8583
  httpApi = null;
8405
- /** Last runtime version read from the node, for {@link confirmedRuntimeVersion} to compare against. */
8406
- lastRuntimeVersion;
8407
- /** Set once the node refuses `state_queryStorage`, so the poll stops asking for it. */
8408
- rangeQueryUnavailable = false;
8409
8584
  // Serialises every extrinsic submission on this instance's substrate account. All submit/retract
8410
- // methods funnel through signAndSendExtrinsic, each using the API's auto-nonce; fired in parallel
8411
- // (bids for orders on different chains, or several phantom orders in one interval) they would grab
8412
- // the same nonce and all but one would fail. Concurrency 1 sequences them.
8585
+ // methods funnel through signAndSendExtrinsic; fired in parallel (bids for orders on different
8586
+ // chains, or several bids on one order) they would grab the same nonce and all but one would
8587
+ // fail. Concurrency 1 sequences them, and {@link nextNonce} keeps consecutive submissions off
8588
+ // each other's nonce even while the first is still in the pool.
8413
8589
  submissionQueue = new PQueue__default.default({ concurrency: 1 });
8590
+ /** The highest nonce this instance has put into the pool, so the next submission clears it. */
8591
+ lastPooledNonce;
8414
8592
  /**
8415
8593
  * Creates and connects an IntentsCoprocessor to a Hyperbridge node.
8416
8594
  * This creates and manages its own API connection.
@@ -8472,10 +8650,10 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8472
8650
  * The HTTP api is always created here, so it is always ours to close.
8473
8651
  */
8474
8652
  async disconnect() {
8475
- const http4 = this.httpApi;
8653
+ const http3 = this.httpApi;
8476
8654
  this.httpApi = null;
8477
- if (http4) {
8478
- await http4.then((api) => api.disconnect()).catch(() => {
8655
+ if (http3) {
8656
+ await http3.then((api) => api.disconnect()).catch(() => {
8479
8657
  });
8480
8658
  }
8481
8659
  if (this.ownsConnection) {
@@ -8489,9 +8667,8 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8489
8667
  *
8490
8668
  * The connection attempt is bounded on both sides. `isReadyOrError` rejects on a failed
8491
8669
  * handshake, where plain `isReady` would simply never resolve, and the timeout covers an
8492
- * endpoint that accepts the request and then goes quiet. An unbounded wait here would hang the
8493
- * poll tick awaiting it, which is precisely the silent stall that polling exists to avoid. A
8494
- * failed attempt is not cached, so the next call tries again.
8670
+ * endpoint that accepts the request and then goes quiet, so a caller awaiting it never hangs
8671
+ * silently. A failed attempt is not cached, so the next call tries again.
8495
8672
  */
8496
8673
  async http() {
8497
8674
  if (!this.httpApi) {
@@ -8500,11 +8677,9 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8500
8677
  // Response cache off (third argument, capacity 0). polkadot-js caches every request that
8501
8678
  // names a block hash — `chain_getHeader(hash)`, `state_getRuntimeVersion(hash)`, storage
8502
8679
  // reads at a hash — by storing the request promise itself, a rejected one included, for a
8503
- // 30s TTL that every hit refreshes. The phantom poll retries the block it failed on with
8504
- // identical parameters every tick, so one reset connection became the same rejection
8505
- // replayed from memory on every tick, faster than the TTL could lapse, and the node never
8506
- // saw a second request. The cache bought nothing here anyway: the poll reads each block
8507
- // once, and `api.at(hash)` reuses registries at the api layer regardless.
8680
+ // 30s TTL that every hit refreshes. A caller retrying a failed read with identical
8681
+ // parameters would get the same rejection replayed from memory for as long as it kept
8682
+ // retrying inside the TTL, and the node would never see a second request.
8508
8683
  // Concurrent calls are coalesced into one JSON-RPC batch request, and every request
8509
8684
  // to this endpoint is paced by a bucket shared with any other coprocessor in this
8510
8685
  // process pointed at the same host — the limit is the server's, and it counts
@@ -8579,7 +8754,12 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8579
8754
  return { success: false, error: err instanceof Error ? err.message : String(err) };
8580
8755
  }
8581
8756
  }
8582
- return await this.sendExtrinsicWithRetries(build(this.api), maxRetries, timeoutMs);
8757
+ return await this.sendExtrinsicWithRetries(
8758
+ build(this.api),
8759
+ maxRetries,
8760
+ timeoutMs,
8761
+ await this.nextNonce(this.api)
8762
+ );
8583
8763
  });
8584
8764
  return result ?? { success: false, error: "Submission queue returned no result" };
8585
8765
  }
@@ -8598,7 +8778,10 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8598
8778
  */
8599
8779
  async sendViaHttp(api, build) {
8600
8780
  try {
8601
- const hash = await build(api).signAndSend(this.getKeyPair(), { tip: BASE_TIP });
8781
+ const nonce = await this.nextNonce(api);
8782
+ const options = nonce === void 0 ? { tip: BASE_TIP } : { tip: BASE_TIP, nonce };
8783
+ const hash = await build(api).signAndSend(this.getKeyPair(), options);
8784
+ if (nonce !== void 0) this.lastPooledNonce = nonce;
8602
8785
  return { success: false, pending: true, extrinsicHash: hash.toHex() };
8603
8786
  } catch (err) {
8604
8787
  return this.classifySubmissionError(err instanceof Error ? err : new Error(String(err)));
@@ -8611,7 +8794,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8611
8794
  * Two kinds of failure are retried, and the difference is the nonce.
8612
8795
  *
8613
8796
  * An attempt that verifiably went nowhere (rejected before the pool, dropped, invalid) leaves
8614
- * the account nonce free, so the next attempt simply re-signs with the auto-nonce.
8797
+ * the account nonce free, so the next attempt simply re-signs with the pool-aware nonce.
8615
8798
  *
8616
8799
  * An attempt that reached the pool and was still there when the watch timed out (`stalled`) is
8617
8800
  * retried as a *replacement*: the same nonce it was signed with, and double the tip. Substrate's
@@ -8630,16 +8813,19 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8630
8813
  * copy is in flight and its outcome is unknown here, so the `pending` result goes back to the
8631
8814
  * caller to confirm later.
8632
8815
  */
8633
- async sendExtrinsicWithRetries(extrinsic, maxRetries, timeoutMs) {
8816
+ async sendExtrinsicWithRetries(extrinsic, maxRetries, timeoutMs, initialNonce) {
8634
8817
  const keyPair = this.getKeyPair();
8635
8818
  let attempt = 0;
8636
- let nonce;
8819
+ let nonce = initialNonce;
8637
8820
  let stalled;
8638
8821
  while (attempt < maxRetries) {
8639
8822
  const currentTip = BASE_TIP * BigInt(2 ** attempt);
8640
8823
  attempt++;
8641
8824
  try {
8642
8825
  const result = await this.sendWithTimeout(extrinsic, keyPair, currentTip, timeoutMs, nonce);
8826
+ if (nonce !== void 0 && (result.success || result.pending || result.stalled)) {
8827
+ this.lastPooledNonce = Math.max(nonce, this.lastPooledNonce ?? nonce);
8828
+ }
8643
8829
  if (result.success || result.error?.includes("Dispatch error")) {
8644
8830
  return result;
8645
8831
  }
@@ -8699,6 +8885,35 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8699
8885
  * replacement rather than a second extrinsic queued behind the first. Left undefined on the
8700
8886
  * first attempt, where the api's auto-nonce is correct.
8701
8887
  */
8888
+ /**
8889
+ * The nonce the next submission signs with, or undefined when the node cannot be asked.
8890
+ *
8891
+ * Submissions are serialised, but a watch gives up while its extrinsic is still in the pool
8892
+ * (`stalled`, reported as `pending`), and a pooled extrinsic does not advance the account's
8893
+ * on-chain nonce. An extrinsic signed against on-chain state would then take the nonce of the
8894
+ * one ahead of it and bounce off it (1013/1014) instead of queueing behind it.
8895
+ *
8896
+ * `system_accountNextIndex` is the only pool-aware source here. `signAndSend`'s own `nonce: -1`
8897
+ * is not: on a runtime carrying `AccountNonceApi`, which Hyperbridge does, polkadot-js reads the
8898
+ * nonce through that runtime call and never sees the pool.
8899
+ *
8900
+ * The RPC covers the ready queue, so anything of this instance's that has left it — retried,
8901
+ * queued as future, or simply not counted yet — is covered by `lastPooledNonce` instead, which
8902
+ * is the highest nonce this instance has put into the pool. The two are combined rather than
8903
+ * trusted separately, so a burst never reuses a nonce and a restart still picks up the chain's.
8904
+ */
8905
+ async nextNonce(api) {
8906
+ try {
8907
+ const accountNextIndex = api.rpc?.system?.accountNextIndex;
8908
+ if (!accountNextIndex) return void 0;
8909
+ const next = await accountNextIndex(this.getKeyPair().address);
8910
+ const fromPool = Number(next.toString());
8911
+ if (!Number.isFinite(fromPool)) return void 0;
8912
+ return this.lastPooledNonce === void 0 ? fromPool : Math.max(fromPool, this.lastPooledNonce + 1);
8913
+ } catch {
8914
+ return void 0;
8915
+ }
8916
+ }
8702
8917
  async sendWithTimeout(extrinsic, keyPair, tip, timeoutMs, nonce) {
8703
8918
  return new Promise((resolve) => {
8704
8919
  let resolved = false;
@@ -8741,25 +8956,10 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8741
8956
  } else if (result.status.isInBlock || result.status.isFinalized) {
8742
8957
  resolved = true;
8743
8958
  clearTimeout(timeoutId);
8744
- const interrupted = result.events.find(
8745
- ({ event }) => event.section === "utility" && event.method === "BatchInterrupted"
8746
- );
8747
- if (interrupted) {
8748
- const [indexCodec, dispatchError] = interrupted.event.data;
8749
- if (Number(indexCodec.toString()) === 0) {
8750
- resolve({
8751
- success: false,
8752
- error: `Dispatch error: ${this.describeDispatchError(dispatchError)}`
8753
- });
8754
- return;
8755
- }
8756
- }
8757
8959
  resolve({
8758
8960
  success: true,
8759
8961
  blockHash: (result.status.isInBlock ? result.status.asInBlock : result.status.asFinalized).toHex(),
8760
- extrinsicHash: extrinsic.hash.toHex(),
8761
- // Carried so a batch caller can attribute each item's outcome.
8762
- events: result.events
8962
+ extrinsicHash: extrinsic.hash.toHex()
8763
8963
  });
8764
8964
  }
8765
8965
  }).then((unsub) => {
@@ -8780,13 +8980,19 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8780
8980
  /**
8781
8981
  * Submits a bid to Hyperbridge's pallet-intents
8782
8982
  *
8983
+ * A filler can hold several bids on one order, one per price it offers, and `bid` tells them
8984
+ * apart: submitting again under an identifier the filler already holds replaces that bid alone.
8985
+ * By convention it is `keccak256` of the UserOp's `callData`, which is also what gives each bid
8986
+ * its own nonce key on the `SolverAccount` (see `CryptoUtils.bidId`).
8987
+ *
8783
8988
  * @param commitment - The order commitment hash (bytes32)
8784
8989
  * @param userOp - The encoded PackedUserOperation as hex string
8990
+ * @param bid - Identifies this bid among the filler's bids on the order (bytes32)
8785
8991
  * @returns BidSubmissionResult with success status and block/extrinsic hash
8786
8992
  */
8787
- async submitBid(commitment, userOp) {
8993
+ async submitBid(commitment, userOp, bid) {
8788
8994
  try {
8789
- return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.placeBid(commitment, userOp));
8995
+ return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.placeBid(commitment, bid, userOp));
8790
8996
  } catch (error) {
8791
8997
  return {
8792
8998
  success: false,
@@ -8800,46 +9006,12 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8800
9006
  * Use this to remove unused quotes and claim back deposited BRIDGE tokens.
8801
9007
  *
8802
9008
  * @param commitment - The order commitment hash (bytes32)
9009
+ * @param bid - Which of the filler's bids on the order to retract (bytes32)
8803
9010
  * @returns BidSubmissionResult with success status and block/extrinsic hash
8804
9011
  */
8805
- async retractBid(commitment) {
8806
- try {
8807
- return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.retractBid(commitment));
8808
- } catch (error) {
8809
- return {
8810
- success: false,
8811
- error: error instanceof Error ? error.message : "Unknown error"
8812
- };
8813
- }
8814
- }
8815
- /**
8816
- * Places a new bid and retracts a previous one in a single transaction via utility.batch.
8817
- *
8818
- * The new bid is the primary operation, so `placeBid` MUST run first. `utility.batch` is
8819
- * non-atomic: a failing call interrupts the batch (via a BatchInterrupted event) without
8820
- * reverting the calls that already succeeded. Placing first guarantees the new bid lands even
8821
- * when the retraction then fails — which it routinely does, because a previous commitment's bid
8822
- * may already be gone (or was itself never placed), making `retractBid` return `BidNotFound`.
8823
- *
8824
- * Ordering retraction first (the previous behaviour) caused a self-sustaining cascade: a
8825
- * `BidNotFound` on the leading retract skipped the trailing `placeBid`, so the current bid never
8826
- * landed, so the *next* interval's retract of that never-placed commitment also failed, and so
8827
- * on — silently, because the batch extrinsic itself reports success. The deposit reclaim is
8828
- * best-effort; landing the bid is not.
8829
- *
8830
- * @param retractCommitment - The order commitment of the bid to retract (bytes32)
8831
- * @param bidCommitment - The order commitment of the new bid (bytes32)
8832
- * @param userOp - The encoded PackedUserOperation as hex string
8833
- * @returns BidSubmissionResult with success status and block/extrinsic hash
8834
- */
8835
- async submitBidWithRetraction(retractCommitment, bidCommitment, userOp) {
9012
+ async retractBid(commitment, bid) {
8836
9013
  try {
8837
- return await this.signAndSendExtrinsic(
8838
- (api) => api.tx.utility.batch([
8839
- api.tx.intentsCoprocessor.placeBid(bidCommitment, userOp),
8840
- api.tx.intentsCoprocessor.retractBid(retractCommitment)
8841
- ])
8842
- );
9014
+ return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.retractBid(commitment, bid));
8843
9015
  } catch (error) {
8844
9016
  return {
8845
9017
  success: false,
@@ -8847,89 +9019,6 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8847
9019
  };
8848
9020
  }
8849
9021
  }
8850
- /**
8851
- * Places every phantom bid of one interval in a single extrinsic, retracting each chain's
8852
- * previous bid alongside it.
8853
- *
8854
- * The pallet registers one phantom order per configured chain in the same block, so this is the
8855
- * whole interval's set. Submitting them one at a time costs a block per chain: submissions are
8856
- * serialised on the account nonce and each waits for inclusion, so the last chain's bid is many
8857
- * blocks behind the first, against a bid window measured in tens of blocks. Batched, every bid
8858
- * lands in the same block.
8859
- *
8860
- * Uses `utility.force_batch`, not `utility.batch`. `batch` stops at the first failing call, so
8861
- * one rejected bid — a closed window, a duplicate, an insufficient deposit — would silently
8862
- * drop every bid after it. `force_batch` runs them all and reports each outcome. It needs no
8863
- * special origin: any signed account may call it, exactly like `batch`.
8864
- *
8865
- * @param bids - The bids to place; an empty list is a no-op
8866
- * @returns Per-bid outcomes, in the order given
8867
- */
8868
- async submitPhantomBids(bids) {
8869
- if (bids.length === 0) return { bids: [] };
8870
- const placeIndexByBid = [];
8871
- let callCount = 0;
8872
- for (const bid of bids) {
8873
- placeIndexByBid.push(callCount);
8874
- callCount += bid.retractCommitment ? 2 : 1;
8875
- }
8876
- const outcome = await this.signAndSendExtrinsic(
8877
- (api) => api.tx.utility.forceBatch(
8878
- bids.flatMap((bid) => {
8879
- const calls = [api.tx.intentsCoprocessor.placeBid(bid.commitment, bid.userOp)];
8880
- if (bid.retractCommitment) {
8881
- calls.push(api.tx.intentsCoprocessor.retractBid(bid.retractCommitment));
8882
- }
8883
- return calls;
8884
- })
8885
- )
8886
- );
8887
- if (!outcome.success) {
8888
- return {
8889
- bids: bids.map((bid) => ({ commitment: bid.commitment, success: false, error: outcome.error })),
8890
- pending: outcome.pending,
8891
- extrinsicHash: outcome.extrinsicHash,
8892
- error: outcome.error
8893
- };
8894
- }
8895
- const items = this.readForceBatchItems(outcome.events ?? [], callCount);
8896
- return {
8897
- bids: bids.map((bid, index) => {
8898
- const error = items.errors[placeIndexByBid[index]];
8899
- return { commitment: bid.commitment, success: !error, error };
8900
- }),
8901
- blockHash: outcome.blockHash,
8902
- extrinsicHash: outcome.extrinsicHash,
8903
- error: items.error
8904
- };
8905
- }
8906
- /**
8907
- * Reads one outcome per call out of a force_batch's events.
8908
- *
8909
- * `ItemFailed` carries no index — pallet-utility emits exactly one `ItemCompleted` or
8910
- * `ItemFailed` per call, in call order, so the k-th item event belongs to call k.
8911
- *
8912
- * A count that does not match the calls submitted means the events are not the ones assumed
8913
- * here, and every attribution after the discrepancy would be off by one. The bids are then
8914
- * reported as placed: a bid wrongly recorded as landed is retracted next interval and the
8915
- * retraction harmlessly fails with `BidNotFound`, whereas one wrongly recorded as failed is
8916
- * never retracted at all and leaves its deposit reserved.
8917
- */
8918
- readForceBatchItems(events, callCount) {
8919
- const errors = [];
8920
- for (const { event } of events) {
8921
- if (event.section !== "utility") continue;
8922
- if (event.method === "ItemCompleted") errors.push(void 0);
8923
- else if (event.method === "ItemFailed") errors.push(this.describeDispatchError(event.data[0]));
8924
- }
8925
- if (errors.length !== callCount) {
8926
- return {
8927
- errors: new Array(callCount).fill(void 0),
8928
- error: `force_batch reported ${errors.length} item events for ${callCount} calls; outcomes not attributed`
8929
- };
8930
- }
8931
- return { errors };
8932
- }
8933
9022
  /** Renders a DispatchError as `pallet::Error`, falling back to its raw form. */
8934
9023
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8935
9024
  describeDispatchError(dispatchError) {
@@ -8948,10 +9037,11 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8948
9037
  */
8949
9038
  async getBidStorageEntries(commitment) {
8950
9039
  const api = await this.http();
8951
- const entries = await api.query.intentsCoprocessor.bids.entries(commitment);
9040
+ const entries = await api.query.intentsCoprocessor.orderBids.entries(commitment);
8952
9041
  return entries.map(([storageKey, depositValue]) => ({
8953
9042
  commitment,
8954
9043
  filler: storageKey.args[1].toString(),
9044
+ bid: storageKey.args[2].toHex(),
8955
9045
  deposit: BigInt(depositValue.toString())
8956
9046
  }));
8957
9047
  }
@@ -8983,7 +9073,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8983
9073
  return result.map((entry) => {
8984
9074
  const userOp = decodeUserOpScale(entry.user_op);
8985
9075
  const filler = new api.Keyring({ type: "sr25519" }).encodeAddress(util.hexToU8a(entry.filler));
8986
- return { filler, userOp, deposit: 0n };
9076
+ return { filler, bid: entry.bid, userOp, deposit: 0n };
8987
9077
  });
8988
9078
  }
8989
9079
  /**
@@ -8992,19 +9082,20 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8992
9082
  */
8993
9083
  async getBidsViaStorage(commitment) {
8994
9084
  const api = await this.http();
8995
- const entries = await api.query.intentsCoprocessor.bids.entries(commitment);
9085
+ const entries = await api.query.intentsCoprocessor.orderBids.entries(commitment);
8996
9086
  if (entries.length === 0) return [];
8997
9087
  const bidPromises = entries.map(async ([storageKey, depositValue]) => {
8998
9088
  try {
8999
9089
  const filler = storageKey.args[1].toString();
9090
+ const bid = storageKey.args[2].toHex();
9000
9091
  const deposit = BigInt(depositValue.toString());
9001
- const offchainKey = this.buildOffchainBidKey(commitment, filler);
9092
+ const offchainKey = this.buildOffchainBidKey(commitment, filler, bid);
9002
9093
  const offchainKeyHex = util.u8aToHex(offchainKey);
9003
9094
  const offchainResult = await api.rpc.offchain.localStorageGet("PERSISTENT", offchainKeyHex);
9004
9095
  if (!offchainResult || offchainResult.isNone) return null;
9005
9096
  const bidData = offchainResult.unwrap().toHex();
9006
9097
  const decoded = this.decodeBid(bidData);
9007
- return { filler: decoded.filler, userOp: decoded.userOp, deposit };
9098
+ return { filler: decoded.filler, bid, userOp: decoded.userOp, deposit };
9008
9099
  } catch {
9009
9100
  return null;
9010
9101
  }
@@ -9020,418 +9111,38 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
9020
9111
  const userOp = decodeUserOpScale(userOpHex);
9021
9112
  return { filler, userOp };
9022
9113
  }
9023
- /** Builds offchain storage key: "intents::bid::" + commitment + filler */
9024
- buildOffchainBidKey(commitment, filler) {
9025
- return util.u8aConcat(OFFCHAIN_BID_PREFIX, util.hexToU8a(commitment), utilCrypto.decodeAddress(filler));
9114
+ /** Builds offchain storage key: "intents::bid::" + commitment + filler + bid */
9115
+ buildOffchainBidKey(commitment, filler, bid) {
9116
+ return util.u8aConcat(OFFCHAIN_BID_PREFIX, util.hexToU8a(commitment), utilCrypto.decodeAddress(filler), util.hexToU8a(bid));
9026
9117
  }
9027
- /**
9028
- * Fetches the ABI-encoded phantom order from offchain storage and decodes it
9029
- * into an `Order` object. The pallet writes the order bytes under the key
9030
- * `intents::phantom::order::<commitment>` when it calls `on_initialize`.
9031
- *
9032
- * Returns `null` if the key is absent (e.g. the node is not an offchain worker
9033
- * or the commitment has expired and been cleared).
9034
- */
9035
- async fetchPhantomOrder(commitment) {
9036
- const key = util.u8aConcat(OFFCHAIN_PHANTOM_PREFIX, util.hexToU8a(commitment));
9037
- const api = await this.http();
9038
- const result = await api.rpc.offchain.localStorageGet("PERSISTENT", util.u8aToHex(key));
9039
- if (!result || result.isNone) return null;
9040
- const rawHex = result.unwrap().toHex();
9041
- if (rawHex === "0x" || rawHex === "0x00") return null;
9042
- const placeOrderAbi = IntentGatewayV2_default.ABI.find((item) => item.type === "function" && item.name === "placeOrder");
9043
- const orderType = placeOrderAbi?.inputs?.[0];
9044
- if (!orderType) return null;
9045
- const [decoded] = viem.decodeAbiParameters([orderType], rawHex);
9046
- const d = decoded;
9047
- const textDecoder = new TextDecoder();
9048
- return {
9049
- id: commitment,
9050
- user: d.user,
9051
- source: textDecoder.decode(viem.hexToBytes(d.source)),
9052
- destination: textDecoder.decode(viem.hexToBytes(d.destination)),
9053
- deadline: d.deadline,
9054
- nonce: d.nonce,
9055
- fees: d.fees,
9056
- session: d.session,
9057
- predispatch: {
9058
- assets: d.predispatch.assets.map((a) => ({
9059
- token: a.token,
9060
- amount: a.amount
9061
- })),
9062
- call: d.predispatch.call
9063
- },
9064
- inputs: d.inputs.map((i) => ({
9065
- token: i.token,
9066
- amount: i.amount
9067
- })),
9068
- output: {
9069
- beneficiary: d.output.beneficiary,
9070
- assets: d.output.assets.map((a) => ({
9071
- token: a.token,
9072
- amount: a.amount
9073
- })),
9074
- call: d.output.call
9075
- }
9076
- };
9118
+ };
9119
+ var TronChain = class _TronChain {
9120
+ constructor(params, evm) {
9121
+ this.params = params;
9122
+ this.evm = evm;
9123
+ this.tronWebInstance = new tronweb.TronWeb({ fullHost: params.rpcUrl });
9077
9124
  }
9125
+ params;
9126
+ evm;
9127
+ tronWebInstance;
9078
9128
  /**
9079
- * Reads the PhantomOrderRegistered events emitted in a single block.
9129
+ * Creates a `TronChain` instance from a fully-specified config object.
9080
9130
  *
9081
- * Costs two RPCs per block when `knownVersion` is supplied and four without it, which is why the
9082
- * poll goes to the trouble of establishing one. `api.at(hash)` has to work out which metadata to
9083
- * decode the block against, and with nothing to go on it fetches the header and then the runtime
9084
- * version at its parent — every block, forever. Its cheaper paths are a registry already pinned
9085
- * to this exact hash (only ever the previous block's) or one matching a version the caller
9086
- * names, so naming the version is the only way out. See `getBlockRegistry` in
9087
- * `@polkadot/api/base/Init`; the `getUpgradeVersion` shortcut that would otherwise skip the
9088
- * lookup only covers chains hardcoded in `@polkadot/types-known`, which Hyperbridge is not.
9131
+ * This is the only public way to construct a `TronChain` — the constructor is private.
9089
9132
  *
9090
- * @param knownVersion - the runtime version this block is known to run, if the caller has
9091
- * established one. Passing a version the block does not actually run decodes it against the
9092
- * wrong metadata, so this is for callers that have checked, not a place to pass a guess.
9133
+ * @param params - Tron chain configuration
9134
+ * @returns A `TronChain` instance
9093
9135
  */
9094
- async getPhantomOrdersInBlock(blockNumber, knownVersion) {
9095
- const api = await this.http();
9096
- const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
9097
- return await this.getPhantomOrdersAtHash(blockHash.toHex(), knownVersion);
9136
+ static async fromParams(params) {
9137
+ const evm = await EvmChain.create(params.rpcUrl, params.bundlerUrl);
9138
+ return new _TronChain(params, evm);
9098
9139
  }
9099
- /**
9100
- * The same read, for a caller that already holds the block's hash.
9101
- *
9102
- * Split out so the poll can fetch a whole range's hashes in one concurrent wave — which the
9103
- * provider coalesces into a single batched request — and then read each block's events knowing
9104
- * its hash. `chain_getBlockHash` is the half of the pair that parallelises safely: it takes no
9105
- * historic block hash, so it never triggers polkadot-js's per-hash registry resolution, and
9106
- * concurrent calls cannot race each other's registry state.
9107
- */
9108
- async getPhantomOrdersAtHash(blockHash, knownVersion) {
9109
- const api = await this.http();
9110
- const apiAt = await api.at(blockHash, knownVersion);
9111
- return phantomOrdersFrom(await apiAt.query.system.events());
9112
- }
9113
- /**
9114
- * Every block's phantom orders across a whole range, in one `state_queryStorage` call.
9115
- *
9116
- * This is the cheap path: the request cost of a scan stops depending on how many blocks it
9117
- * covers. The events key is the only key queried, and both bounds are block hashes the caller
9118
- * already holds.
9119
- *
9120
- * Two properties of the RPC shape the result.
9121
- *
9122
- * It returns *diffs*: `query_storage_unfiltered` in `sc-rpc` pushes a change set for a block only
9123
- * when the value differs from the previous block in the range (`has_changed`, and the set is
9124
- * dropped when empty), so a block whose events encode byte-for-byte identically to its
9125
- * predecessor's is simply absent. That happens on a quiet chain, where consecutive blocks carry
9126
- * nothing but the timestamp inherent's `ExtrinsicSuccess`. It is safe here because an absent
9127
- * block provably carries no phantom orders: a `PhantomOrderRegistered` commitment is derived from
9128
- * the block number (`phantom_order_commitment`), so a block that registered orders can never
9129
- * encode identically to any other block. Absent therefore means "same as the previous block",
9130
- * and the previous block having orders would contradict that.
9131
- *
9132
- * And it is gated by `--rpc-methods` (`check_if_safe` in `sc-rpc`), which answers a denied call
9133
- * with `Method not found`. The node this reads from must already run unsafe RPC to serve
9134
- * `offchain_localStorageGet` for the orders themselves, so this is normally available; the poll
9135
- * falls back to reading block by block when it is not.
9136
- *
9137
- * @returns one entry per block the node reported a change for, in ascending block order.
9138
- */
9139
- async getPhantomOrdersInRange(fromBlockHash, toBlockHash) {
9140
- const api = await this.http();
9141
- const changeSets = await api.rpc.state.queryStorage.raw(
9142
- [SYSTEM_EVENTS_KEY],
9143
- fromBlockHash,
9144
- toBlockHash
9145
- );
9146
- return changeSets.map((changeSet) => {
9147
- const value = changeSet?.changes?.find(([key]) => key === SYSTEM_EVENTS_KEY)?.[1];
9148
- if (!value) return [];
9149
- return phantomOrdersFrom(api.registry.createType("Vec<EventRecord>", value));
9150
- });
9151
- }
9152
- /**
9153
- * Polls for newly registered phantom orders, invoking the callback once per block that carries
9154
- * any, with all of that block's orders.
9155
- *
9156
- * Per block rather than per order because that is how the pallet writes them: one order per
9157
- * configured chain, all registered in the same `on_initialize`. Delivering them together lets a
9158
- * caller bid on the whole interval in one extrinsic (see {@link submitPhantomBids}) instead of
9159
- * one per chain.
9160
- *
9161
- * Each tick reads the current head and scans every block between the last one processed and that
9162
- * head, so the block cursor — not the connection — determines what has been seen. This replaced a
9163
- * system.events subscription, which was only as reliable as its socket: polkadot-js reconnects
9164
- * the transport but does not reliably re-establish storage subscriptions, and anything emitted
9165
- * while disconnected was lost silently. With a bid window measured in a handful of blocks, that
9166
- * meant silently missed bids.
9167
- *
9168
- * Scanning a block range is gap-free rather than merely self-healing: an outage delays orders but
9169
- * cannot drop them, because the cursor only advances past a block whose events were actually
9170
- * read. Recovery replays the backlog.
9171
- *
9172
- * Every read here goes over HTTP, never the websocket. Polling is a sequence of independent
9173
- * one-shot requests with no state to lose between them, which is exactly what a stateless
9174
- * transport does well: a request either answers or fails loudly on this tick, instead of a
9175
- * socket that looks alive while delivering nothing. It also means a websocket outage does not
9176
- * pause phantom bidding at all — the two transports fail independently.
9177
- *
9178
- * What the cadence does *not* describe is the request rate, which is what rate limiters police.
9179
- * A tick costs four requests whatever the range covers — the head, the runtime version, the two
9180
- * bounding block hashes as one batched request, and one `state_queryStorage` for every block's
9181
- * events — and they go out back-to-back, so an interval well under any per-second limit could
9182
- * still arrive as a burst over it. Three things keep that in bounds: the provider coalesces
9183
- * concurrent calls into one request and paces requests through the endpoint's token bucket (see
9184
- * `http`), and `maxBlocksPerPoll` bounds the range. A 429 that gets through anyway backs the
9185
- * poll off for a doubling number of ticks, so a limiter that is already shedding load is not
9186
- * handed the next window's budget in rejections too.
9187
- *
9188
- * Where the node will not serve `state_queryStorage` the poll reads block by block instead, at
9189
- * three requests plus one per block; see {@link scanRangeAtOnce}.
9190
- *
9191
- * Returns a function that stops polling.
9192
- */
9193
- pollPhantomOrders(callback, options = {}) {
9194
- const {
9195
- intervalMs,
9196
- maxBlocksPerPoll = DEFAULT_MAX_BLOCKS_PER_POLL,
9197
- onError,
9198
- onSkip
9199
- } = options;
9200
- let cursor = null;
9201
- let inFlight = false;
9202
- let stopped = false;
9203
- let backoffTicks = 0;
9204
- let backoffLength = 0;
9205
- const tick = async () => {
9206
- if (inFlight || stopped) return;
9207
- if (backoffTicks > 0) {
9208
- backoffTicks -= 1;
9209
- return;
9210
- }
9211
- inFlight = true;
9212
- try {
9213
- const api = await this.http();
9214
- const head = (await api.rpc.chain.getHeader()).number.toNumber();
9215
- if (cursor === null) {
9216
- cursor = Math.max(head - 1, -1);
9217
- } else {
9218
- const { bidWindowBlocks, intervalBlocks } = await this.phantomTimings();
9219
- if (head - cursor > bidWindowBlocks + Math.max(intervalBlocks, bidWindowBlocks)) {
9220
- const from = cursor + 1;
9221
- cursor = Math.max(head - 1 - bidWindowBlocks, -1);
9222
- onSkip?.({ from, to: cursor, head });
9223
- }
9224
- }
9225
- if (head <= cursor) return;
9226
- const knownVersion = await this.confirmedRuntimeVersion();
9227
- const to = Math.min(head, cursor + maxBlocksPerPoll);
9228
- const ranged = await this.scanRangeAtOnce(api, cursor + 1, to, knownVersion, onError);
9229
- if (ranged) {
9230
- for (const orders of ranged) {
9231
- if (stopped) return;
9232
- if (orders.length > 0) callback(orders);
9233
- }
9234
- cursor = to;
9235
- backoffLength = 0;
9236
- return;
9237
- }
9238
- const numbers = [];
9239
- for (let blockNumber = cursor + 1; blockNumber <= to; blockNumber++) numbers.push(blockNumber);
9240
- const hashes = await Promise.allSettled(
9241
- numbers.map((blockNumber) => api.rpc.chain.getBlockHash(blockNumber))
9242
- );
9243
- for (let index = 0; index < numbers.length; index++) {
9244
- if (stopped) return;
9245
- const hash = hashes[index];
9246
- if (hash.status === "rejected") throw hash.reason;
9247
- const orders = await this.getPhantomOrdersAtHash(hash.value.toHex(), knownVersion);
9248
- if (orders.length > 0) callback(orders);
9249
- cursor = numbers[index];
9250
- }
9251
- backoffLength = 0;
9252
- } catch (err) {
9253
- if (isRateLimited(err)) {
9254
- backoffLength = Math.min(backoffLength === 0 ? 1 : backoffLength * 2, MAX_RATE_LIMIT_BACKOFF_TICKS);
9255
- backoffTicks = backoffLength;
9256
- }
9257
- onError?.(err);
9258
- } finally {
9259
- inFlight = false;
9260
- }
9261
- };
9262
- void tick();
9263
- let timer = null;
9264
- const startTimer = (ms) => {
9265
- if (stopped) return;
9266
- timer = setInterval(() => void tick(), ms);
9267
- };
9268
- if (intervalMs !== void 0) startTimer(intervalMs);
9269
- else void this.phantomPollIntervalMs().then(startTimer);
9270
- return () => {
9271
- stopped = true;
9272
- if (timer) clearInterval(timer);
9273
- };
9274
- }
9275
- /**
9276
- * The Hyperbridge head, over HTTP like every other read here.
9277
- *
9278
- * Exposed for callers that have to know how old something is: a phantom order carries the block
9279
- * it was registered at, and only against the head does that become "still biddable" or "long
9280
- * expired".
9281
- */
9282
- async latestBlockNumber() {
9283
- const api = await this.http();
9284
- return (await api.rpc.chain.getHeader()).number.toNumber();
9285
- }
9286
- /**
9287
- * The pallet's phantom timings, read from chain state.
9288
- *
9289
- * Both are governance-settable and neither is derivable: on Nexus today the window is 15 while
9290
- * the runtime constant behind it is 25, so anything hard-coded is wrong in one direction or the
9291
- * other — too tight and live orders are dropped, too loose and bids are sent into a closed
9292
- * window for the pallet to reject.
9293
- *
9294
- * Read once per instance and cached, because a governance change to either is rare and a read
9295
- * per poll tick would be a request per tick forever. The cost is that a change is picked up on
9296
- * the next restart rather than immediately. A failed read is not cached, so it retries.
9297
- */
9298
- async phantomTimings() {
9299
- if (!this.phantomTimingsRead) {
9300
- this.phantomTimingsRead = this.readPhantomTimings().catch((err) => {
9301
- this.phantomTimingsRead = null;
9302
- throw err;
9303
- });
9304
- }
9305
- return this.phantomTimingsRead;
9306
- }
9307
- async readPhantomTimings() {
9308
- const api = await this.http();
9309
- const [window, interval] = await Promise.all([
9310
- api.query.intentsCoprocessor.phantomBidWindow(),
9311
- api.query.intentsCoprocessor.phantomOrderInterval()
9312
- ]);
9313
- const stored = Number(window.toString());
9314
- return {
9315
- bidWindowBlocks: stored === 0 ? Number(api.consts.intentsCoprocessor.phantomOrderBidWindowBlocks.toString()) : stored,
9316
- intervalBlocks: Number(interval.toString())
9317
- };
9318
- }
9319
- /**
9320
- * A whole range of blocks in one `state_queryStorage` call, or `null` when that is not available
9321
- * and the caller should read block by block.
9322
- *
9323
- * Two conditions have to hold, and both are about decoding rather than the range itself.
9324
- *
9325
- * The version must be confirmed for this tick — an upgrade inside the range means blocks decode
9326
- * against different metadata, and one call cannot do that.
9327
- *
9328
- * And that confirmed version must still be the one the api's own registry was built for.
9329
- * `state_queryStorage` declares no historic block hash, so rpc-core skips its registry swap and
9330
- * decodes the reply against the default registry — fixed at connect, with no
9331
- * `subscribeRuntimeVersion` on an HTTP api to refresh it. After an upgrade the two diverge, and
9332
- * the per-block path takes over for good: `api.at(hash, version)` resolves, and builds, the right
9333
- * registry. That costs a restart to get the cheap path back, which is the correct direction to
9334
- * fail in.
9335
- */
9336
- async scanRangeAtOnce(api, from, to, knownVersion, onError) {
9337
- if (this.rangeQueryUnavailable || !knownVersion) return null;
9338
- const registryVersion = api.runtimeVersion?.specVersion;
9339
- if (!registryVersion || !knownVersion.specVersion.eq(registryVersion)) return null;
9340
- const [fromHash, toHash] = from === to ? await Promise.all([api.rpc.chain.getBlockHash(from)]).then(([only]) => [only, only]) : await Promise.all([api.rpc.chain.getBlockHash(from), api.rpc.chain.getBlockHash(to)]);
9341
- try {
9342
- return await this.getPhantomOrdersInRange(fromHash.toHex(), toHash.toHex());
9343
- } catch (err) {
9344
- if (isMethodUnavailable(err)) {
9345
- this.rangeQueryUnavailable = true;
9346
- return null;
9347
- }
9348
- if (err instanceof EventDecodeError) {
9349
- this.rangeQueryUnavailable = true;
9350
- onError?.(err);
9351
- return null;
9352
- }
9353
- throw err;
9354
- }
9355
- }
9356
- /**
9357
- * The runtime version this tick's blocks may be decoded against, or `undefined` when that cannot
9358
- * be established and each block must resolve its own.
9359
- *
9360
- * Naming a version to `api.at` is what removes two of the four RPCs a block scan costs, and it
9361
- * is only sound while the version is actually the block's. Getting that wrong is not a loud
9362
- * failure: events decoded against the wrong metadata come back as a shape the scan does not
9363
- * recognise, so the block reads as carrying no phantom orders and the cursor advances past it —
9364
- * exactly the silent miss the block cursor exists to rule out.
9365
- *
9366
- * So the version is read fresh each tick and only used when it matches the previous reading.
9367
- * `specVersion` only ever increases, and this read happens *after* the head read, so two equal
9368
- * readings mean no upgrade landed anywhere in between — and therefore none in the range about to
9369
- * be scanned. A reading that differs means an upgrade landed inside the range: that tick falls
9370
- * back to per-block resolution, which is exact, and the version is used from the next tick on
9371
- * once it has been seen twice.
9372
- *
9373
- * The gap this leaves is a backlog reaching back past an upgrade, whose oldest blocks predate
9374
- * even the previous reading. Recovering from an outage that long means those bid windows closed
9375
- * many upgrades ago, so nothing is lost that was still winnable.
9376
- *
9377
- * A version that cannot be read at all yields `undefined` rather than an error: the scan is
9378
- * about to make the same request against the same endpoint and is the better place to report it.
9379
- */
9380
- async confirmedRuntimeVersion() {
9381
- let api;
9382
- let current;
9383
- try {
9384
- api = await this.http();
9385
- current = await api.rpc.state.getRuntimeVersion();
9386
- } catch {
9387
- return void 0;
9388
- }
9389
- const previous = this.lastRuntimeVersion ?? api.runtimeVersion;
9390
- this.lastRuntimeVersion = current;
9391
- if (!previous?.specVersion || !previous?.specName) return void 0;
9392
- return current.specVersion.eq(previous.specVersion) && current.specName.eq(previous.specName) ? current : void 0;
9393
- }
9394
- /**
9395
- * The poll cadence for the runtime this instance is connected to: Gargantua polls every block,
9396
- * everything else every 15s. Falls back to the slower cadence if the runtime cannot be read,
9397
- * since an unreachable node is the poll's problem to report, not the cadence lookup's.
9398
- */
9399
- async phantomPollIntervalMs() {
9400
- try {
9401
- const specName = (await this.http()).runtimeVersion.specName.toString();
9402
- return specName === "gargantua" ? GARGANTUA_PHANTOM_POLL_INTERVAL_MS : PHANTOM_POLL_INTERVAL_MS;
9403
- } catch {
9404
- return PHANTOM_POLL_INTERVAL_MS;
9405
- }
9406
- }
9407
- };
9408
- var TronChain = class _TronChain {
9409
- constructor(params, evm) {
9410
- this.params = params;
9411
- this.evm = evm;
9412
- this.tronWebInstance = new tronweb.TronWeb({ fullHost: params.rpcUrl });
9413
- }
9414
- params;
9415
- evm;
9416
- tronWebInstance;
9417
- /**
9418
- * Creates a `TronChain` instance from a fully-specified config object.
9419
- *
9420
- * This is the only public way to construct a `TronChain` — the constructor is private.
9421
- *
9422
- * @param params - Tron chain configuration
9423
- * @returns A `TronChain` instance
9424
- */
9425
- static async fromParams(params) {
9426
- const evm = await EvmChain.create(params.rpcUrl, params.bundlerUrl);
9427
- return new _TronChain(params, evm);
9428
- }
9429
- // -------------------------------------------------------------------------
9430
- // Public accessors
9431
- // -------------------------------------------------------------------------
9432
- /** Underlying viem public client (delegated from the internal EvmChain) */
9433
- get client() {
9434
- return this.evm.client;
9140
+ // -------------------------------------------------------------------------
9141
+ // Public accessors
9142
+ // -------------------------------------------------------------------------
9143
+ /** Underlying viem public client (delegated from the internal EvmChain) */
9144
+ get client() {
9145
+ return this.evm.client;
9435
9146
  }
9436
9147
  /** Host contract address for this chain (delegated from the internal EvmChain) */
9437
9148
  get host() {
@@ -10487,8 +10198,8 @@ var PharosChain = class _PharosChain {
10487
10198
  };
10488
10199
  async function generateRootWithProof(postRequest, treeSize) {
10489
10200
  const { generate_root_with_proof: generate_root_with_proof2 } = await load_ckb_mmr();
10490
- const { commitment: hash, encodePacked: encodePacked4 } = postRequestCommitment(postRequest);
10491
- const result = JSON.parse(generate_root_with_proof2(viem.hexToBytes(encodePacked4), treeSize));
10201
+ const { commitment: hash, encodePacked: encodePacked5 } = postRequestCommitment(postRequest);
10202
+ const result = JSON.parse(generate_root_with_proof2(viem.hexToBytes(encodePacked5), treeSize));
10492
10203
  const { root, proof, mmr_size, keccak_hash_calldata } = result;
10493
10204
  if (keccak_hash_calldata !== hash) {
10494
10205
  console.log("keccak_hash", keccak_hash_calldata);
@@ -10705,7 +10416,8 @@ async function retryPromise(operation, retryConfig) {
10705
10416
  return await operation();
10706
10417
  } catch (error) {
10707
10418
  if (shouldRetry && !shouldRetry(error)) throw error;
10708
- logger.trace(`Retrying(${i}) > ${logMessage}`);
10419
+ const detail = error instanceof Error ? error.message.split("\n")[0] : String(error);
10420
+ logger.trace(`Retrying(${i}) > ${logMessage}: ${detail.slice(0, 200)}`);
10709
10421
  lastError = error;
10710
10422
  if (i === retryConfig.maxRetries - 1) break;
10711
10423
  await new Promise((resolve) => setTimeout(resolve, retryConfig.backoffMs * 2 ** i));
@@ -12751,92 +12463,6 @@ query OrderStatus($commitment: String!) {
12751
12463
  }
12752
12464
  }
12753
12465
  }`;
12754
- var LATEST_PHANTOM_ORDER_PRICE_SNAPSHOT = `
12755
- query LatestPhantomOrderPriceSnapshot($tokenA: String!, $tokenB: String!) {
12756
- phantomOrderPriceSnapshots(
12757
- filter: {
12758
- and: [
12759
- { tokenA: { equalTo: $tokenA } }
12760
- { tokenB: { equalTo: $tokenB } }
12761
- { medianPrice: { isNull: false } }
12762
- ]
12763
- }
12764
- orderBy: BLOCK_NUMBER_DESC
12765
- first: 1
12766
- ) {
12767
- nodes {
12768
- commitment
12769
- tokenA
12770
- tokenB
12771
- standardAmount
12772
- blockNumber
12773
- medianPrice
12774
- lowestPrice
12775
- highestPrice
12776
- bidCount
12777
- snapshotTime
12778
- }
12779
- }
12780
- }`;
12781
- var AVAILABLE_LIQUIDITY = `
12782
- query AvailableLiquidity(
12783
- $poolId: String!
12784
- $sourceChain: String!
12785
- $destinationChain: String!
12786
- $direction: String!
12787
- ) {
12788
- poolChainLiquidities(
12789
- filter: {
12790
- and: [
12791
- { poolId: { equalToInsensitive: $poolId } }
12792
- { chain: { equalTo: $destinationChain } }
12793
- { direction: { equalTo: $direction } }
12794
- ]
12795
- }
12796
- first: 1
12797
- ) {
12798
- nodes {
12799
- depth
12800
- bidCount
12801
- unrestrictedDepth
12802
- unrestrictedBidCount
12803
- lastUpdatedAt
12804
- }
12805
- }
12806
- poolRoutes(
12807
- filter: {
12808
- and: [
12809
- { poolId: { equalToInsensitive: $poolId } }
12810
- { sourceChain: { equalTo: $sourceChain } }
12811
- { chain: { equalTo: $destinationChain } }
12812
- { direction: { equalTo: $direction } }
12813
- ]
12814
- }
12815
- first: 1
12816
- ) {
12817
- nodes {
12818
- depth
12819
- bidCount
12820
- lastUpdatedAt
12821
- }
12822
- }
12823
- }`;
12824
- var BUY_AND_SELL_RATES = `
12825
- query GetLiquidityPoolRate($poolId: String!) {
12826
- liquidityPools(
12827
- first: 1
12828
- filter: { id: { equalToInsensitive: $poolId } }
12829
- ) {
12830
- nodes {
12831
- id
12832
- token0Symbol
12833
- token1Symbol
12834
- sellRate
12835
- buyRate
12836
- lastUpdatedAt
12837
- }
12838
- }
12839
- }`;
12840
12466
  var TOKEN_GATEWAY_ASSET_TELEPORTED_STATUS = `
12841
12467
  query TokenGatewayAssetTeleportedStatus($commitment: String!) {
12842
12468
  tokenGatewayAssetTeleportedV2s(
@@ -12898,29 +12524,6 @@ async function queryAssetTeleported(params) {
12898
12524
  function queryGetRequest(params) {
12899
12525
  return _queryGetRequestInternal(params);
12900
12526
  }
12901
- async function _queryLatestPhantomOrderPriceSnapshot(params) {
12902
- const response = await params.queryClient.request(
12903
- LATEST_PHANTOM_ORDER_PRICE_SNAPSHOT,
12904
- {
12905
- tokenA: params.tokenA.toLowerCase(),
12906
- tokenB: params.tokenB.toLowerCase()
12907
- }
12908
- );
12909
- const snapshot = response.phantomOrderPriceSnapshots.nodes[0];
12910
- if (!snapshot?.medianPrice) return;
12911
- return {
12912
- commitment: snapshot.commitment,
12913
- tokenA: snapshot.tokenA,
12914
- tokenB: snapshot.tokenB,
12915
- standardAmount: BigInt(snapshot.standardAmount),
12916
- blockNumber: BigInt(snapshot.blockNumber),
12917
- medianPrice: BigInt(snapshot.medianPrice),
12918
- lowestPrice: snapshot.lowestPrice === null ? void 0 : BigInt(snapshot.lowestPrice),
12919
- highestPrice: snapshot.highestPrice === null ? void 0 : BigInt(snapshot.highestPrice),
12920
- bidCount: snapshot.bidCount,
12921
- snapshotTime: new Date(snapshot.snapshotTime)
12922
- };
12923
- }
12924
12527
  async function _queryTokenGatewayAssetTeleportedInternal(params) {
12925
12528
  const { commitmentHash, queryClient: client, logger = DEFAULT_LOGGER } = params;
12926
12529
  const response = await retryPromise(
@@ -16194,18 +15797,39 @@ var CryptoUtils = class _CryptoUtils {
16194
15797
  }
16195
15798
  /**
16196
15799
  * Derives the ERC-4337 nonce key that binds a bid UserOperation to its
16197
- * order and session key: the lower 192 bits of
16198
- * `keccak256(commitment ‖ sessionKey)`. `SolverAccount` rejects bid
16199
- * operations whose nonce key differs — this is what lets the solver sign
16200
- * the plain userOpHash while staying committed to the order and to the
16201
- * session key it bid against.
15800
+ * order, session key and calldata: the lower 192 bits of
15801
+ * `keccak256(commitment ‖ sessionKey ‖ keccak256(callData))`. `SolverAccount`
15802
+ * rejects bid operations whose nonce key differs — this is what lets the
15803
+ * solver sign the plain userOpHash while staying committed to the order and
15804
+ * to the session key it bid against.
15805
+ *
15806
+ * The calldata gives every bid its own key. A solver bidding several prices
15807
+ * on one order signs one op per price, and each is sequence 0 of its own key,
15808
+ * so they execute independently rather than in a sequence where one bid that
15809
+ * is never selected blocks the rest.
16202
15810
  *
16203
15811
  * @param commitment - The order commitment (`order.id`).
16204
15812
  * @param sessionKey - The order's session key address (`order.session`).
15813
+ * @param callData - The UserOperation's `callData`.
16205
15814
  * @returns The 192-bit nonce key as a bigint (pass to `EntryPoint.getNonce`).
16206
15815
  */
16207
- static bidNonceKey(commitment, sessionKey) {
16208
- return BigInt(viem.keccak256(viem.encodePacked(["bytes32", "address"], [commitment, sessionKey]))) & (1n << 192n) - 1n;
15816
+ static bidNonceKey(commitment, sessionKey, callData) {
15817
+ return BigInt(
15818
+ viem.keccak256(
15819
+ viem.encodePacked(["bytes32", "address", "bytes32"], [commitment, sessionKey, viem.keccak256(callData)])
15820
+ )
15821
+ ) & (1n << 192n) - 1n;
15822
+ }
15823
+ /**
15824
+ * The identifier Hyperbridge files a bid under: `keccak256` of the UserOp's
15825
+ * `callData`. It is the same hash {@link bidNonceKey} takes, so a bid's
15826
+ * identifier and its nonce key both follow from the op itself, and one
15827
+ * solver's bids on one order are kept apart for the same reason on both sides.
15828
+ *
15829
+ * @param callData - The UserOperation's `callData`.
15830
+ */
15831
+ static bidId(callData) {
15832
+ return viem.keccak256(callData);
16209
15833
  }
16210
15834
  /**
16211
15835
  * Builds the EIP-712 typed-data payload whose digest is the EntryPoint v0.8
@@ -16764,9 +16388,98 @@ var OrderPlacer = class {
16764
16388
  return { order: finalizedOrder, receipt };
16765
16389
  }
16766
16390
  };
16391
+ var TOKEN_KEYED_GATEWAY_ABI = viem.parseAbi([
16392
+ "function _orders(bytes32 commitment, address token) view returns (uint256)",
16393
+ "function _partialFills(bytes32 commitment, bytes32 token) view returns (uint256)"
16394
+ ]);
16395
+ var keyingByClient = /* @__PURE__ */ new WeakMap();
16396
+ async function readLegEscrow(client, gateway, commitment, index, token) {
16397
+ return readKeyed(
16398
+ client,
16399
+ gateway,
16400
+ (keying) => keying === "leg" ? client.readContract({
16401
+ address: gateway,
16402
+ abi: ABI3,
16403
+ functionName: "_orders",
16404
+ args: [commitment, BigInt(index)]
16405
+ }) : client.readContract({
16406
+ address: gateway,
16407
+ abi: TOKEN_KEYED_GATEWAY_ABI,
16408
+ functionName: "_orders",
16409
+ args: [commitment, bytes32ToBytes20(token)]
16410
+ })
16411
+ );
16412
+ }
16413
+ async function readLegPartialFill(client, gateway, commitment, index, token) {
16414
+ return readKeyed(
16415
+ client,
16416
+ gateway,
16417
+ (keying) => keying === "leg" ? client.readContract({
16418
+ address: gateway,
16419
+ abi: ABI3,
16420
+ functionName: "_partialFills",
16421
+ args: [commitment, BigInt(index)]
16422
+ }) : client.readContract({
16423
+ address: gateway,
16424
+ abi: TOKEN_KEYED_GATEWAY_ABI,
16425
+ functionName: "_partialFills",
16426
+ args: [commitment, token]
16427
+ })
16428
+ );
16429
+ }
16430
+ async function readKeyed(client, gateway, read) {
16431
+ let known = keyingByClient.get(client);
16432
+ if (!known) {
16433
+ known = /* @__PURE__ */ new Map();
16434
+ keyingByClient.set(client, known);
16435
+ }
16436
+ const key = gateway.toLowerCase();
16437
+ const first = known.get(key) ?? "leg";
16438
+ try {
16439
+ const value = await read(first);
16440
+ known.set(key, first);
16441
+ return value;
16442
+ } catch (error) {
16443
+ if (!isRevert(error)) throw error;
16444
+ const second = first === "leg" ? "token" : "leg";
16445
+ try {
16446
+ const value = await read(second);
16447
+ known.set(key, second);
16448
+ return value;
16449
+ } catch (secondError) {
16450
+ throw isRevert(secondError) ? error : secondError;
16451
+ }
16452
+ }
16453
+ }
16454
+ function isRevert(error) {
16455
+ let current = error;
16456
+ while (current && typeof current === "object") {
16457
+ const item = current;
16458
+ if (item.name === "ContractFunctionZeroDataError") return true;
16459
+ if (item.code === 3) return true;
16460
+ if (!item.cause || typeof item.cause !== "object") {
16461
+ return /^(?:execution reverted\b|VM Exception while processing transaction:\s*revert\b|function selector was not recognized\b)/i.test(
16462
+ item.message ?? ""
16463
+ );
16464
+ }
16465
+ current = item.cause;
16466
+ }
16467
+ return false;
16468
+ }
16469
+ var PARTIAL_FILLS_SLOT = 11n;
16470
+ function partialFillSlot(commitment, index) {
16471
+ const inner = viem.keccak256(viem.encodePacked(["bytes32", "uint256"], [commitment, PARTIAL_FILLS_SLOT]));
16472
+ return viem.keccak256(viem.encodePacked(["uint256", "bytes32"], [BigInt(index), inner]));
16473
+ }
16767
16474
 
16768
16475
  // src/protocols/intents/OrderExecutor.ts
16769
16476
  var USED_USEROPS_STORAGE_KEY = (commitment) => `used-userops:${commitment.toLowerCase()}`;
16477
+ function remainingAfter(targets, filled) {
16478
+ return targets.map((target, index) => {
16479
+ const credited = filled[index]?.amount ?? 0n;
16480
+ return { token: target.token, amount: credited >= target.amount ? 0n : target.amount - credited };
16481
+ });
16482
+ }
16770
16483
  var OrderExecutor = class {
16771
16484
  constructor(ctx, bidManager) {
16772
16485
  this.ctx = ctx;
@@ -16816,6 +16529,38 @@ var OrderExecutor = class {
16816
16529
  JSON.stringify([...usedUserOps])
16817
16530
  );
16818
16531
  }
16532
+ /**
16533
+ * Reads the order's state on its destination chain into `totalFilledAssets`: the output credited
16534
+ * per leg from `_partialFills`, never below what is already counted, so a lagging RPC node cannot
16535
+ * take back a fill this executor has seen. Other solvers can fill the order too, so the chain, not
16536
+ * this executor's own fills, says how much is left.
16537
+ *
16538
+ * Returns the terminal update once `_filled` is set: `FILLED` when every leg is complete,
16539
+ * `CANCELLED` otherwise. `_filled` is read first because progress cannot change after it is set,
16540
+ * so a finalized order is never paired with progress from before its last fill.
16541
+ */
16542
+ async syncWithDestination(order, commitment, targetAssets, totalFilledAssets) {
16543
+ const client = this.ctx.dest.client;
16544
+ const gateway = this.ctx.dest.configService.getIntentGatewayAddress(normalizeStateMachineId(order.destination));
16545
+ const finalizer = await client.readContract({
16546
+ address: gateway,
16547
+ abi: ABI3,
16548
+ functionName: "_filled",
16549
+ args: [commitment]
16550
+ });
16551
+ const filled = await Promise.all(
16552
+ order.output.assets.map(async (asset, index) => {
16553
+ const credited = await readLegPartialFill(client, gateway, commitment, index, asset.token);
16554
+ const counted = totalFilledAssets[index]?.amount ?? 0n;
16555
+ return { token: asset.token, amount: credited > counted ? credited : counted };
16556
+ })
16557
+ );
16558
+ const remainingAssets = remainingAfter(targetAssets, filled);
16559
+ if (/^0x0{40}$/i.test(finalizer)) return { totalFilledAssets: filled, remainingAssets };
16560
+ const progress = { commitment, totalFilledAssets: filled, remainingAssets };
16561
+ const final = remainingAssets.every((asset) => asset.amount === 0n) ? { status: "FILLED", selectedSolver: finalizer, ...progress } : { status: "CANCELLED", ...progress };
16562
+ return { totalFilledAssets: filled, remainingAssets, final };
16563
+ }
16819
16564
  /**
16820
16565
  * Creates a closure that computes the deduplication hash key for a
16821
16566
  * UserOperation, pre-bound to the order's destination chain and entry point.
@@ -16874,12 +16619,12 @@ var OrderExecutor = class {
16874
16619
  }
16875
16620
  if (result.fillStatus === "partial") {
16876
16621
  const filledAssets = result.filledAssets ?? [];
16877
- totalFilledAssets = totalFilledAssets.map((a) => {
16878
- const filled = filledAssets.find((f) => f.token === a.token);
16622
+ totalFilledAssets = totalFilledAssets.map((a, index) => {
16623
+ const filled = filledAssets[index];
16879
16624
  return filled ? { token: a.token, amount: a.amount + filled.amount } : { ...a };
16880
16625
  });
16881
- remainingAssets = targetAssets.map((target) => {
16882
- const filled = totalFilledAssets.find((a) => a.token === target.token);
16626
+ remainingAssets = targetAssets.map((target, index) => {
16627
+ const filled = totalFilledAssets[index];
16883
16628
  const filledAmt = filled?.amount ?? 0n;
16884
16629
  return {
16885
16630
  token: target.token,
@@ -16945,8 +16690,27 @@ var OrderExecutor = class {
16945
16690
  const usedUserOps = await this.loadUsedUserOps(commitment);
16946
16691
  const userOpHashKey = this.createUserOpHasher(order);
16947
16692
  const targetAssets = order.output.assets.map((a) => ({ token: a.token, amount: a.amount }));
16948
- let totalFilledAssets = order.output.assets.map((a) => ({ token: a.token, amount: 0n }));
16949
- let remainingAssets = order.output.assets.map((a) => ({ token: a.token, amount: a.amount }));
16693
+ let initial;
16694
+ try {
16695
+ initial = await this.syncWithDestination(
16696
+ order,
16697
+ commitment,
16698
+ targetAssets,
16699
+ order.output.assets.map((a) => ({ token: a.token, amount: 0n }))
16700
+ );
16701
+ } catch (err) {
16702
+ yield {
16703
+ status: "FAILED",
16704
+ commitment,
16705
+ error: `Could not read the order on its destination chain: ${err instanceof Error ? err.message : String(err)}`
16706
+ };
16707
+ return;
16708
+ }
16709
+ if (initial.final) {
16710
+ yield initial.final;
16711
+ return;
16712
+ }
16713
+ const { totalFilledAssets, remainingAssets } = initial;
16950
16714
  const executionStream = this.executionStream({
16951
16715
  order,
16952
16716
  sessionPrivateKey,
@@ -16978,7 +16742,7 @@ var OrderExecutor = class {
16978
16742
  if (done) return;
16979
16743
  const fed = yield value;
16980
16744
  if (value.status === "BIDS_RECEIVED") input = fed;
16981
- if (value.status === "EXPIRED" || value.status === "FILLED") return;
16745
+ if (value.status === "EXPIRED" || value.status === "FILLED" || value.status === "CANCELLED") return;
16982
16746
  }
16983
16747
  } finally {
16984
16748
  console.log(`[OrderExecutor] Tearing down streams for commitment=${commitment}`);
@@ -17037,18 +16801,29 @@ var OrderExecutor = class {
17037
16801
  }
17038
16802
  while (true) {
17039
16803
  let freshBids;
16804
+ let final;
17040
16805
  try {
17041
- const bids2 = await this.fetchBids({ commitment, solver, solverLockStartTime });
17042
- freshBids = bids2.filter(isFreshBid);
16806
+ ;
16807
+ ({ totalFilledAssets, remainingAssets, final } = await this.syncWithDestination(
16808
+ order,
16809
+ commitment,
16810
+ targetAssets,
16811
+ totalFilledAssets
16812
+ ));
16813
+ freshBids = final ? [] : (await this.fetchBids({ commitment, solver, solverLockStartTime })).filter(isFreshBid);
17043
16814
  } catch {
17044
16815
  await sleep(pollIntervalMs);
17045
16816
  continue;
17046
16817
  }
16818
+ if (final) {
16819
+ yield final;
16820
+ return;
16821
+ }
17047
16822
  if (freshBids.length === 0) {
17048
16823
  await sleep(pollIntervalMs);
17049
16824
  continue;
17050
16825
  }
17051
- const bids = this.bidManager.buildBids(order, freshBids, sessionPrivateKey);
16826
+ const bids = this.bidManager.buildBids(order, freshBids, sessionPrivateKey).filter((bid) => servesOpenLeg(bid, remainingAssets));
17052
16827
  if (bids.length === 0) {
17053
16828
  await sleep(pollIntervalMs);
17054
16829
  continue;
@@ -17091,6 +16866,10 @@ var OrderExecutor = class {
17091
16866
  }
17092
16867
  }
17093
16868
  };
16869
+ function servesOpenLeg(bid, remainingAssets) {
16870
+ if (bid.outputs.length !== remainingAssets.length) return true;
16871
+ return bid.outputs.some((output, leg) => output.amount > 0n && remainingAssets[leg].amount > 0n);
16872
+ }
17094
16873
  var OrderCanceller = class _OrderCanceller {
17095
16874
  /**
17096
16875
  * @param ctx - Shared IntentsV2 context providing the source and destination
@@ -17129,8 +16908,9 @@ var OrderCanceller = class _OrderCanceller {
17129
16908
  /**
17130
16909
  * Quotes the native token cost of a source-initiated cross-chain cancellation.
17131
16910
  *
17132
- * Constructs a mock ISMP GET request for the destination commitment slot and
17133
- * calls `quoteNative` on the source host to obtain the dispatch fee.
16911
+ * Constructs the ISMP GET request the source gateway will dispatch, one
16912
+ * `_partialFills` key per leg, and calls `quoteNative` on the source host to
16913
+ * obtain the dispatch fee.
17134
16914
  * Returns 0 for same-chain orders (no ISMP call needed).
17135
16915
  *
17136
16916
  * @param order - The order to quote.
@@ -17144,21 +16924,16 @@ var OrderCanceller = class _OrderCanceller {
17144
16924
  }
17145
16925
  const height = order.deadline + 1n;
17146
16926
  const destIntentGateway = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachine);
17147
- const slotHash = await this.ctx.dest.client.readContract({
17148
- abi: ABI3,
17149
- address: destIntentGateway,
17150
- functionName: "calculateCommitmentSlotHash",
17151
- args: [order.id]
17152
- });
17153
- const key = viem.concatHex([destIntentGateway, slotHash]);
17154
- const context = encodeWithdrawalRequest(order, order.user);
16927
+ const commitment = this.orderId(order);
16928
+ const keys = order.inputs.map((_, index) => viem.concatHex([destIntentGateway, partialFillSlot(commitment, index)]));
16929
+ const context = cancelFromSourceContext(order, commitment);
17155
16930
  const getRequest = {
17156
16931
  source: sourceStateMachine,
17157
16932
  dest: destStateMachine,
17158
16933
  from: this.ctx.source.configService.getIntentGatewayAddress(destStateMachine),
17159
16934
  nonce: await this.ctx.source.getHostNonce(),
17160
16935
  height,
17161
- keys: [key],
16936
+ keys,
17162
16937
  timeoutTimestamp: 0n,
17163
16938
  context
17164
16939
  };
@@ -17183,10 +16958,13 @@ var OrderCanceller = class _OrderCanceller {
17183
16958
  *
17184
16959
  * @param order - The order to cancel.
17185
16960
  * @param indexerClient - Indexer client used to stream ISMP request status
17186
- * updates and query state-machine heights.
17187
- * @param options - Choose the initiation side. Defaults to source-side cancellation.
16961
+ * updates and query state-machine heights. The direct same-chain route does
16962
+ * not access it.
16963
+ * @param options - Choose the initiation side. Defaults to source-side
16964
+ * cancellation. Same-chain orders always use the direct local route.
17188
16965
  * @yields {@link CancelEvent} objects describing each stage of the
17189
- * cancellation lifecycle.
16966
+ * cancellation lifecycle. The caller signs or broadcasts the yielded
16967
+ * transaction, so its signer may differ from `order.user` after expiry.
17190
16968
  */
17191
16969
  async *cancelOrder(order, indexerClient, options = {}) {
17192
16970
  const sourceStateMachine = normalizeStateMachineId(order.source);
@@ -17220,9 +16998,12 @@ var OrderCanceller = class _OrderCanceller {
17220
16998
  * Async generator that cancels an order by initiating the cancel from the
17221
16999
  * source chain.
17222
17000
  *
17223
- * **Same-chain path:** encodes a direct `cancelOrder` call, yields
17224
- * `AWAITING_CANCEL_TRANSACTION`, broadcasts the signed transaction, and
17225
- * yields `CANCELLATION_COMPLETE` after confirming the `EscrowRefunded` event.
17001
+ * **Same-chain path:** encodes a direct `cancelOrder` call with zero cancel
17002
+ * options, yields `AWAITING_CANCEL_TRANSACTION`, accepts either a signed raw
17003
+ * transaction or an already-broadcast hash, and yields `CANCELLATION_COMPLETE`
17004
+ * after confirming the `EscrowRefunded` event. The contract authorizes the
17005
+ * order user through the deadline and any caller strictly after it; the SDK
17006
+ * preserves `order.user` in calldata so a third party cannot redirect escrow.
17226
17007
  *
17227
17008
  * **Cross-chain path:**
17228
17009
  * 1. Fetches (or resumes from cache) a destination finalization proof.
@@ -17532,9 +17313,9 @@ var OrderCanceller = class _OrderCanceller {
17532
17313
  }
17533
17314
  }
17534
17315
  /**
17535
- * Polls for a finalized destination-chain state proof that demonstrates
17536
- * the order commitment slot is unset (i.e. the order was not filled before
17537
- * the deadline).
17316
+ * Polls for a finalized destination-chain state proof of every leg's
17317
+ * `_partialFills` slot, i.e. how much of the order was filled before the
17318
+ * deadline.
17538
17319
  *
17539
17320
  * Waits until the latest Hyperbridge-tracked state-machine height exceeds
17540
17321
  * `order.deadline` (or the last failed probe height) before attempting to
@@ -17563,14 +17344,9 @@ var OrderCanceller = class _OrderCanceller {
17563
17344
  const intentGatewayV2Address = this.ctx.dest.configService.getIntentGatewayAddress(
17564
17345
  this.ctx.dest.config.stateMachineId
17565
17346
  );
17566
- const orderId = this.orderId(order);
17567
- const slotHash = await this.ctx.dest.client.readContract({
17568
- abi: ABI3,
17569
- address: intentGatewayV2Address,
17570
- functionName: "calculateCommitmentSlotHash",
17571
- args: [orderId]
17572
- });
17573
- const proofHex = await this.ctx.dest.queryStateProof(latestHeight, [slotHash], intentGatewayV2Address);
17347
+ const commitment = this.orderId(order);
17348
+ const slots = order.inputs.map((_, index) => partialFillSlot(commitment, index));
17349
+ const proofHex = await this.ctx.dest.queryStateProof(latestHeight, slots, intentGatewayV2Address);
17574
17350
  const proof = {
17575
17351
  consensusStateId: this.ctx.dest.config.consensusStateId,
17576
17352
  height: latestHeight,
@@ -17768,118 +17544,135 @@ var OrderCanceller = class _OrderCanceller {
17768
17544
  return feeInDestFeeToken * 1005n / 1000n;
17769
17545
  }
17770
17546
  };
17771
- var FILL_ORDER_V1_ABI = [
17772
- {
17773
- type: "function",
17774
- name: "fillOrder",
17775
- stateMutability: "payable",
17776
- outputs: [],
17777
- inputs: [
17778
- ABI3.find((e) => e.type === "function" && e.name === "fillOrder").inputs[0],
17547
+ function cancelFromSourceContext(order, commitment) {
17548
+ return viem.encodeAbiParameters(
17549
+ [
17550
+ { type: "bytes32" },
17551
+ { type: "bytes32" },
17779
17552
  {
17780
- name: "options",
17781
- type: "tuple",
17782
- internalType: "struct FillOptions",
17553
+ type: "tuple[]",
17783
17554
  components: [
17784
- { name: "relayerFee", type: "uint256", internalType: "uint256" },
17785
- { name: "nativeDispatchFee", type: "uint256", internalType: "uint256" },
17786
- {
17787
- name: "outputs",
17788
- type: "tuple[]",
17789
- internalType: "struct TokenInfo[]",
17790
- components: [
17791
- { name: "token", type: "bytes32", internalType: "bytes32" },
17792
- { name: "amount", type: "uint256", internalType: "uint256" }
17793
- ]
17794
- }
17555
+ { name: "token", type: "bytes32" },
17556
+ { name: "amount", type: "uint256" }
17795
17557
  ]
17796
- }
17558
+ },
17559
+ { type: "uint256[]" }
17560
+ ],
17561
+ [
17562
+ commitment,
17563
+ bytes20ToBytes32(order.user),
17564
+ order.inputs.map((input) => ({ token: normalizeAddressForEvmBytes32(input.token), amount: input.amount })),
17565
+ order.output.assets.map((asset) => asset.amount)
17797
17566
  ]
17567
+ );
17568
+ }
17569
+ var FILL_ORDER_SELECTOR = "0x68ddf058";
17570
+ var SUPPORTED_INTENTS_VERSION = 3n;
17571
+ var CONTRACT_VERSION_ABI = [
17572
+ {
17573
+ type: "function",
17574
+ name: "version",
17575
+ stateMutability: "view",
17576
+ inputs: [],
17577
+ outputs: [{ name: "", type: "uint64" }]
17798
17578
  }
17799
17579
  ];
17800
- var ERC1967_IMPLEMENTATION_SLOT = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
17801
- var LEGACY_FILL_OPTIONS_IMPLEMENTATIONS = /* @__PURE__ */ new Set([
17802
- // The pre-validUntil IntentGatewayV2 implementation. One entry covers every chain: the
17803
- // protocol contracts are CREATE2-deployed, so this is the implementation address on all
17804
- // of them (confirmed with the maintainers).
17805
- "0x976b268b06f545c4a2bf44866aa2465bd8b3c67d"
17806
- ]);
17807
- var CHAINS_WITHOUT_VALID_UNTIL = /* @__PURE__ */ new Set([
17808
- 97,
17809
- // BNB testnet
17810
- 10200,
17811
- // Gnosis Chiado
17812
- 80002,
17813
- // Polygon Amoy
17814
- 84532,
17815
- // Base Sepolia
17816
- 421614,
17817
- // Arbitrum Sepolia
17818
- 688689,
17819
- // Pharos testnet
17820
- 11155111,
17821
- // Sepolia
17822
- 11155420,
17823
- // Optimism Sepolia
17824
- 420420417
17825
- // Polkadot Hub Paseo
17826
- ]);
17827
- var knownV2Gateways = /* @__PURE__ */ new Set();
17828
- function resetFillOptionsVersionCache() {
17829
- knownV2Gateways.clear();
17580
+ async function readContractVersion(client, address) {
17581
+ try {
17582
+ return await client.readContract({ address, abi: CONTRACT_VERSION_ABI, functionName: "version" });
17583
+ } catch (error) {
17584
+ if (isRevert(error)) return void 0;
17585
+ throw error;
17586
+ }
17830
17587
  }
17831
- async function resolveImplementation(client, gateway) {
17832
- const slot = await client.getStorageAt({ address: gateway, slot: ERC1967_IMPLEMENTATION_SLOT });
17833
- if (!slot || slot.length < 66) return gateway;
17834
- const addr = `0x${slot.slice(-40)}`;
17835
- return /^0x0{40}$/.test(addr) ? gateway : addr;
17588
+ async function supportsRateFills(client, gateway) {
17589
+ return await readContractVersion(client, gateway) === SUPPORTED_INTENTS_VERSION;
17836
17590
  }
17837
- async function getFillOptionsVersion(client, gateway) {
17838
- const chainId = client.chain?.id;
17839
- if (chainId !== void 0 && CHAINS_WITHOUT_VALID_UNTIL.has(chainId)) return 1;
17840
- const key = gateway.toLowerCase();
17841
- if (knownV2Gateways.has(key)) return 2;
17842
- const implementation = await resolveImplementation(client, gateway);
17843
- if (LEGACY_FILL_OPTIONS_IMPLEMENTATIONS.has(implementation.toLowerCase())) return 1;
17844
- knownV2Gateways.add(key);
17845
- return 2;
17591
+ async function assertGatewayRelease(client, gateway) {
17592
+ const version = await readContractVersion(client, gateway);
17593
+ if (version === SUPPORTED_INTENTS_VERSION) return;
17594
+ throw new Error(
17595
+ version === void 0 ? `IntentGateway ${gateway} reports no version(); this SDK requires release ${SUPPORTED_INTENTS_VERSION}` : `IntentGateway ${gateway} reports release ${String(version)}; this SDK requires release ${SUPPORTED_INTENTS_VERSION}`
17596
+ );
17846
17597
  }
17847
- function encodeFillOrder(order, options, version) {
17848
- if (version === 2) {
17849
- return viem.encodeFunctionData({
17850
- abi: ABI3,
17851
- functionName: "fillOrder",
17852
- args: [order, options]
17853
- });
17854
- }
17855
- const { relayerFee, nativeDispatchFee, outputs } = options;
17856
- return viem.encodeFunctionData({
17857
- abi: FILL_ORDER_V1_ABI,
17858
- functionName: "fillOrder",
17859
- args: [order, { relayerFee, nativeDispatchFee, outputs }]
17860
- });
17598
+ var CANONICAL_EVM_TOKEN = /^0x0{24}[0-9a-fA-F]{40}$/;
17599
+ function isCanonicalEvmToken(token) {
17600
+ return CANONICAL_EVM_TOKEN.test(token);
17601
+ }
17602
+ function validateFillQuotes(order, options) {
17603
+ const count = order.output.assets.length;
17604
+ if (!count || !Array.isArray(options.inputs) || options.inputs.length !== count || options.outputs.length !== count || order.inputs.length !== count) {
17605
+ throw new Error("Fill inputs and outputs must quote every order leg");
17606
+ }
17607
+ for (let i = 0; i < count; i++) {
17608
+ const input = options.inputs[i];
17609
+ const output = options.outputs[i];
17610
+ if (input.token.toLowerCase() !== order.inputs[i].token.toLowerCase() || output.token.toLowerCase() !== order.output.assets[i].token.toLowerCase() || !isCanonicalEvmToken(input.token) || !isCanonicalEvmToken(output.token)) {
17611
+ throw new Error("Fill quote tokens must match their order legs");
17612
+ }
17613
+ if (input.amount === 0n !== (output.amount === 0n)) {
17614
+ throw new Error("Fill quote input and output must both be zero or positive");
17615
+ }
17616
+ }
17617
+ }
17618
+ function encodeFillOrder(order, options) {
17619
+ validateFillQuotes(order, options);
17620
+ return viem.encodeFunctionData({
17621
+ abi: ABI3,
17622
+ functionName: "fillOrder",
17623
+ args: [order, options]
17624
+ });
17861
17625
  }
17862
17626
  function decodeFillOrder(data) {
17863
17627
  try {
17864
17628
  const decoded = viem.decodeFunctionData({ abi: ABI3, data });
17865
- if (decoded.functionName === "fillOrder" && decoded.args && decoded.args.length >= 2) {
17866
- return { order: decoded.args[0], options: decoded.args[1] };
17867
- }
17629
+ if (decoded.functionName !== "fillOrder" || !decoded.args || decoded.args.length < 2) return null;
17630
+ const order = decoded.args[0];
17631
+ const options = decoded.args[1];
17632
+ validateFillQuotes(order, options);
17633
+ return { order, options };
17868
17634
  } catch {
17635
+ return null;
17869
17636
  }
17870
- try {
17871
- const decoded = viem.decodeFunctionData({ abi: FILL_ORDER_V1_ABI, data });
17872
- if (decoded.functionName === "fillOrder" && decoded.args && decoded.args.length >= 2) {
17873
- const legacy = decoded.args[1];
17874
- return { order: decoded.args[0], options: { ...legacy, validUntil: 0n } };
17875
- }
17876
- } catch {
17637
+ }
17638
+ var ENTRY_POINT_EVENT_ABI = [
17639
+ { type: "event", name: "BeforeExecution", inputs: [] },
17640
+ {
17641
+ type: "event",
17642
+ name: "UserOperationEvent",
17643
+ inputs: [
17644
+ { name: "userOpHash", type: "bytes32", indexed: true },
17645
+ { name: "sender", type: "address", indexed: true },
17646
+ { name: "paymaster", type: "address", indexed: true },
17647
+ { name: "nonce", type: "uint256", indexed: false },
17648
+ { name: "success", type: "bool", indexed: false },
17649
+ { name: "actualGasCost", type: "uint256", indexed: false },
17650
+ { name: "actualGasUsed", type: "uint256", indexed: false }
17651
+ ]
17877
17652
  }
17878
- return null;
17653
+ ];
17654
+ function userOperationLogs(logs, entryPoint, userOpHash) {
17655
+ const [beforeExecution, userOperationEvent] = ENTRY_POINT_EVENT_ABI.map((event) => viem.toEventSelector(event));
17656
+ const isBoundary = (log) => log.address.toLowerCase() === entryPoint.toLowerCase() && (log.topics[0] === beforeExecution || log.topics[0] === userOperationEvent);
17657
+ const end = logs.findIndex(
17658
+ (log) => isBoundary(log) && log.topics[0] === userOperationEvent && log.topics[1]?.toLowerCase() === userOpHash.toLowerCase()
17659
+ );
17660
+ if (end === -1) throw new Error(`UserOperation ${userOpHash} is not in the transaction`);
17661
+ const { args } = viem.decodeEventLog({
17662
+ abi: ENTRY_POINT_EVENT_ABI,
17663
+ eventName: "UserOperationEvent",
17664
+ data: logs[end].data,
17665
+ topics: logs[end].topics
17666
+ });
17667
+ if (!args.success) throw new Error(`UserOperation ${userOpHash} reverted`);
17668
+ let start = end - 1;
17669
+ while (start >= 0 && !isBoundary(logs[start])) start--;
17670
+ return logs.slice(start + 1, end);
17879
17671
  }
17880
17672
  var BidImpl = class {
17881
17673
  solverAddress;
17882
17674
  outputs;
17675
+ inputs;
17883
17676
  relayerFee;
17884
17677
  nativeDispatchFee;
17885
17678
  userOp;
@@ -17902,6 +17695,7 @@ var BidImpl = class {
17902
17695
  this.sessionPrivateKey = params.sessionPrivateKey;
17903
17696
  this.solverAddress = params.fillerBid.userOp.sender;
17904
17697
  this.outputs = params.fillOptions.outputs;
17698
+ this.inputs = params.fillOptions.inputs;
17905
17699
  this.relayerFee = params.fillOptions.relayerFee;
17906
17700
  this.nativeDispatchFee = params.fillOptions.nativeDispatchFee;
17907
17701
  this.userOp = params.fillerBid.userOp;
@@ -18034,31 +17828,28 @@ var BidImpl = class {
18034
17828
  { maxRetries: 5, backoffMs: 2e3, logMessage: "Fetching user operation receipt" }
18035
17829
  );
18036
17830
  txnHash = receipt.receipt.transactionHash;
18037
- try {
18038
- const chainReceipt = await this.ctx.dest.client.waitForTransactionReceipt({
18039
- hash: txnHash,
18040
- confirmations: 1
18041
- });
18042
- const events = viem.parseEventLogs({
18043
- abi: ABI3,
18044
- logs: chainReceipt.logs,
18045
- eventName: ["OrderFilled", "PartialFill"]
18046
- });
18047
- const matched = events.find((e) => {
18048
- if (e.eventName === "OrderFilled")
18049
- return e.args.commitment.toLowerCase() === commitment.toLowerCase();
18050
- if (e.eventName === "PartialFill")
18051
- return e.args.commitment.toLowerCase() === commitment.toLowerCase();
18052
- return false;
18053
- });
18054
- if (matched?.eventName === "OrderFilled") {
18055
- fillStatus = "full";
18056
- } else if (matched?.eventName === "PartialFill") {
18057
- fillStatus = "partial";
18058
- filledAssets = matched.args.outputs ?? [];
18059
- }
18060
- } catch {
18061
- throw new Error("Failed to determine fill status from logs");
17831
+ const chainReceipt = await this.ctx.dest.client.waitForTransactionReceipt({
17832
+ hash: txnHash,
17833
+ confirmations: 1
17834
+ });
17835
+ const gateway = this.ctx.dest.configService.getIntentGatewayAddress(
17836
+ normalizeStateMachineId(this.order.destination)
17837
+ );
17838
+ const fills = viem.parseEventLogs({
17839
+ abi: ABI3,
17840
+ logs: userOperationLogs(chainReceipt.logs, entryPointAddress, userOpHash),
17841
+ eventName: ["OrderFilled", "PartialFill"]
17842
+ }).filter(
17843
+ (event) => event.address.toLowerCase() === gateway.toLowerCase() && event.args.commitment.toLowerCase() === commitment.toLowerCase()
17844
+ );
17845
+ if (fills.some((event) => event.eventName === "OrderFilled")) {
17846
+ fillStatus = "full";
17847
+ } else if (fills.length > 0) {
17848
+ fillStatus = "partial";
17849
+ filledAssets = fills[0].args.outputs.map(({ token }, index) => ({
17850
+ token,
17851
+ amount: fills.reduce((sum, event) => sum + (event.args.outputs[index]?.amount ?? 0n), 0n)
17852
+ }));
18062
17853
  }
18063
17854
  } catch (err) {
18064
17855
  throw new Error(`Failed to execute bid: ${err instanceof Error ? err.message : String(err)}`);
@@ -18125,6 +17916,20 @@ var BidManager = class {
18125
17916
  callData,
18126
17917
  paymasterAndData = "0x"
18127
17918
  } = options;
17919
+ const fills = (this.crypto.decodeERC7821Execute(callData) ?? []).filter(
17920
+ (call) => call.data.slice(0, 10).toLowerCase() === FILL_ORDER_SELECTOR
17921
+ );
17922
+ if (fills.length === 0) throw new Error("Bid calldata carries no fillOrder call");
17923
+ for (const call of fills) {
17924
+ if (!decodeFillOrder(call.data)) {
17925
+ throw new Error("Malformed fill quote; every leg requires inputs and outputs");
17926
+ }
17927
+ }
17928
+ const chain = normalizeStateMachineId(order.destination);
17929
+ const gateway = this.ctx.dest.configService.getIntentGatewayAddress(chain);
17930
+ if (!await supportsRateFills(this.ctx.dest.client, gateway)) {
17931
+ throw new Error("Fills are not supported by the destination gateway");
17932
+ }
18128
17933
  const chainId = BigInt(
18129
17934
  this.ctx.dest.client.chain?.id ?? Number.parseInt(this.ctx.dest.config.stateMachineId.split("-")[1])
18130
17935
  );
@@ -18142,10 +17947,10 @@ var BidManager = class {
18142
17947
  signature: "0x"
18143
17948
  };
18144
17949
  const nonceKey = BigInt(nonce) >> 64n;
18145
- const expectedKey = CryptoUtils.bidNonceKey(order.id, order.session);
17950
+ const expectedKey = CryptoUtils.bidNonceKey(order.id, order.session, callData);
18146
17951
  if (nonceKey !== expectedKey) {
18147
17952
  console.warn(
18148
- `[BidManager] bid nonce key does not bind the order commitment and session key; on-chain validation will fail (order=${order.id})`
17953
+ `[BidManager] bid nonce key does not bind the order commitment, session key and calldata; on-chain validation will fail (order=${order.id})`
18149
17954
  );
18150
17955
  }
18151
17956
  const solverSignature = await solverSigner.signTypedData(
@@ -18210,7 +18015,7 @@ var BidManager = class {
18210
18015
  return this.selectAndExecuteBest(order, this.buildBids(order, bids, sessionPrivateKey));
18211
18016
  }
18212
18017
  /**
18213
- * Autopilot bid selection: sorts the given bids by output value, simulates
18018
+ * Autopilot bid selection: sorts the given bids by rate, simulates
18214
18019
  * each in order and attempts execution. Any bid that fails simulation or
18215
18020
  * execution is skipped once so the next ranked bid can be attempted in the
18216
18021
  * same round. For consumers that do not need custom selection logic.
@@ -18265,35 +18070,58 @@ var BidManager = class {
18265
18070
  throw new Error("No bids passed simulation and execution");
18266
18071
  }
18267
18072
  /**
18268
- * Sorts a list of bids for the given order by output value.
18073
+ * Sorts bids by rate, best to worst, independently of how much each one fills.
18269
18074
  *
18270
- * Delegates to one of three strategies based on the order's output token
18271
- * composition:
18272
- * - Single output token: sort by offered amount descending.
18273
- * - All stable outputs (USDC/USDT): sort by normalised USD value descending.
18274
- * - Mixed outputs: sort by DEX-quoted USD value descending, with a raw-amount
18275
- * fallback if pricing fails.
18075
+ * A leg's rate is what the bid pays out for what it takes: `output / input` for
18076
+ * that leg. Every leg of an order trades the same pair, so a bid's rate is its
18077
+ * total output over its total input across the legs it quotes. Rates are
18078
+ * compared exactly, by cross-multiplication; equal rates keep arrival order.
18276
18079
  *
18277
- * Bids that cannot satisfy the order's token set are dropped.
18080
+ * A bid is dropped when it does not quote the order's legs one for one, names
18081
+ * another token on a leg, quotes an input without an output (or the reverse),
18082
+ * quotes no leg at all, or quotes a leg below the rate the order requires.
18278
18083
  *
18279
- * @param order - The placed order whose output spec drives sorting logic.
18084
+ * @param order - The placed order the bids compete to fill.
18280
18085
  * @param bids - Executable bids to sort (from {@link buildBids}).
18281
- * @returns Sorted array of `Bid` objects ready for simulation.
18086
+ * @returns The valid bids, best rate first, ready for simulation.
18282
18087
  */
18283
18088
  async sortBids(order, bids) {
18284
- const outputs = order.output.assets;
18285
- if (outputs.length <= 1) {
18286
- console.log(`[BidManager] Using single-output sorting (1 output asset)`);
18287
- return this.sortSingleOutput(bids, outputs[0]);
18288
- }
18289
- const chainId = this.ctx.dest.config.stateMachineId;
18290
- const allStables = outputs.every((o) => this.isStableToken(bytes32ToBytes20(o.token), chainId));
18291
- if (allStables) {
18292
- console.log(`[BidManager] Using all-stables sorting (${outputs.length} stable output assets)`);
18293
- return this.sortAllStables(bids, outputs, chainId);
18089
+ const legs = order.inputs.length;
18090
+ const ranked = [];
18091
+ for (const [index, bid] of bids.entries()) {
18092
+ if (bid.inputs.length !== legs || bid.outputs.length !== legs || order.output.assets.length !== legs) {
18093
+ console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: does not quote the order's legs`);
18094
+ continue;
18095
+ }
18096
+ let input = 0n;
18097
+ let output = 0n;
18098
+ let reason = "";
18099
+ for (let leg = 0; leg < legs && !reason; leg++) {
18100
+ const take = bid.inputs[leg];
18101
+ const pay = bid.outputs[leg];
18102
+ const escrow = order.inputs[leg];
18103
+ const required = order.output.assets[leg];
18104
+ if (take.token.toLowerCase() !== escrow.token.toLowerCase()) reason = `input token mismatch on leg ${leg}`;
18105
+ else if (pay.token.toLowerCase() !== required.token.toLowerCase()) reason = `output token mismatch on leg ${leg}`;
18106
+ else if (take.amount < 0n || pay.amount < 0n || take.amount === 0n !== (pay.amount === 0n))
18107
+ reason = `no valid quote on leg ${leg}`;
18108
+ else if (pay.amount * escrow.amount < take.amount * required.amount) reason = `rate below the order on leg ${leg}`;
18109
+ input += take.amount;
18110
+ output += pay.amount;
18111
+ }
18112
+ if (!reason && input === 0n) reason = "quotes no leg";
18113
+ if (reason) {
18114
+ console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: ${reason}`);
18115
+ continue;
18116
+ }
18117
+ ranked.push({ bid, input, output, index });
18294
18118
  }
18295
- console.log(`[BidManager] Using mixed-output sorting (${outputs.length} output assets, some non-stable)`);
18296
- return this.sortMixedOutputs(bids, outputs, chainId);
18119
+ ranked.sort((a, b) => {
18120
+ const left = a.output * b.input;
18121
+ const right = b.output * a.input;
18122
+ return left === right ? a.index - b.index : left > right ? -1 : 1;
18123
+ });
18124
+ return ranked.map(({ bid }) => bid);
18297
18125
  }
18298
18126
  /**
18299
18127
  * Extracts the `FillOptions` struct from a single bid's ERC-7821
@@ -18316,150 +18144,6 @@ var BidManager = class {
18316
18144
  }
18317
18145
  return null;
18318
18146
  }
18319
- /**
18320
- * Case A: single output token.
18321
- * Filter bids by token match only, sort descending by amount.
18322
- * Partial fill bids are allowed — the contract determines fill status.
18323
- */
18324
- sortSingleOutput(bids, requiredAsset) {
18325
- const requiredAmount = new Decimal2__default.default(requiredAsset.amount.toString());
18326
- console.log(
18327
- `[BidManager] sortSingleOutput: required token=${requiredAsset.token}, amount=${requiredAmount.toString()}`
18328
- );
18329
- const validBids = [];
18330
- for (const bid of bids) {
18331
- const bidOutput = bid.outputs[0];
18332
- const bidAmount = new Decimal2__default.default(bidOutput.amount.toString());
18333
- if (bidOutput.token.toLowerCase() !== requiredAsset.token.toLowerCase()) {
18334
- console.warn(
18335
- `[BidManager] Bid from solver=${bid.solverAddress} REJECTED: token mismatch (bid=${bidOutput.token}, required=${requiredAsset.token})`
18336
- );
18337
- continue;
18338
- }
18339
- if (bidAmount.lt(requiredAmount)) {
18340
- console.log(
18341
- `[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidAmount.toString()}, required=${requiredAmount.toString()}, covers=${bidAmount.div(requiredAmount).mul(100).toFixed(2)}%)`
18342
- );
18343
- } else {
18344
- console.log(
18345
- `[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: amount=${bidAmount.toString()} (surplus=${bidAmount.minus(requiredAmount).toString()})`
18346
- );
18347
- }
18348
- validBids.push({ bid, amount: bidOutput.amount });
18349
- }
18350
- validBids.sort((a, b) => {
18351
- const aAmt = new Decimal2__default.default(a.amount.toString());
18352
- const bAmt = new Decimal2__default.default(b.amount.toString());
18353
- return bAmt.comparedTo(aAmt);
18354
- });
18355
- return validBids.map(({ bid }) => bid);
18356
- }
18357
- /**
18358
- * Case B: all outputs are USDC/USDT.
18359
- * Sum normalised USD values (treating each stable as $1) and sort descending.
18360
- * Partial fill bids are allowed.
18361
- */
18362
- sortAllStables(bids, orderOutputs, chainId) {
18363
- const requiredUsd = this.computeStablesUsdValue(orderOutputs, chainId);
18364
- console.log(`[BidManager] sortAllStables: required USD value=${requiredUsd.toString()}`);
18365
- const validBids = [];
18366
- for (const bid of bids) {
18367
- const bidUsd = this.computeStablesUsdValue(bid.outputs, chainId);
18368
- if (bidUsd === null) {
18369
- console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: unable to compute USD value`);
18370
- continue;
18371
- }
18372
- if (bidUsd.lt(requiredUsd)) {
18373
- console.log(
18374
- `[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidUsd.toString()}, required=${requiredUsd.toString()}, covers=${bidUsd.div(requiredUsd).mul(100).toFixed(2)}%)`
18375
- );
18376
- } else {
18377
- console.log(`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: USD value=${bidUsd.toString()}`);
18378
- }
18379
- validBids.push({ bid, usdValue: bidUsd });
18380
- }
18381
- validBids.sort((a, b) => b.usdValue.comparedTo(a.usdValue));
18382
- return validBids.map(({ bid }) => bid);
18383
- }
18384
- /**
18385
- * Case C: mixed output tokens (at least one non-stable).
18386
- * Price every token via on-chain DEX quotes, fall back to raw amounts
18387
- * if pricing is unavailable. Partial fill bids are allowed.
18388
- */
18389
- async sortMixedOutputs(bids, orderOutputs, chainId) {
18390
- const requiredUsd = await this.computeOutputsUsdValue(orderOutputs, chainId);
18391
- if (requiredUsd === null) {
18392
- console.warn("[BidManager] sortMixedOutputs: output tokens unpriceable, falling back to raw-amount sort");
18393
- return this.sortByRawAmountFallback(bids, orderOutputs);
18394
- }
18395
- console.log(`[BidManager] sortMixedOutputs: required USD value=${requiredUsd.toString()}`);
18396
- const validBids = [];
18397
- for (const bid of bids) {
18398
- const bidUsd = await this.computeOutputsUsdValue(bid.outputs, chainId);
18399
- if (bidUsd === null) {
18400
- console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: unable to price mixed outputs`);
18401
- continue;
18402
- }
18403
- if (bidUsd.lt(requiredUsd)) {
18404
- console.log(
18405
- `[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidUsd.toString()}, required=${requiredUsd.toString()}, covers=${bidUsd.div(requiredUsd).mul(100).toFixed(2)}%)`
18406
- );
18407
- } else {
18408
- console.log(
18409
- `[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: mixed USD value=${bidUsd.toString()}`
18410
- );
18411
- }
18412
- validBids.push({ bid, usdValue: bidUsd });
18413
- }
18414
- validBids.sort((a, b) => b.usdValue.comparedTo(a.usdValue));
18415
- return validBids.map(({ bid }) => bid);
18416
- }
18417
- /**
18418
- * Fallback when DEX pricing is unavailable.
18419
- * Computes total spread per bid. Bids missing a required token are rejected.
18420
- * Bids offering less than required for a token are allowed (partial fill).
18421
- * Sorted by total offered amount descending.
18422
- */
18423
- sortByRawAmountFallback(bids, orderOutputs) {
18424
- console.log(
18425
- `[BidManager] sortByRawAmountFallback: checking ${bids.length} bid(s) against ${orderOutputs.length} required output(s)`
18426
- );
18427
- const validBids = [];
18428
- for (const bid of bids) {
18429
- let valid = true;
18430
- let totalOffered = new Decimal2__default.default(0);
18431
- let rejectReason = "";
18432
- for (const required of orderOutputs) {
18433
- const matching = bid.outputs.find((o) => o.token.toLowerCase() === required.token.toLowerCase());
18434
- if (!matching) {
18435
- valid = false;
18436
- rejectReason = `missing output token=${required.token}`;
18437
- break;
18438
- }
18439
- totalOffered = totalOffered.plus(new Decimal2__default.default(matching.amount.toString()));
18440
- }
18441
- if (!valid) {
18442
- console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED (fallback): ${rejectReason}`);
18443
- continue;
18444
- }
18445
- const totalRequired = orderOutputs.reduce(
18446
- (acc, o) => acc.plus(new Decimal2__default.default(o.amount.toString())),
18447
- new Decimal2__default.default(0)
18448
- );
18449
- if (totalOffered.lt(totalRequired)) {
18450
- console.log(
18451
- `[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (fallback) (offered=${totalOffered.toString()}, required=${totalRequired.toString()}, covers=${totalOffered.div(totalRequired).mul(100).toFixed(2)}%)`
18452
- );
18453
- } else {
18454
- console.log(
18455
- `[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED (fallback): totalOffered=${totalOffered.toString()}`
18456
- );
18457
- }
18458
- validBids.push({ bid, totalOffered });
18459
- }
18460
- validBids.sort((a, b) => b.totalOffered.comparedTo(a.totalOffered));
18461
- return validBids.map(({ bid }) => bid);
18462
- }
18463
18147
  // ── Token classification helpers ──────────────────────────────────
18464
18148
  /**
18465
18149
  * Returns `true` if `tokenAddr` is either USDC or USDT on the given chain.
@@ -18490,23 +18174,6 @@ var BidManager = class {
18490
18174
  return configService.getUsdtDecimals(chainId);
18491
18175
  }
18492
18176
  // ── Basket valuation helpers ──────────────────────────────────────
18493
- /**
18494
- * Sums the USD value of a basket of stable tokens (USDC/USDT only),
18495
- * normalising each amount by its decimal count and treating each token as $1.
18496
- *
18497
- * @param outputs - List of token/amount pairs where every token is a stable.
18498
- * @param chainId - State-machine ID used to look up decimals.
18499
- * @returns Total USD value as a `Decimal`.
18500
- */
18501
- computeStablesUsdValue(outputs, chainId) {
18502
- let total = new Decimal2__default.default(0);
18503
- for (const output of outputs) {
18504
- const tokenAddr = bytes32ToBytes20(output.token);
18505
- const decimals = this.getStableDecimals(tokenAddr, chainId);
18506
- total = total.plus(new Decimal2__default.default(output.amount.toString()).div(new Decimal2__default.default(10).pow(decimals)));
18507
- }
18508
- return total;
18509
- }
18510
18177
  /**
18511
18178
  * Prices every token in the basket via on-chain DEX quotes (token → USDC).
18512
18179
  * Stables are valued at $1. Non-stables are quoted through Uniswap
@@ -18521,6 +18188,7 @@ var BidManager = class {
18521
18188
  const { asset: wethAddr } = configService.getWrappedNativeAssetWithDecimals(chainId);
18522
18189
  let totalUsd = new Decimal2__default.default(0);
18523
18190
  for (const output of outputs) {
18191
+ if (output.amount === 0n) continue;
18524
18192
  const tokenAddr = bytes32ToBytes20(output.token);
18525
18193
  if (this.isStableToken(tokenAddr, chainId)) {
18526
18194
  const decimals = this.getStableDecimals(tokenAddr, chainId);
@@ -18644,6 +18312,14 @@ var GasEstimator = class {
18644
18312
  */
18645
18313
  async estimateFillOrder(params, pricingOptions = {}) {
18646
18314
  const { order } = params;
18315
+ if (params.outputs !== void 0 && params.inputs === void 0) {
18316
+ throw new Error("Custom output quotes require explicit inputs");
18317
+ }
18318
+ const inputs = (params.inputs ?? order.inputs).map((input) => ({
18319
+ ...input,
18320
+ token: normalizeAddressForEvmBytes32(input.token)
18321
+ }));
18322
+ const quotedOutputs = params.outputs ?? order.output.assets;
18647
18323
  const orderFeeGasPriceBumpPercent = pricingOptions.orderFeeGasPriceBumpPercent ?? 0n;
18648
18324
  const solverPrivateKey = accounts.generatePrivateKey();
18649
18325
  const solverAccountAddress = accounts.privateKeyToAddress(solverPrivateKey);
@@ -18652,7 +18328,7 @@ var GasEstimator = class {
18652
18328
  const intentGatewayV2Address = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachineId);
18653
18329
  const entryPointAddress = this.ctx.dest.configService.getEntryPointV08Address(destStateMachineId);
18654
18330
  const chainId = BigInt(Number.parseInt(destStateMachineId.split("-")[1]));
18655
- const totalEthValue = order.output.assets.filter((output) => bytes32ToBytes20(output.token) === ADDRESS_ZERO2).reduce((sum, output) => sum + output.amount, 0n);
18331
+ const totalEthValue = quotedOutputs.filter((output) => bytes32ToBytes20(output.token) === ADDRESS_ZERO2).reduce((sum, output) => sum + output.amount, 0n);
18656
18332
  const [sourceFeeToken, destFeeToken, gasPrice, latestBlock] = await Promise.all([
18657
18333
  getFeeToken(this.ctx, this.ctx.source.config.stateMachineId, this.ctx.source),
18658
18334
  getFeeToken(this.ctx, this.ctx.dest.config.stateMachineId, this.ctx.dest),
@@ -18661,7 +18337,7 @@ var GasEstimator = class {
18661
18337
  ]);
18662
18338
  const baseFeePerGas = latestBlock.baseFeePerGas ?? gasPrice;
18663
18339
  const feeTokenAsBytes32 = bytes20ToBytes32(destFeeToken.address);
18664
- const assetsForOverrides = [...order.output.assets];
18340
+ const assetsForOverrides = [...quotedOutputs];
18665
18341
  if (!assetsForOverrides.some((asset) => asset.token.toLowerCase() === feeTokenAsBytes32.toLowerCase())) {
18666
18342
  assetsForOverrides.push({ token: feeTokenAsBytes32, amount: 0n });
18667
18343
  }
@@ -18684,6 +18360,7 @@ var GasEstimator = class {
18684
18360
  ]);
18685
18361
  const { bundler: bundlerStateOverrides } = stateOverridesResult;
18686
18362
  const fillOptions = {
18363
+ inputs,
18687
18364
  relayerFee: crossChainFees.postRequestFee,
18688
18365
  // Always dispatch with the fee token (see the method docs).
18689
18366
  nativeDispatchFee: 0n,
@@ -18691,7 +18368,7 @@ var GasEstimator = class {
18691
18368
  // bound here would only risk the estimate reverting on a slow bundler round trip.
18692
18369
  // The caller sets the real one on the options it actually signs.
18693
18370
  validUntil: 0n,
18694
- outputs: order.output.assets.map((asset) => ({
18371
+ outputs: quotedOutputs.map((asset) => ({
18695
18372
  ...asset,
18696
18373
  token: normalizeAddressForEvmBytes32(asset.token)
18697
18374
  }))
@@ -18703,12 +18380,8 @@ var GasEstimator = class {
18703
18380
  let maxFeePerGas = gasPrice + gasPrice * BigInt(maxFeeBumpPercent) / 100n;
18704
18381
  const orderForEstimation = { ...order, session: solverAccountAddress };
18705
18382
  const commitment = orderCommitment(orderForEstimation);
18706
- const fillOptionsVersion = await getFillOptionsVersion(this.ctx.dest.client, intentGatewayV2Address);
18707
- const fillOrderCalldata = encodeFillOrder(
18708
- transformOrderForContract(orderForEstimation),
18709
- fillOptions,
18710
- fillOptionsVersion
18711
- );
18383
+ await assertGatewayRelease(this.ctx.dest.client, intentGatewayV2Address);
18384
+ const fillOrderCalldata = encodeFillOrder(transformOrderForContract(orderForEstimation), fillOptions);
18712
18385
  let callGasLimit = 500000n;
18713
18386
  let verificationGasLimit = 100000n;
18714
18387
  let preVerificationGas = 100000n;
@@ -18855,7 +18528,8 @@ var GasEstimator = class {
18855
18528
  totalGasCostWei,
18856
18529
  totalGasInFeeToken: totalGasInSourceFeeToken,
18857
18530
  relayerFeeInSourceFeeToken: crossChainFees.relayerFeeInSourceFeeToken,
18858
- fillOptions
18531
+ fillOptions,
18532
+ inputs
18859
18533
  };
18860
18534
  }
18861
18535
  /**
@@ -19018,35 +18692,47 @@ var OrderStatusChecker = class {
19018
18692
  }
19019
18693
  ctx;
19020
18694
  /**
19021
- * Checks if a V2 order has been filled by reading the commitment storage slot on the destination chain.
19022
- *
19023
- * Reads the storage slot returned by `calculateCommitmentSlotHash` on the IntentGatewayV2 contract.
19024
- * A non-zero value at that slot means the solver has called `fillOrder` and the order is complete
19025
- * from the user's perspective (the beneficiary has received their tokens).
18695
+ * Whether the order is finalized on the destination chain, read from the gateway's `_filled`
18696
+ * getter. It is set by the completing fill, and by a cancellation, which records the refund
18697
+ * beneficiary there. A partially filled order reads false; see {@link getFillProgress}.
19026
18698
  *
19027
18699
  * @param order - The V2 order to check. `order.id` is used as the commitment; if not set it is computed.
19028
- * @returns True if the order has been filled on the destination chain, false otherwise.
19029
18700
  */
19030
18701
  async isOrderFilled(order) {
19031
18702
  const commitment = order.id ?? orderCommitment(order);
19032
- const destStateMachineId = viem.isHex(order.destination) ? viem.hexToString(order.destination) : order.destination;
19033
- const intentGatewayV2Address = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachineId);
19034
- const filledSlot = await this.ctx.dest.client.readContract({
18703
+ const gateway = this.ctx.dest.configService.getIntentGatewayAddress(destinationStateMachine(order));
18704
+ const finalizer = await this.ctx.dest.client.readContract({
19035
18705
  abi: ABI3,
19036
- address: intentGatewayV2Address,
19037
- functionName: "calculateCommitmentSlotHash",
18706
+ address: gateway,
18707
+ functionName: "_filled",
19038
18708
  args: [commitment]
19039
18709
  });
19040
- const filledStatus = await this.ctx.dest.client.getStorageAt({
19041
- address: intentGatewayV2Address,
19042
- slot: filledSlot
19043
- });
19044
- return filledStatus !== "0x0000000000000000000000000000000000000000000000000000000000000000";
18710
+ return !/^0x0{40}$/i.test(finalizer);
18711
+ }
18712
+ /**
18713
+ * Output credited to the order so far, one entry per leg, read from `_partialFills` on the
18714
+ * destination chain. Several solvers can each fill a slice of a same-chain or cross-chain order,
18715
+ * so this, not {@link isOrderFilled}, says how far along an open order is. Surplus paid above
18716
+ * the order's rate is not counted.
18717
+ *
18718
+ * @param order - The V2 order to check. `order.id` is used as the commitment; if not set it is computed.
18719
+ */
18720
+ async getFillProgress(order) {
18721
+ const commitment = order.id ?? orderCommitment(order);
18722
+ const gateway = this.ctx.dest.configService.getIntentGatewayAddress(destinationStateMachine(order));
18723
+ return Promise.all(
18724
+ order.output.assets.map(async (asset, index) => ({
18725
+ token: asset.token,
18726
+ amount: await readLegPartialFill(this.ctx.dest.client, gateway, commitment, index, asset.token)
18727
+ }))
18728
+ );
19045
18729
  }
19046
18730
  /**
19047
18731
  * Checks if a V2 order has been refunded by reading the `_orders` mapping on the source chain.
19048
18732
  *
19049
- * Calls `_orders(commitment, tokenAddress)` for each input token. When the order is placed the
18733
+ * Calls `_orders(commitment, index)` for each input: escrow is held per leg, keyed by the input's
18734
+ * index, so inputs that repeat a token are read separately. A gateway not yet upgraded to per-leg
18735
+ * escrow is read by token instead, see {@link readLegEscrow}. When the order is placed the
19050
18736
  * escrowed amounts are stored there. After a successful refund the contract zeroes them out.
19051
18737
  * An order is considered refunded when all escrowed input amounts have been returned (i.e. are 0).
19052
18738
  *
@@ -19058,14 +18744,14 @@ var OrderStatusChecker = class {
19058
18744
  const commitment = order.id ?? orderCommitment(order);
19059
18745
  const sourceStateMachineId = viem.isHex(order.source) ? viem.hexToString(order.source) : order.source;
19060
18746
  const intentGatewayV2Address = this.ctx.source.configService.getIntentGatewayAddress(sourceStateMachineId);
19061
- for (const input of order.inputs) {
19062
- const tokenAddress = bytes32ToBytes20(input.token);
19063
- const escrowedAmount = await this.ctx.source.client.readContract({
19064
- abi: ABI3,
19065
- address: intentGatewayV2Address,
19066
- functionName: "_orders",
19067
- args: [commitment, tokenAddress]
19068
- });
18747
+ for (let index = 0; index < order.inputs.length; index++) {
18748
+ const escrowedAmount = await readLegEscrow(
18749
+ this.ctx.source.client,
18750
+ intentGatewayV2Address,
18751
+ commitment,
18752
+ index,
18753
+ order.inputs[index].token
18754
+ );
19069
18755
  if (escrowedAmount !== 0n) {
19070
18756
  return false;
19071
18757
  }
@@ -19073,726 +18759,466 @@ var OrderStatusChecker = class {
19073
18759
  return true;
19074
18760
  }
19075
18761
  };
19076
-
19077
- // src/protocols/intents/liquidity-pool.ts
19078
- function sortPoolSymbols(symbolA, symbolB) {
19079
- return symbolA.toLowerCase() <= symbolB.toLowerCase() ? [symbolA, symbolB] : [symbolB, symbolA];
18762
+ function destinationStateMachine(order) {
18763
+ return viem.isHex(order.destination) ? viem.hexToString(order.destination) : order.destination;
19080
18764
  }
19081
- function poolSlug(symbolA, symbolB) {
19082
- return sortPoolSymbols(symbolA, symbolB).join("-");
19083
- }
19084
- function resolveLiquidityPool(symbolA, symbolB) {
19085
- const [token0Symbol, token1Symbol] = sortPoolSymbols(symbolA, symbolB);
19086
- return {
19087
- poolId: `${token0Symbol}-${token1Symbol}`,
19088
- token0Symbol,
19089
- token1Symbol
19090
- };
18765
+ var ORDERBOOK_URLS = {
18766
+ mainnet: "https://orderbook.hyperfx.finance/mainnet/graphql",
18767
+ testnet: "https://orderbook.hyperfx.finance/testnet/graphql"
18768
+ };
18769
+ function orderbookUrlFor(stateMachineId) {
18770
+ return TESTNET_CHAINS.has(stateMachineId) ? ORDERBOOK_URLS.testnet : ORDERBOOK_URLS.mainnet;
19091
18771
  }
19092
-
19093
- // src/protocols/intents/LiquidityEngine.ts
19094
- var INDEXER_FIXED_POINT_DECIMALS = 18;
19095
- var POOL_RATE_SCALE = 10n ** 18n;
19096
- var SELL = "SELL";
19097
- var BUY = "BUY";
19098
- var USD_STABLE_SYMBOLS = /* @__PURE__ */ new Set(["USDC", "USDT"]);
19099
- var LiquidityEngine = class {
19100
- constructor(queryClient) {
19101
- this.queryClient = queryClient;
19102
- }
19103
- queryClient;
18772
+ var ORDERBOOK_DECIMALS = 18;
18773
+ var ORDERBOOK_SCALE = 10n ** BigInt(ORDERBOOK_DECIMALS);
18774
+ var OrderbookRequestError = class extends Error {
18775
+ constructor(message) {
18776
+ super(`HyperFX orderbook request failed: ${message}`);
18777
+ this.name = "OrderbookRequestError";
18778
+ }
18779
+ };
18780
+ var RATE_FIELDS = "side fillChain rate depthIn depthOut backingLiquidity sourceChains orderCount solverCount";
18781
+ var TOP_OF_BOOK_QUERY = `
18782
+ query TopOfBook($tokenA: String!, $tokenB: String!, $fillChain: String!, $sourceChain: String!) {
18783
+ books { id base quote }
18784
+ aToB: bestRate(tokenIn: $tokenA, tokenOut: $tokenB, fillChain: $fillChain, sourceChain: $sourceChain) { ${RATE_FIELDS} }
18785
+ bToA: bestRate(tokenIn: $tokenB, tokenOut: $tokenA, fillChain: $fillChain, sourceChain: $sourceChain) { ${RATE_FIELDS} }
18786
+ }`;
18787
+ var ROUTE_LIQUIDITY_QUERY = `
18788
+ query RouteLiquidity($route: RouteInput!) {
18789
+ books { id base quote }
18790
+ routeLiquidity(route: $route) {
18791
+ route bestRate slippageBps depthIn depthOut availableLiquidity maxFillableIn orderCount solverCount
18792
+ }
18793
+ }`;
18794
+ var QUOTE_QUERY = `
18795
+ query Quote($route: RouteInput!, $amountIn: BigInt!) {
18796
+ quote(route: $route, amountIn: $amountIn) {
18797
+ route side amountIn slippageBps fillable maxFillableIn
18798
+ fills { advertisedSize orderRate amountIn amountOut }
18799
+ }
18800
+ }`;
18801
+ var QUOTE_PESSIMISTIC_QUERY = `
18802
+ query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
18803
+ quotePessimistic(route: $route, amountIn: $amountIn) {
18804
+ route side amountIn amountOut rate priceBucket slippageBps fillable maxFillableIn
18805
+ }
18806
+ }`;
18807
+ var ORDERBOOK_QUERIES = {
18808
+ topOfBook: TOP_OF_BOOK_QUERY,
18809
+ routeLiquidity: ROUTE_LIQUIDITY_QUERY,
18810
+ quote: QUOTE_QUERY,
18811
+ quotePessimistic: QUOTE_PESSIMISTIC_QUERY
18812
+ };
18813
+ var HyperFxOrderbook = class {
18814
+ client;
18815
+ /** @param urlOrClient - The orderbook's GraphQL URL, or a configured `graphql-request` client. */
18816
+ constructor(urlOrClient = ORDERBOOK_URLS.mainnet) {
18817
+ this.client = typeof urlOrClient === "string" ? new graphqlRequest.GraphQLClient(urlOrClient) : urlOrClient;
18818
+ }
19104
18819
  /**
19105
- * Returns liquidity reachable from one source chain on one destination.
19106
- *
19107
- * The caller resolves chain-specific token addresses through chain
19108
- * configuration; this layer only maps those configured symbols onto the
19109
- * indexer's canonical pool and route fields.
19110
- *
19111
- * Destination, unrestricted, and explicit-route capacity are returned as
19112
- * separate values so callers can apply their own source-chain policy.
19113
- *
19114
- * @returns `undefined` only when the indexer has not published a destination
19115
- * pool sample yet.
19116
- */
19117
- async getAvailableLiquidity(params) {
19118
- const pool = resolveLiquidityPool(params.source.symbol, params.destination.symbol);
19119
- const direction = params.source.symbol.toLowerCase() === pool.token0Symbol.toLowerCase() ? SELL : BUY;
19120
- const variables = {
19121
- poolId: pool.poolId,
19122
- sourceChain: params.source.chain,
19123
- destinationChain: params.destination.chain,
19124
- direction
18820
+ * The best bid and ask on `fillChain` for the pair `tokenA`/`tokenB`, counting
18821
+ * only orders a swap from `sourceChain` can use.
18822
+ */
18823
+ async topOfBook(params) {
18824
+ const data = await this.request(TOP_OF_BOOK_QUERY, params);
18825
+ const book = findBook(data.books, params.tokenA, params.tokenB);
18826
+ const rates = [data.aToB, data.bToA].filter((rate) => rate !== null).map(parseRate);
18827
+ return {
18828
+ book,
18829
+ bid: rates.find((rate) => rate.side === "BID") ?? null,
18830
+ ask: rates.find((rate) => rate.side === "ASK") ?? null
19125
18831
  };
19126
- const response = await this.queryClient.request(AVAILABLE_LIQUIDITY, variables);
19127
- if (!response?.poolChainLiquidities?.nodes || !response?.poolRoutes?.nodes) {
19128
- throw new InvalidLiquidityIndexerResponseError("liquidity connections are missing");
19129
- }
19130
- const chainLiquidity = response.poolChainLiquidities.nodes[0];
19131
- if (!chainLiquidity) return void 0;
19132
- const route = response.poolRoutes.nodes[0];
18832
+ }
18833
+ /** Total liquidity on a route, no amount required. */
18834
+ async routeLiquidity(route) {
18835
+ const { books, routeLiquidity: raw } = await this.request(ROUTE_LIQUIDITY_QUERY, { route });
18836
+ const book = findBook(books, route.tokenIn, route.tokenOut);
19133
18837
  return {
19134
- sourceChain: params.source.chain,
19135
- destinationChain: params.destination.chain,
19136
- tokenAddress: normalizeEvmAddress(params.destination.address, "destination token"),
19137
- updatedAt: readIndexerDate(chainLiquidity.lastUpdatedAt, "destination lastUpdatedAt"),
19138
- destination: readLiquiditySlice(chainLiquidity.depth, chainLiquidity.bidCount, "destination"),
19139
- unrestricted: readLiquiditySlice(
19140
- chainLiquidity.unrestrictedDepth,
19141
- chainLiquidity.unrestrictedBidCount,
19142
- "unrestricted"
19143
- ),
19144
- explicitRoute: route ? {
19145
- ...readLiquiditySlice(route.depth, route.bidCount, "explicit route"),
19146
- updatedAt: readIndexerDate(route.lastUpdatedAt, "route lastUpdatedAt")
19147
- } : null
18838
+ route: raw.route,
18839
+ book,
18840
+ side: sameSymbol(book.base, route.tokenIn) ? "BID" : "ASK",
18841
+ bestRate: optionalBigInt(raw.bestRate),
18842
+ slippageBps: raw.slippageBps,
18843
+ depthIn: BigInt(raw.depthIn),
18844
+ depthOut: BigInt(raw.depthOut),
18845
+ availableLiquidity: BigInt(raw.availableLiquidity),
18846
+ maxFillableIn: BigInt(raw.maxFillableIn),
18847
+ orderCount: raw.orderCount,
18848
+ solverCount: raw.solverCount
19148
18849
  };
19149
18850
  }
19150
18851
  /**
19151
- * Returns the indexed pool's aggregate buy and sell rates in less-valued
19152
- * quote-token units per one base token.
19153
- *
19154
- * The indexer depth-weights fresh per-chain samples into the pool rates. The
19155
- * source and destination chains remain part of the result because they define
19156
- * the cross-chain route whose configured token symbols were resolved.
19157
- */
19158
- async getBuyAndSellRates(params) {
19159
- const pool = resolveLiquidityPool(params.tokenInSymbol, params.tokenOutSymbol);
19160
- const response = await this.queryClient.request(BUY_AND_SELL_RATES, {
19161
- poolId: pool.poolId
18852
+ * The optimistic quote for `amountIn` (1e18 tokenIn) on a route: the route's orders, best price
18853
+ * first, each taking what it can at its own price. The fills' `amountOut`s sum to the output.
18854
+ */
18855
+ async quote(route, amountIn) {
18856
+ const { quote: raw } = await this.request(QUOTE_QUERY, {
18857
+ route,
18858
+ amountIn: amountIn.toString()
19162
18859
  });
19163
- if (!response?.liquidityPools?.nodes) {
19164
- throw new InvalidLiquidityIndexerResponseError("liquidity pool connection is missing");
19165
- }
19166
- const indexedPool = response.liquidityPools.nodes[0];
19167
- if (!indexedPool) return void 0;
19168
- validateIndexedPool(indexedPool, pool);
19169
- const sell = readIndexedRate(indexedPool.sellRate, indexedPool.lastUpdatedAt, "pool sell rate");
19170
- const buy = readIndexedRate(indexedPool.buyRate, indexedPool.lastUpdatedAt, "pool buy rate");
19171
- const inputIsToken0 = params.tokenInSymbol.toLowerCase() === pool.token0Symbol.toLowerCase();
19172
- const direct = inputIsToken0 ? sell : buy;
19173
- const reverse = inputIsToken0 ? buy : sell;
19174
- if (!direct && !reverse) return void 0;
19175
- const quoteTokenSymbol = resolveQuoteTokenSymbol(
19176
- params.tokenInSymbol,
19177
- params.tokenOutSymbol,
19178
- direct?.scaledRate,
19179
- reverse?.scaledRate
18860
+ return {
18861
+ route: raw.route,
18862
+ side: raw.side,
18863
+ amountIn: BigInt(raw.amountIn),
18864
+ slippageBps: raw.slippageBps,
18865
+ fillable: raw.fillable,
18866
+ maxFillableIn: BigInt(raw.maxFillableIn),
18867
+ fills: raw.fills.map((fill) => ({
18868
+ advertisedSize: BigInt(fill.advertisedSize),
18869
+ orderRate: BigInt(fill.orderRate),
18870
+ amountIn: BigInt(fill.amountIn),
18871
+ amountOut: BigInt(fill.amountOut)
18872
+ }))
18873
+ };
18874
+ }
18875
+ /**
18876
+ * The pessimistic quote for `amountIn` (1e18 tokenIn) on a route: one price, from the first level
18877
+ * deep enough to fill the whole trade by itself, at which every order in it fills; the route's
18878
+ * worst price when no one level can but its levels together can.
18879
+ */
18880
+ async quotePessimistic(route, amountIn) {
18881
+ const { quotePessimistic: raw } = await this.request(
18882
+ QUOTE_PESSIMISTIC_QUERY,
18883
+ { route, amountIn: amountIn.toString() }
19180
18884
  );
19181
- const quoteIsTokenOut = quoteTokenSymbol === params.tokenOutSymbol;
19182
- const orientedBuy = quoteIsTokenOut ? direct : reverse;
19183
- const orientedSell = quoteIsTokenOut ? reverse : direct;
19184
18885
  return {
19185
- baseTokenSymbol: quoteIsTokenOut ? params.tokenInSymbol : params.tokenOutSymbol,
19186
- quoteTokenSymbol,
19187
- sourceChain: params.sourceChain,
19188
- destinationChain: params.destinationChain,
19189
- buyRate: orientedBuy ? viem.formatUnits(orientedBuy.scaledRate, INDEXER_FIXED_POINT_DECIMALS) : null,
19190
- sellRate: orientedSell ? viem.formatUnits(reciprocalRate(orientedSell.scaledRate, "sell rate"), INDEXER_FIXED_POINT_DECIMALS) : null,
19191
- buyRateUpdatedAt: orientedBuy?.updatedAt ?? null,
19192
- sellRateUpdatedAt: orientedSell?.updatedAt ?? null
18886
+ route: raw.route,
18887
+ side: raw.side,
18888
+ amountIn: BigInt(raw.amountIn),
18889
+ amountOut: BigInt(raw.amountOut),
18890
+ rate: optionalBigInt(raw.rate),
18891
+ priceBucket: optionalBigInt(raw.priceBucket),
18892
+ slippageBps: raw.slippageBps,
18893
+ fillable: raw.fillable,
18894
+ maxFillableIn: BigInt(raw.maxFillableIn)
19193
18895
  };
19194
18896
  }
19195
- };
19196
- var InvalidLiquidityIndexerResponseError = class extends Error {
19197
- constructor(reason) {
19198
- super(`Invalid liquidity indexer response: ${reason}`);
19199
- this.name = "InvalidLiquidityIndexerResponseError";
18897
+ async request(query, variables) {
18898
+ try {
18899
+ return await this.client.request(query, variables);
18900
+ } catch (error) {
18901
+ throw new OrderbookRequestError(describeError(error));
18902
+ }
19200
18903
  }
19201
18904
  };
18905
+ function describeError(error) {
18906
+ const graphqlErrors = error?.response?.errors;
18907
+ if (graphqlErrors?.length) return graphqlErrors.map((e) => e.message).join("; ");
18908
+ return error instanceof Error ? error.message : String(error);
18909
+ }
18910
+ function sameSymbol(a, b) {
18911
+ return a.toLowerCase() === b.toLowerCase();
18912
+ }
18913
+ function findBook(books, tokenA, tokenB) {
18914
+ const book = books.find(
18915
+ (b) => sameSymbol(b.base, tokenA) && sameSymbol(b.quote, tokenB) || sameSymbol(b.base, tokenB) && sameSymbol(b.quote, tokenA)
18916
+ );
18917
+ if (!book) throw new OrderbookRequestError(`no book trades ${tokenA} for ${tokenB}`);
18918
+ return book;
18919
+ }
18920
+ function optionalBigInt(value) {
18921
+ return value === null ? null : BigInt(value);
18922
+ }
18923
+ function parseRate(raw) {
18924
+ return {
18925
+ side: raw.side,
18926
+ fillChain: raw.fillChain,
18927
+ rate: BigInt(raw.rate),
18928
+ depthIn: BigInt(raw.depthIn),
18929
+ depthOut: BigInt(raw.depthOut),
18930
+ backingLiquidity: BigInt(raw.backingLiquidity),
18931
+ sourceChains: raw.sourceChains,
18932
+ orderCount: raw.orderCount,
18933
+ solverCount: raw.solverCount
18934
+ };
18935
+ }
18936
+
18937
+ // src/protocols/intents/orderbook/types.ts
19202
18938
  var UnsupportedLiquidityAssetError = class extends Error {
19203
18939
  constructor(chain, asset) {
19204
- super(`No configured liquidity asset found for ${asset} on ${chain}`);
18940
+ super(`No configured asset found for ${asset} on ${chain}`);
19205
18941
  this.name = "UnsupportedLiquidityAssetError";
19206
18942
  }
19207
18943
  };
19208
18944
  var UnsupportedLiquidityChainError = class extends Error {
19209
18945
  constructor(chainId) {
19210
- super(`No configured liquidity chain found for chain ID ${chainId}`);
18946
+ super(`No configured chain found for chain ID ${chainId}`);
19211
18947
  this.name = "UnsupportedLiquidityChainError";
19212
18948
  }
19213
18949
  };
19214
- function readLiquiditySlice(depth, providerCount, label) {
19215
- if (!Number.isSafeInteger(providerCount) || providerCount < 0) {
19216
- throw new InvalidLiquidityIndexerResponseError(`${label} provider count is invalid`);
19217
- }
19218
- return { totalLiquidity: formatIndexerAmount(depth, `${label} depth`), providerCount };
19219
- }
19220
- function formatIndexerAmount(value, label) {
19221
- try {
19222
- const amount = BigInt(value);
19223
- if (amount < 0n) throw new Error();
19224
- return viem.formatUnits(amount, INDEXER_FIXED_POINT_DECIMALS);
19225
- } catch {
19226
- throw new InvalidLiquidityIndexerResponseError(`${label} is not a non-negative integer`);
18950
+ var OrderbookQuoteNotConvergedError = class extends Error {
18951
+ constructor(route, rounds, lastAmountIn) {
18952
+ super(
18953
+ `No input for ${route.tokenIn} -> ${route.tokenOut} on ${route.sourceChain} -> ${route.destinationChain} delivered the requested output within ${rounds} quotes; the last tried ${lastAmountIn} raw units`
18954
+ );
18955
+ this.route = route;
18956
+ this.rounds = rounds;
18957
+ this.lastAmountIn = lastAmountIn;
18958
+ this.name = "OrderbookQuoteNotConvergedError";
18959
+ }
18960
+ route;
18961
+ rounds;
18962
+ lastAmountIn;
18963
+ };
18964
+ var InsufficientOrderbookLiquidityError = class extends Error {
18965
+ constructor(route, maxFillableIn) {
18966
+ super(
18967
+ `The HyperFX orderbook cannot fill ${route.tokenIn} -> ${route.tokenOut} on ${route.sourceChain} -> ${route.destinationChain}; max fillable input is ${maxFillableIn} raw units`
18968
+ );
18969
+ this.route = route;
18970
+ this.maxFillableIn = maxFillableIn;
18971
+ this.name = "InsufficientOrderbookLiquidityError";
19227
18972
  }
19228
- }
19229
- function readIndexerDate(value, label) {
19230
- const date = new Date(dateStringtoTimestamp(value));
19231
- if (Number.isNaN(date.getTime())) throw new InvalidLiquidityIndexerResponseError(`${label} is invalid`);
19232
- return date;
19233
- }
19234
- function readIndexedRate(value, lastUpdatedAt, label) {
19235
- if (value === null) return void 0;
19236
- try {
19237
- const scaledRate = BigInt(value);
19238
- if (scaledRate <= 0n) throw new Error();
18973
+ route;
18974
+ maxFillableIn;
18975
+ };
18976
+
18977
+ // src/protocols/intents/orderbook/market.ts
18978
+ var MAX_EXACT_OUTPUT_ROUNDS = 16;
18979
+ var OrderbookMarket = class {
18980
+ constructor(configService, orderbook) {
18981
+ this.configService = configService;
18982
+ this.orderbook = orderbook;
18983
+ }
18984
+ configService;
18985
+ orderbook;
18986
+ async quoteIntent(params, sourceChain, destinationChain) {
18987
+ const orderbook = this.orderbook();
18988
+ if (!params.optimistic) {
18989
+ const { quote: quote2, tokenIn: tokenIn2, tokenOut: tokenOut2 } = await this.priceIntent(
18990
+ params,
18991
+ sourceChain,
18992
+ destinationChain,
18993
+ (route, amountIn) => orderbook.quotePessimistic(route, amountIn),
18994
+ levelQuoteTotals
18995
+ );
18996
+ return {
18997
+ route: quote2.route,
18998
+ side: quote2.side,
18999
+ amountIn: fromOrderbookAmount(quote2.amountIn, tokenIn2.decimals),
19000
+ amountOut: fromOrderbookAmount(quote2.amountOut, tokenOut2.decimals),
19001
+ rate: quote2.rate,
19002
+ priceBucket: quote2.priceBucket,
19003
+ slippageBps: quote2.slippageBps,
19004
+ fillable: quote2.fillable,
19005
+ maxFillableIn: fromOrderbookAmount(quote2.maxFillableIn, tokenIn2.decimals)
19006
+ };
19007
+ }
19008
+ const { quote, tokenIn, tokenOut } = await this.priceIntent(
19009
+ params,
19010
+ sourceChain,
19011
+ destinationChain,
19012
+ (route, amountIn) => orderbook.quote(route, amountIn),
19013
+ swapQuoteTotals
19014
+ );
19239
19015
  return {
19240
- scaledRate,
19241
- updatedAt: readIndexerDate(lastUpdatedAt, `${label} lastUpdatedAt`)
19016
+ route: quote.route,
19017
+ side: quote.side,
19018
+ amountIn: fromOrderbookAmount(quote.amountIn, tokenIn.decimals),
19019
+ slippageBps: quote.slippageBps,
19020
+ fillable: quote.fillable,
19021
+ maxFillableIn: fromOrderbookAmount(quote.maxFillableIn, tokenIn.decimals),
19022
+ legs: quote.fills.map((fill) => ({
19023
+ advertisedSize: fromOrderbookAmount(fill.advertisedSize, tokenOut.decimals),
19024
+ orderRate: fill.orderRate,
19025
+ amountIn: fromOrderbookAmount(fill.amountIn, tokenIn.decimals),
19026
+ amountOut: fromOrderbookAmount(fill.amountOut, tokenOut.decimals)
19027
+ }))
19242
19028
  };
19243
- } catch (error) {
19244
- if (error instanceof InvalidLiquidityIndexerResponseError) throw error;
19245
- throw new InvalidLiquidityIndexerResponseError(`${label} is not a positive integer`);
19246
19029
  }
19247
- }
19248
- function validateIndexedPool(indexedPool, expected) {
19249
- if (indexedPool.id.toLowerCase() !== expected.poolId.toLowerCase() || indexedPool.token0Symbol.toLowerCase() !== expected.token0Symbol.toLowerCase() || indexedPool.token1Symbol.toLowerCase() !== expected.token1Symbol.toLowerCase()) {
19250
- throw new InvalidLiquidityIndexerResponseError(`pool identity does not match ${expected.poolId}`);
19030
+ /**
19031
+ * The fillable quote for an exact input, or for the input found to deliver an
19032
+ * exact output, with the assets it trades.
19033
+ */
19034
+ async priceIntent(params, sourceChain, destinationChain, price, totals) {
19035
+ validateQuoteParams(params);
19036
+ const tokenIn = this.assetByAddress(sourceChain, params.tokenIn);
19037
+ const tokenOut = this.assetByAddress(destinationChain, params.tokenOut);
19038
+ const route = toRoute(tokenIn, tokenOut);
19039
+ if (params.amountIn !== void 0) {
19040
+ const quote2 = await price(route, toOrderbookAmount(params.amountIn, tokenIn.decimals));
19041
+ const { fillable, amountOut, maxFillableIn } = totals(quote2);
19042
+ if (!fillable || fromOrderbookAmount(amountOut, tokenOut.decimals) === 0n) {
19043
+ throw this.insufficient(route, maxFillableIn, tokenIn);
19044
+ }
19045
+ return { quote: quote2, tokenIn, tokenOut };
19046
+ }
19047
+ const targetOut = toOrderbookAmount(params.amountOut, tokenOut.decimals);
19048
+ const quote = await this.quoteExactOutput(route, targetOut, tokenIn, price, totals);
19049
+ return { quote, tokenIn, tokenOut };
19050
+ }
19051
+ async availableLiquidity(params, sourceChain, destinationChain) {
19052
+ const tokenIn = this.assetByAddress(sourceChain, params.tokenIn);
19053
+ const tokenOut = this.assetByAddress(destinationChain, params.tokenOut);
19054
+ const liquidity = await this.orderbook().routeLiquidity(toRoute(tokenIn, tokenOut));
19055
+ const [base3, quote] = liquidity.side === "BID" ? [tokenIn, tokenOut] : [tokenOut, tokenIn];
19056
+ return {
19057
+ sourceChain: tokenIn.chain,
19058
+ destinationChain: tokenOut.chain,
19059
+ tokenInSymbol: tokenIn.symbol,
19060
+ tokenOutSymbol: tokenOut.symbol,
19061
+ tokenAddress: tokenOut.address,
19062
+ route: liquidity.route,
19063
+ side: liquidity.side,
19064
+ baseTokenSymbol: base3.symbol,
19065
+ quoteTokenSymbol: quote.symbol,
19066
+ bestRate: formatOptional(liquidity.bestRate),
19067
+ availableLiquidity: format(liquidity.availableLiquidity),
19068
+ depthIn: format(liquidity.depthIn),
19069
+ depthOut: format(liquidity.depthOut),
19070
+ maxFillableIn: format(liquidity.maxFillableIn),
19071
+ orderCount: liquidity.orderCount,
19072
+ solverCount: liquidity.solverCount
19073
+ };
19251
19074
  }
19252
- }
19253
- function resolveQuoteTokenSymbol(tokenInSymbol, tokenOutSymbol, directRate, reverseRate) {
19254
- const inputIsUsdStable = USD_STABLE_SYMBOLS.has(tokenInSymbol);
19255
- const outputIsUsdStable = USD_STABLE_SYMBOLS.has(tokenOutSymbol);
19256
- if (inputIsUsdStable !== outputIsUsdStable) return inputIsUsdStable ? tokenOutSymbol : tokenInSymbol;
19257
- if (directRate !== void 0) return directRate >= POOL_RATE_SCALE ? tokenOutSymbol : tokenInSymbol;
19258
- if (reverseRate !== void 0) return reverseRate >= POOL_RATE_SCALE ? tokenInSymbol : tokenOutSymbol;
19259
- throw new InvalidLiquidityIndexerResponseError("cannot orient an empty rate pair");
19260
- }
19261
- function reciprocalRate(rate, label) {
19262
- const numerator = POOL_RATE_SCALE * POOL_RATE_SCALE;
19263
- const reciprocal = (numerator + rate - 1n) / rate;
19264
- if (reciprocal <= 0n) throw new InvalidLiquidityIndexerResponseError(`${label} reciprocal underflowed`);
19265
- return reciprocal;
19266
- }
19267
-
19268
- // src/protocols/intents/quote/types.ts
19269
- var UnsupportedIntentQuoteStrategyError = class extends Error {
19270
- constructor(strategy) {
19271
- super(`Unsupported intent quote strategy: ${strategy}`);
19272
- this.name = "UnsupportedIntentQuoteStrategyError";
19075
+ async buyAndSellRates(params) {
19076
+ const tokenIn = this.assetBySymbol(params.sourceChain, params.tokenInSymbol);
19077
+ const tokenOut = this.assetBySymbol(params.destinationChain, params.tokenOutSymbol);
19078
+ const { book, bid, ask } = await this.orderbook().topOfBook({
19079
+ tokenA: tokenIn.symbol,
19080
+ tokenB: tokenOut.symbol,
19081
+ fillChain: tokenOut.chain,
19082
+ sourceChain: tokenIn.chain
19083
+ });
19084
+ const bidRate = bid?.rate ?? null;
19085
+ const askRate = ask?.rate ?? null;
19086
+ const mid = bidRate !== null && askRate !== null ? (bidRate + askRate) / 2n : null;
19087
+ const spread = bidRate !== null && askRate !== null && askRate >= bidRate ? askRate - bidRate : null;
19088
+ return {
19089
+ baseTokenSymbol: book.base,
19090
+ quoteTokenSymbol: book.quote,
19091
+ sourceChain: tokenIn.chain,
19092
+ destinationChain: tokenOut.chain,
19093
+ bid: formatOptional(bidRate),
19094
+ ask: formatOptional(askRate),
19095
+ mid: formatOptional(mid),
19096
+ spread: formatOptional(spread),
19097
+ spreadBps: spread !== null && mid ? Number(spread * 1000000n / mid) / 100 : null
19098
+ };
19273
19099
  }
19274
- };
19275
- var UnsupportedIntentQuotePairError = class extends Error {
19276
- constructor(params) {
19277
- super(
19278
- `No ${params.quoteSource ?? "Uniswap v4 pool config"} found for ${params.tokenIn} -> ${params.tokenOut} on ${params.source} -> ${params.destination}`
19100
+ /**
19101
+ * The orderbook only quotes an input amount, so an output is priced by
19102
+ * guessing the input from the best rate, then keeping the part of each quote
19103
+ * filled better than its worst rate and re-pricing the rest of `targetOut` at
19104
+ * the input-to-output ratio that part filled at, until a quote delivers it.
19105
+ * That ratio is the orderbook's own, with the protocol fee already taken off.
19106
+ */
19107
+ async quoteExactOutput(route, targetOut, tokenIn, price, totals) {
19108
+ const liquidity = await this.orderbook().routeLiquidity(route);
19109
+ if (liquidity.bestRate === null) throw this.insufficient(route, 0n, tokenIn);
19110
+ const inputUnit = toOrderbookAmount(1n, tokenIn.decimals);
19111
+ let amountIn = roundUpTo(requiredInput(liquidity.side, targetOut, liquidity.bestRate), inputUnit);
19112
+ let lastAmountIn = amountIn;
19113
+ for (let round = 0; round < MAX_EXACT_OUTPUT_ROUNDS; round++) {
19114
+ if (amountIn > liquidity.maxFillableIn) throw this.insufficient(route, liquidity.maxFillableIn, tokenIn);
19115
+ const served = await price(route, amountIn);
19116
+ const quote = totals(served);
19117
+ if (quote.fillable && quote.amountOut >= targetOut) return served;
19118
+ lastAmountIn = amountIn;
19119
+ const betterIn = quote.amountIn - quote.worstIn;
19120
+ const betterOut = quote.amountOut - quote.worstOut;
19121
+ const next = quote.fillable && quote.worstOut > 0n ? betterIn + roundUpTo(divCeil((targetOut - betterOut) * quote.worstIn, quote.worstOut), inputUnit) : amountIn;
19122
+ amountIn = next > amountIn ? next : amountIn + inputUnit;
19123
+ }
19124
+ throw new OrderbookQuoteNotConvergedError(
19125
+ route,
19126
+ MAX_EXACT_OUTPUT_ROUNDS,
19127
+ fromOrderbookAmount(lastAmountIn, tokenIn.decimals)
19279
19128
  );
19280
- this.name = "UnsupportedIntentQuotePairError";
19281
19129
  }
19282
- };
19283
- var PhantomSnapshotUnavailableError = class extends Error {
19284
- constructor(tokenA, tokenB) {
19285
- super(`No Phantom order price snapshot found for ${tokenA} -> ${tokenB}`);
19286
- this.name = "PhantomSnapshotUnavailableError";
19130
+ insufficient(route, maxFillableIn, tokenIn) {
19131
+ return new InsufficientOrderbookLiquidityError(route, fromOrderbookAmount(maxFillableIn, tokenIn.decimals));
19287
19132
  }
19288
- };
19289
- var InvalidPhantomSnapshotError = class extends Error {
19290
- constructor(commitment, reason) {
19291
- super(`Invalid Phantom order price snapshot ${commitment}: ${reason}`);
19292
- this.name = "InvalidPhantomSnapshotError";
19133
+ assetByAddress(chain, address) {
19134
+ const resolvedChain = resolveChain(chain);
19135
+ return withDecimals(
19136
+ resolvedChain,
19137
+ address,
19138
+ this.configService.getAssetMetadataByAddress(resolvedChain, address)
19139
+ );
19293
19140
  }
19294
- };
19295
- var IndexedRateUnavailableError = class extends Error {
19296
- constructor(params) {
19297
- const route = params.source && params.destination && params.tokenIn && params.tokenOut ? ` for ${params.tokenIn} -> ${params.tokenOut} on ${params.source} -> ${params.destination}` : "";
19298
- const side = params.side ? ` ${params.side}` : "";
19299
- super(`No indexed${side} rate available${route}`);
19300
- this.name = "IndexedRateUnavailableError";
19141
+ assetBySymbol(chain, symbol) {
19142
+ const resolvedChain = resolveChain(chain);
19143
+ return withDecimals(resolvedChain, symbol, this.configService.getAssetMetadataBySymbol(resolvedChain, symbol));
19301
19144
  }
19302
19145
  };
19303
- var InvalidIndexedRateError = class extends Error {
19304
- constructor(reason) {
19305
- super(`Invalid indexed intent rate: ${reason}`);
19306
- this.name = "InvalidIndexedRateError";
19146
+ function resolveChain(chain) {
19147
+ const stateMachineId = getConfigByStateMachineId(chain)?.stateMachineId;
19148
+ if (!stateMachineId) throw new UnsupportedLiquidityChainError(chain);
19149
+ return stateMachineId;
19150
+ }
19151
+ function withDecimals(chain, asset, metadata) {
19152
+ if (!metadata) throw new UnsupportedLiquidityAssetError(chain, asset);
19153
+ const { decimals } = metadata;
19154
+ if (decimals === void 0 || !Number.isSafeInteger(decimals) || decimals < 0 || decimals > ORDERBOOK_DECIMALS) {
19155
+ throw new UnsupportedLiquidityAssetError(chain, `${metadata.symbol} (decimals are not configured)`);
19307
19156
  }
19308
- };
19309
- var BPS_DENOMINATOR = 10000n;
19157
+ return { chain, symbol: metadata.symbol, address: metadata.address, decimals };
19158
+ }
19310
19159
  function validateQuoteParams(params) {
19311
19160
  const hasAmountIn = params.amountIn !== void 0;
19312
19161
  const hasAmountOut = params.amountOut !== void 0;
19313
19162
  if (hasAmountIn === hasAmountOut) throw new Error("Provide exactly one of amountIn or amountOut");
19314
19163
  if (params.amountIn !== void 0 && params.amountIn <= 0n) throw new Error("amountIn must be greater than zero");
19315
19164
  if (params.amountOut !== void 0 && params.amountOut <= 0n) throw new Error("amountOut must be greater than zero");
19316
- if (params.tokenIn.toLowerCase() === params.tokenOut.toLowerCase()) {
19317
- throw new Error("tokenIn and tokenOut cannot be the same");
19318
- }
19319
19165
  }
19320
- async function readProtocolFeeBps(chainConfigService, source) {
19321
- const gatewayAddress = chainConfigService.getIntentGatewayAddress(source.stateMachineId);
19322
- if (!gatewayAddress || gatewayAddress === "0x" || gatewayAddress === viem.zeroAddress) {
19323
- throw new Error(`IntentGatewayV2 is not configured for chain ${source.stateMachineId}`);
19324
- }
19325
- const gatewayParams = await source.client.readContract({
19326
- address: gatewayAddress,
19327
- abi: IntentGatewayV2_default.ABI,
19328
- functionName: "params"
19329
- });
19330
- const protocolFeeBps = Array.isArray(gatewayParams) ? BigInt(gatewayParams[4]) : BigInt(gatewayParams.protocolFeeBps ?? 0);
19331
- if (protocolFeeBps < 0n || protocolFeeBps >= BPS_DENOMINATOR) {
19332
- throw new Error(`Invalid IntentGateway protocol fee: ${protocolFeeBps} bps`);
19333
- }
19334
- return protocolFeeBps;
19335
- }
19336
- function deductProtocolFee(amount, protocolFeeBps) {
19337
- if (protocolFeeBps <= 0n) return amount;
19338
- const fee = amount * protocolFeeBps / BPS_DENOMINATOR;
19339
- return amount - fee;
19166
+ function toRoute(tokenIn, tokenOut) {
19167
+ return {
19168
+ tokenIn: tokenIn.symbol,
19169
+ tokenOut: tokenOut.symbol,
19170
+ sourceChain: tokenIn.chain,
19171
+ destinationChain: tokenOut.chain
19172
+ };
19340
19173
  }
19341
- function grossUpForProtocolFee(netAmount, protocolFeeBps) {
19342
- if (protocolFeeBps <= 0n) return netAmount;
19343
- if (protocolFeeBps >= BPS_DENOMINATOR) throw new Error("protocolFeeBps must be less than 10,000");
19344
- return divCeil(netAmount * BPS_DENOMINATOR, BPS_DENOMINATOR - protocolFeeBps);
19174
+ function requiredInput(side, amountOut, rate) {
19175
+ return side === "BID" ? divCeil(amountOut * ORDERBOOK_SCALE, rate) : divCeil(amountOut * rate, ORDERBOOK_SCALE);
19345
19176
  }
19346
- function divCeil(numerator, denominator) {
19347
- if (denominator <= 0n) throw new Error("denominator must be greater than zero");
19348
- return (numerator + denominator - 1n) / denominator;
19177
+ function swapQuoteTotals(quote) {
19178
+ const worstFill = quote.fills.at(-1);
19179
+ if (quote.fillable && !worstFill)
19180
+ throw new OrderbookRequestError("the orderbook served a fillable quote with no fills");
19181
+ return {
19182
+ side: quote.side,
19183
+ amountIn: quote.amountIn,
19184
+ amountOut: quote.fills.reduce((total, fill) => total + fill.amountOut, 0n),
19185
+ fillable: quote.fillable,
19186
+ maxFillableIn: quote.maxFillableIn,
19187
+ worstIn: worstFill?.amountIn ?? 0n,
19188
+ worstOut: worstFill?.amountOut ?? 0n
19189
+ };
19349
19190
  }
19350
-
19351
- // src/protocols/intents/quote/uniswapV4.ts
19352
- var UNISWAP_INTENT_QUOTE_CHAIN = "EVM-8453" /* BASE_MAINNET */;
19353
- var UniswapV4IntentQuoteStrategy = class {
19354
- constructor(chainConfigService) {
19355
- this.chainConfigService = chainConfigService;
19356
- }
19357
- chainConfigService;
19358
- baseQuoteClient;
19359
- async quote(params, source, destination) {
19360
- validateQuoteParams(params);
19361
- const protocolFeeBps = await readProtocolFeeBps(this.chainConfigService, source);
19362
- const quoteClient = this.resolveQuoteClient(source, destination);
19363
- const poolConfig = this.resolvePoolConfig(params, source.stateMachineId, destination.stateMachineId);
19364
- return params.amountIn !== void 0 ? this.quoteExactInput({ params, client: quoteClient, protocolFeeBps, poolConfig }) : this.quoteExactOutput({ params, client: quoteClient, protocolFeeBps, poolConfig });
19365
- }
19366
- resolveQuoteClient(source, destination) {
19367
- if (source.stateMachineId === UNISWAP_INTENT_QUOTE_CHAIN) return source.client;
19368
- if (destination.stateMachineId === UNISWAP_INTENT_QUOTE_CHAIN) return destination.client;
19369
- if (this.baseQuoteClient) return this.baseQuoteClient;
19370
- const rpcUrl = this.chainConfigService.getRpcUrl(UNISWAP_INTENT_QUOTE_CHAIN);
19371
- if (!rpcUrl) throw new Error(`RPC URL is not configured for ${UNISWAP_INTENT_QUOTE_CHAIN}`);
19372
- const baseQuoteClient = viem.createPublicClient({
19373
- chain: chains$1.base,
19374
- transport: viem.http(rpcUrl)
19375
- });
19376
- this.baseQuoteClient = baseQuoteClient;
19377
- return baseQuoteClient;
19378
- }
19379
- resolvePoolConfig(params, source, destination) {
19380
- const override = params.uniswapV4?.poolKey;
19381
- if (override) {
19382
- const poolKey = normalizePoolKey(override);
19383
- const tokenInForQuote = viem.getAddress(override.currencyIn ?? params.tokenIn);
19384
- if (tokenInForQuote !== poolKey.currency0 && tokenInForQuote !== poolKey.currency1) {
19385
- throw new Error(
19386
- `Input currency ${tokenInForQuote} is not part of the override pool (${poolKey.currency0}, ${poolKey.currency1}). For cross-chain quotes pass uniswapV4.poolKey.currencyIn with the Base-side input currency address.`
19387
- );
19388
- }
19389
- return {
19390
- poolKey,
19391
- quoterAddress: this.resolveQuoterAddress(UNISWAP_INTENT_QUOTE_CHAIN, override.quoterAddress),
19392
- tokenInForQuote
19393
- };
19394
- }
19395
- const poolConfig = this.resolveConfiguredPool(params, source, destination, UNISWAP_INTENT_QUOTE_CHAIN);
19396
- if (poolConfig) return poolConfig;
19397
- throw new UnsupportedIntentQuotePairError({
19398
- source,
19399
- destination,
19400
- tokenIn: params.tokenIn,
19401
- tokenOut: params.tokenOut
19402
- });
19403
- }
19404
- resolveConfiguredPool(params, sourceChain, destinationChain, quoteChain) {
19405
- for (const pool of this.chainConfigService.getUniswapV4PoolConfigs(quoteChain)) {
19406
- const resolvedPool = this.resolveConfiguredPoolTokens(quoteChain, pool);
19407
- if (!resolvedPool) continue;
19408
- const tokenInForQuote = this.matchPoolToken(params.tokenIn, sourceChain, resolvedPool);
19409
- const tokenOutForQuote = this.matchPoolToken(params.tokenOut, destinationChain, resolvedPool);
19410
- if (!tokenInForQuote || !tokenOutForQuote || tokenInForQuote.symbol === tokenOutForQuote.symbol) continue;
19411
- const { currency0, currency1 } = sortCurrencies(resolvedPool[0].address, resolvedPool[1].address);
19412
- return {
19413
- poolKey: {
19414
- currency0,
19415
- currency1,
19416
- fee: pool.fee,
19417
- tickSpacing: pool.tickSpacing,
19418
- hooks: viem.getAddress(pool.hooks ?? viem.zeroAddress)
19419
- },
19420
- quoterAddress: this.resolveQuoterAddress(quoteChain),
19421
- tokenInForQuote: tokenInForQuote.address
19422
- };
19423
- }
19424
- return null;
19191
+ function levelQuoteTotals(quote) {
19192
+ if (quote.fillable && quote.rate === null) {
19193
+ throw new OrderbookRequestError("the orderbook served a fillable quote with no rate");
19425
19194
  }
19426
- resolveQuoterAddress(chain, override) {
19427
- const address = override ?? this.chainConfigService.getUniswapV4QuoterAddress(chain);
19428
- if (!address || address === "0x" || address === viem.zeroAddress) {
19429
- throw new Error(`Uniswap V4 quoter is not configured for chain ${chain}`);
19430
- }
19431
- return viem.getAddress(address);
19432
- }
19433
- resolveConfiguredPoolTokens(chain, pool) {
19434
- const first = this.resolveConfiguredPoolToken(chain, pool.tokens[0]);
19435
- const second = this.resolveConfiguredPoolToken(chain, pool.tokens[1]);
19436
- return first && second ? [first, second] : null;
19437
- }
19438
- resolveConfiguredPoolToken(chain, symbol) {
19439
- const address = this.chainConfigService.getAssetAddress(chain, symbol);
19440
- if (!address || address === "0x") return null;
19441
- return { symbol, address: viem.getAddress(address) };
19442
- }
19443
- matchPoolToken(tokenAddress, orderChain, poolTokens) {
19444
- const addressMatch = poolTokens.find(
19445
- (poolToken) => poolToken.address.toLowerCase() === tokenAddress.toLowerCase()
19446
- );
19447
- if (addressMatch) return addressMatch;
19448
- const metadata = this.chainConfigService.getAssetMetadataByAddress(orderChain, tokenAddress);
19449
- if (!metadata) return null;
19450
- return poolTokens.find((poolToken) => poolToken.symbol === metadata.symbol) ?? null;
19451
- }
19452
- async quoteExactInput(args) {
19453
- const amountIn = args.params.amountIn;
19454
- if (amountIn === void 0) throw new Error("amountIn is required for an exact-input quote");
19455
- const swapAmountIn = deductProtocolFee(amountIn, args.protocolFeeBps);
19456
- const amountOut = await this.readV4QuoteExactInput(args.client, args.poolConfig, swapAmountIn);
19457
- return {
19458
- strategy: "uniswap_v4",
19459
- tradeType: "EXACT_INPUT",
19460
- amountIn,
19461
- amountOut,
19462
- quoteMetadata: {
19463
- quoteChain: UNISWAP_INTENT_QUOTE_CHAIN,
19464
- poolKey: args.poolConfig.poolKey,
19465
- quoterAddress: args.poolConfig.quoterAddress,
19466
- protocolFeeBps: args.protocolFeeBps
19467
- }
19468
- };
19469
- }
19470
- async quoteExactOutput(args) {
19471
- const amountOut = args.params.amountOut;
19472
- if (amountOut === void 0) throw new Error("amountOut is required for an exact-output quote");
19473
- const swapAmountIn = await this.readV4QuoteExactOutput(args.client, args.poolConfig, amountOut);
19474
- const amountIn = grossUpForProtocolFee(swapAmountIn, args.protocolFeeBps);
19475
- return {
19476
- strategy: "uniswap_v4",
19477
- tradeType: "EXACT_OUTPUT",
19478
- amountIn,
19479
- amountOut,
19480
- quoteMetadata: {
19481
- quoteChain: UNISWAP_INTENT_QUOTE_CHAIN,
19482
- poolKey: args.poolConfig.poolKey,
19483
- quoterAddress: args.poolConfig.quoterAddress,
19484
- protocolFeeBps: args.protocolFeeBps
19485
- }
19486
- };
19487
- }
19488
- async readV4QuoteExactInput(client, poolConfig, amountIn) {
19489
- const data = viem.encodeFunctionData({
19490
- abi: UNISWAP_V4_QUOTER_ABI,
19491
- functionName: "quoteExactInputSingle",
19492
- args: [
19493
- {
19494
- poolKey: poolConfig.poolKey,
19495
- zeroForOne: getZeroForOne(poolConfig.tokenInForQuote, poolConfig.poolKey),
19496
- exactAmount: amountIn,
19497
- hookData: "0x"
19498
- }
19499
- ]
19500
- });
19501
- const response = await client.call({ to: poolConfig.quoterAddress, data });
19502
- if (!response.data || response.data === "0x") {
19503
- throw new Error(`Uniswap V4 quoter at ${poolConfig.quoterAddress} returned no data`);
19504
- }
19505
- const [amountOut] = viem.decodeFunctionResult({
19506
- abi: UNISWAP_V4_QUOTER_ABI,
19507
- functionName: "quoteExactInputSingle",
19508
- data: response.data
19509
- });
19510
- return amountOut;
19511
- }
19512
- async readV4QuoteExactOutput(client, poolConfig, amountOut) {
19513
- const data = viem.encodeFunctionData({
19514
- abi: UNISWAP_V4_QUOTER_ABI,
19515
- functionName: "quoteExactOutputSingle",
19516
- args: [
19517
- {
19518
- poolKey: poolConfig.poolKey,
19519
- zeroForOne: getZeroForOne(poolConfig.tokenInForQuote, poolConfig.poolKey),
19520
- exactAmount: amountOut,
19521
- hookData: "0x"
19522
- }
19523
- ]
19524
- });
19525
- const response = await client.call({ to: poolConfig.quoterAddress, data });
19526
- if (!response.data || response.data === "0x") {
19527
- throw new Error(`Uniswap V4 quoter at ${poolConfig.quoterAddress} returned no data`);
19528
- }
19529
- const [amountIn] = viem.decodeFunctionResult({
19530
- abi: UNISWAP_V4_QUOTER_ABI,
19531
- functionName: "quoteExactOutputSingle",
19532
- data: response.data
19533
- });
19534
- return amountIn;
19535
- }
19536
- };
19537
- function normalizePoolKey(poolKey) {
19538
19195
  return {
19539
- currency0: viem.getAddress(poolKey.currency0),
19540
- currency1: viem.getAddress(poolKey.currency1),
19541
- fee: poolKey.fee,
19542
- tickSpacing: poolKey.tickSpacing,
19543
- hooks: viem.getAddress(poolKey.hooks)
19196
+ side: quote.side,
19197
+ amountIn: quote.amountIn,
19198
+ amountOut: quote.amountOut,
19199
+ fillable: quote.fillable,
19200
+ maxFillableIn: quote.maxFillableIn,
19201
+ worstIn: quote.amountIn,
19202
+ worstOut: quote.amountOut
19544
19203
  };
19545
19204
  }
19546
- function sortCurrencies(tokenIn, tokenOut) {
19547
- const input = viem.getAddress(tokenIn);
19548
- const output = viem.getAddress(tokenOut);
19549
- return BigInt(input) < BigInt(output) ? { currency0: input, currency1: output } : { currency0: output, currency1: input };
19550
- }
19551
- function getZeroForOne(tokenIn, poolKey) {
19552
- return viem.getAddress(tokenIn).toLowerCase() === viem.getAddress(poolKey.currency0).toLowerCase();
19553
- }
19554
-
19555
- // src/protocols/intents/quote/phantomSnapshot.ts
19556
- var PHANTOM_INTENT_QUOTE_CHAIN = "EVM-8453" /* BASE_MAINNET */;
19557
- var BASE_SNAPSHOT_ASSET_BY_ORDER_ASSET = {
19558
- USDC: "USDC",
19559
- USDT: "USDC",
19560
- cNGN: "cNGN"
19561
- };
19562
- var SUPPORTED_BASE_SNAPSHOT_PAIRS = /* @__PURE__ */ new Set(["USDC:cNGN", "cNGN:USDC"]);
19563
- var PhantomSnapshotIntentQuoteStrategy = class {
19564
- constructor(chainConfigService, getQueryClient) {
19565
- this.chainConfigService = chainConfigService;
19566
- this.getQueryClient = getQueryClient;
19567
- this.pairResolver = new PhantomSnapshotPairResolver(chainConfigService);
19568
- }
19569
- chainConfigService;
19570
- getQueryClient;
19571
- pairResolver;
19572
- async quote(params, source, destination) {
19573
- validateQuoteParams(params);
19574
- const pair = this.pairResolver.resolve(params, source.stateMachineId, destination.stateMachineId);
19575
- if (!pair) {
19576
- throw new UnsupportedIntentQuotePairError({
19577
- source: source.stateMachineId,
19578
- destination: destination.stateMachineId,
19579
- tokenIn: params.tokenIn,
19580
- tokenOut: params.tokenOut,
19581
- quoteSource: "Phantom snapshot pair"
19582
- });
19583
- }
19584
- const queryClient = this.getQueryClient();
19585
- const [protocolFeeBps, snapshot] = await Promise.all([
19586
- readProtocolFeeBps(this.chainConfigService, source),
19587
- _queryLatestPhantomOrderPriceSnapshot({
19588
- tokenA: pair.tokenA,
19589
- tokenB: pair.tokenB,
19590
- queryClient
19591
- })
19592
- ]);
19593
- if (!snapshot) throw new PhantomSnapshotUnavailableError(pair.tokenA, pair.tokenB);
19594
- this.validateSnapshot(snapshot);
19595
- return this.quoteSnapshot(params, protocolFeeBps, snapshot);
19596
- }
19597
- quoteSnapshot(params, protocolFeeBps, snapshot) {
19598
- if (params.amountIn !== void 0) {
19599
- const netAmountIn2 = deductProtocolFee(params.amountIn, protocolFeeBps);
19600
- const amountOut = netAmountIn2 * snapshot.medianPrice / snapshot.standardAmount;
19601
- if (amountOut <= 0n) {
19602
- throw new InvalidPhantomSnapshotError(snapshot.commitment, "quote rounds down to zero output");
19603
- }
19604
- return this.buildResult("EXACT_INPUT", params.amountIn, amountOut, protocolFeeBps, snapshot);
19605
- }
19606
- if (params.amountOut === void 0) throw new Error("Quote amount is missing after validation");
19607
- const netAmountIn = divCeil(params.amountOut * snapshot.standardAmount, snapshot.medianPrice);
19608
- const amountIn = grossUpForProtocolFee(netAmountIn, protocolFeeBps);
19609
- return this.buildResult("EXACT_OUTPUT", amountIn, params.amountOut, protocolFeeBps, snapshot);
19610
- }
19611
- buildResult(tradeType, amountIn, amountOut, protocolFeeBps, snapshot) {
19612
- return {
19613
- strategy: "phantom_snapshot",
19614
- tradeType,
19615
- amountIn,
19616
- amountOut,
19617
- quoteMetadata: {
19618
- quoteChain: PHANTOM_INTENT_QUOTE_CHAIN,
19619
- commitment: snapshot.commitment,
19620
- tokenA: snapshot.tokenA,
19621
- tokenB: snapshot.tokenB,
19622
- standardAmount: snapshot.standardAmount,
19623
- medianPrice: snapshot.medianPrice,
19624
- lowestPrice: snapshot.lowestPrice,
19625
- highestPrice: snapshot.highestPrice,
19626
- blockNumber: snapshot.blockNumber,
19627
- snapshotTime: snapshot.snapshotTime,
19628
- bidCount: snapshot.bidCount,
19629
- protocolFeeBps
19630
- }
19631
- };
19632
- }
19633
- validateSnapshot(snapshot) {
19634
- if (snapshot.standardAmount <= 0n) {
19635
- throw new InvalidPhantomSnapshotError(snapshot.commitment, "standardAmount must be greater than zero");
19636
- }
19637
- if (snapshot.medianPrice <= 0n) {
19638
- throw new InvalidPhantomSnapshotError(snapshot.commitment, "medianPrice must be greater than zero");
19639
- }
19640
- if (snapshot.bidCount <= 0) {
19641
- throw new InvalidPhantomSnapshotError(snapshot.commitment, "bidCount must be greater than zero");
19642
- }
19643
- if (Number.isNaN(snapshot.snapshotTime.getTime())) {
19644
- throw new InvalidPhantomSnapshotError(snapshot.commitment, "snapshotTime is invalid");
19645
- }
19646
- }
19647
- };
19648
- var PhantomSnapshotPairResolver = class {
19649
- constructor(chainConfigService) {
19650
- this.chainConfigService = chainConfigService;
19651
- }
19652
- chainConfigService;
19653
- resolve(params, sourceStateMachineId, destinationStateMachineId) {
19654
- const tokenIn = this.resolveBaseSnapshotToken(sourceStateMachineId, params.tokenIn);
19655
- const tokenOut = this.resolveBaseSnapshotToken(destinationStateMachineId, params.tokenOut);
19656
- if (!tokenIn || !tokenOut || !isSupportedSnapshotPair(tokenIn.symbol, tokenOut.symbol)) return;
19657
- return { tokenA: tokenIn.address, tokenB: tokenOut.address };
19658
- }
19659
- resolveBaseSnapshotToken(chain, tokenAddress) {
19660
- const orderAsset = this.chainConfigService.getAssetMetadataByAddress(chain, tokenAddress)?.symbol;
19661
- if (!orderAsset) return;
19662
- const snapshotAsset = BASE_SNAPSHOT_ASSET_BY_ORDER_ASSET[orderAsset];
19663
- if (!snapshotAsset) return;
19664
- const address = this.chainConfigService.getAssetAddress(PHANTOM_INTENT_QUOTE_CHAIN, snapshotAsset);
19665
- if (!isConfiguredAddress(address)) return;
19666
- return { symbol: snapshotAsset, address };
19667
- }
19668
- };
19669
- function isSupportedSnapshotPair(tokenA, tokenB) {
19670
- return SUPPORTED_BASE_SNAPSHOT_PAIRS.has(`${tokenA}:${tokenB}`);
19205
+ function toOrderbookAmount(amount, decimals) {
19206
+ return amount * 10n ** BigInt(ORDERBOOK_DECIMALS - decimals);
19671
19207
  }
19672
- function isConfiguredAddress(address) {
19673
- return Boolean(address && address !== "0x" && !/^0x0{40}$/i.test(address));
19208
+ function fromOrderbookAmount(amount, decimals) {
19209
+ return amount / 10n ** BigInt(ORDERBOOK_DECIMALS - decimals);
19674
19210
  }
19675
- var INDEXED_RATE_DECIMALS = 18;
19676
- var INDEXED_RATE_SCALE = 10n ** BigInt(INDEXED_RATE_DECIMALS);
19677
- var IndexedRateIntentQuoteStrategy = class {
19678
- constructor(chainConfigService, getQueryClient) {
19679
- this.chainConfigService = chainConfigService;
19680
- this.getQueryClient = getQueryClient;
19681
- }
19682
- chainConfigService;
19683
- getQueryClient;
19684
- async quote(params, source, destination) {
19685
- validateQuoteParams(params);
19686
- const sourceConfig = getConfigByStateMachineId(source.stateMachineId);
19687
- const destinationConfig = getConfigByStateMachineId(destination.stateMachineId);
19688
- if (!sourceConfig) throw new UnsupportedLiquidityChainError(source.stateMachineId);
19689
- if (!destinationConfig) throw new UnsupportedLiquidityChainError(destination.stateMachineId);
19690
- const tokenIn = this.resolveAsset(sourceConfig.stateMachineId, params.tokenIn);
19691
- const tokenOut = this.resolveAsset(destinationConfig.stateMachineId, params.tokenOut);
19692
- const [protocolFeeBps, rates] = await Promise.all([
19693
- readProtocolFeeBps(this.chainConfigService, source),
19694
- new LiquidityEngine(this.getQueryClient()).getBuyAndSellRates({
19695
- sourceChain: sourceConfig.stateMachineId,
19696
- destinationChain: destinationConfig.stateMachineId,
19697
- tokenInSymbol: tokenIn.symbol,
19698
- tokenOutSymbol: tokenOut.symbol
19699
- })
19700
- ]);
19701
- if (!rates) {
19702
- throw new IndexedRateUnavailableError({
19703
- source: sourceConfig.stateMachineId,
19704
- destination: destinationConfig.stateMachineId,
19705
- tokenIn: tokenIn.symbol,
19706
- tokenOut: tokenOut.symbol
19707
- });
19708
- }
19709
- const selectedRate = selectIndexedRate(rates, tokenIn.symbol, tokenOut.symbol);
19710
- return quoteWithIndexedRate(params, tokenIn, tokenOut, selectedRate, rates, protocolFeeBps);
19711
- }
19712
- resolveAsset(chain, address) {
19713
- const asset = this.chainConfigService.getAssetMetadataByAddress(chain, address);
19714
- if (!asset) throw new UnsupportedLiquidityAssetError(chain, address);
19715
- const { decimals } = asset;
19716
- if (decimals === void 0 || !Number.isSafeInteger(decimals) || decimals < 0) {
19717
- throw new InvalidIndexedRateError(`decimals are not configured for ${asset.symbol} on ${chain}`);
19718
- }
19719
- return { ...asset, decimals };
19720
- }
19721
- };
19722
- function selectIndexedRate(rates, tokenInSymbol, tokenOutSymbol) {
19723
- if (tokenInSymbol === rates.baseTokenSymbol && tokenOutSymbol === rates.quoteTokenSymbol) {
19724
- return readIndexedRate2(
19725
- "buy",
19726
- rates.buyRate,
19727
- rates.buyRateUpdatedAt,
19728
- rates,
19729
- tokenInSymbol,
19730
- tokenOutSymbol
19731
- );
19732
- }
19733
- if (tokenInSymbol === rates.quoteTokenSymbol && tokenOutSymbol === rates.baseTokenSymbol) {
19734
- return readIndexedRate2(
19735
- "sell",
19736
- rates.sellRate,
19737
- rates.sellRateUpdatedAt,
19738
- rates,
19739
- tokenInSymbol,
19740
- tokenOutSymbol
19741
- );
19742
- }
19743
- throw new InvalidIndexedRateError(
19744
- `indexed pair ${rates.baseTokenSymbol}/${rates.quoteTokenSymbol} does not match ${tokenInSymbol}/${tokenOutSymbol}`
19745
- );
19211
+ function roundUpTo(amount, unit) {
19212
+ return divCeil(amount, unit) * unit;
19746
19213
  }
19747
- function readIndexedRate2(side, rate, updatedAt, rates, tokenInSymbol, tokenOutSymbol) {
19748
- if (!rate || !updatedAt) {
19749
- throw new IndexedRateUnavailableError({
19750
- source: rates.sourceChain,
19751
- destination: rates.destinationChain,
19752
- tokenIn: tokenInSymbol,
19753
- tokenOut: tokenOutSymbol,
19754
- side
19755
- });
19756
- }
19757
- try {
19758
- const scaledRate = viem.parseUnits(rate, INDEXED_RATE_DECIMALS);
19759
- if (scaledRate <= 0n || Number.isNaN(updatedAt.getTime())) throw new Error();
19760
- return { side, rate, scaledRate, updatedAt };
19761
- } catch {
19762
- throw new InvalidIndexedRateError(`${side} rate or timestamp is invalid`);
19763
- }
19214
+ function divCeil(numerator, denominator) {
19215
+ return (numerator + denominator - 1n) / denominator;
19764
19216
  }
19765
- function quoteWithIndexedRate(params, tokenIn, tokenOut, selectedRate, rates, protocolFeeBps) {
19766
- const inputUnit = 10n ** BigInt(tokenIn.decimals);
19767
- const outputUnit = 10n ** BigInt(tokenOut.decimals);
19768
- if (params.amountIn !== void 0) {
19769
- const netAmountIn2 = deductProtocolFee(params.amountIn, protocolFeeBps);
19770
- const amountOut = selectedRate.side === "buy" ? netAmountIn2 * selectedRate.scaledRate * outputUnit / (inputUnit * INDEXED_RATE_SCALE) : netAmountIn2 * outputUnit * INDEXED_RATE_SCALE / (inputUnit * selectedRate.scaledRate);
19771
- if (amountOut <= 0n) throw new InvalidIndexedRateError("quote rounds down to zero output");
19772
- return buildResult("EXACT_INPUT", params.amountIn, amountOut, selectedRate, rates, protocolFeeBps);
19773
- }
19774
- if (params.amountOut === void 0) throw new Error("Quote amount is missing after validation");
19775
- const netAmountIn = selectedRate.side === "buy" ? divCeil(params.amountOut * inputUnit * INDEXED_RATE_SCALE, selectedRate.scaledRate * outputUnit) : divCeil(params.amountOut * inputUnit * selectedRate.scaledRate, outputUnit * INDEXED_RATE_SCALE);
19776
- const amountIn = grossUpForProtocolFee(netAmountIn, protocolFeeBps);
19777
- return buildResult("EXACT_OUTPUT", amountIn, params.amountOut, selectedRate, rates, protocolFeeBps);
19217
+ function format(amount) {
19218
+ return viem.formatUnits(amount, ORDERBOOK_DECIMALS);
19778
19219
  }
19779
- function buildResult(tradeType, amountIn, amountOut, selectedRate, rates, protocolFeeBps) {
19780
- return {
19781
- strategy: "indexed_rates",
19782
- tradeType,
19783
- amountIn,
19784
- amountOut,
19785
- quoteMetadata: {
19786
- sourceChain: rates.sourceChain,
19787
- destinationChain: rates.destinationChain,
19788
- baseTokenSymbol: rates.baseTokenSymbol,
19789
- quoteTokenSymbol: rates.quoteTokenSymbol,
19790
- rateSide: selectedRate.side,
19791
- rate: selectedRate.rate,
19792
- rateUpdatedAt: selectedRate.updatedAt,
19793
- protocolFeeBps
19794
- }
19795
- };
19220
+ function formatOptional(amount) {
19221
+ return amount === null ? null : format(amount);
19796
19222
  }
19797
19223
 
19798
19224
  // src/protocols/intents/IntentGateway.ts
@@ -19833,8 +19259,10 @@ var IntentGateway = class _IntentGateway {
19833
19259
  bidManager;
19834
19260
  /** Estimates gas costs for filling an order and converts them to fee-token amounts. */
19835
19261
  gasEstimator;
19836
- /** Quote strategies for pricing orders before placement, keyed by strategy name. */
19837
- quoteStrategies;
19262
+ /** The HyperFX orderbook quotes, liquidity and rates are read from. */
19263
+ orderbook;
19264
+ /** Prices intents, liquidity and rates from {@link orderbook}. */
19265
+ market;
19838
19266
  /**
19839
19267
  * Private constructor — use {@link IntentGateway.create} instead.
19840
19268
  *
@@ -19878,17 +19306,8 @@ var IntentGateway = class _IntentGateway {
19878
19306
  this.bidManager = bidManager;
19879
19307
  this.gasEstimator = gasEstimator;
19880
19308
  this._crypto = crypto;
19881
- this.quoteStrategies = {
19882
- indexed_rates: new IndexedRateIntentQuoteStrategy(
19883
- dest.configService,
19884
- () => this.requireIndexer().queryClient
19885
- ),
19886
- phantom_snapshot: new PhantomSnapshotIntentQuoteStrategy(
19887
- dest.configService,
19888
- () => this.requireIndexer().queryClient
19889
- ),
19890
- uniswap_v4: new UniswapV4IntentQuoteStrategy(dest.configService)
19891
- };
19309
+ this.orderbook = new HyperFxOrderbook(orderbookUrlFor(dest.config.stateMachineId));
19310
+ this.market = new OrderbookMarket(dest.configService, () => this.orderbook);
19892
19311
  }
19893
19312
  /**
19894
19313
  * Creates an initialized IntentGateway instance.
@@ -19934,89 +19353,50 @@ var IntentGateway = class _IntentGateway {
19934
19353
  }
19935
19354
  }
19936
19355
  /**
19937
- * Quotes an intent between this gateway's source and destination chains.
19938
- *
19939
- * Uses the indexer's latest aggregate directional pool rate by default. Pass
19940
- * `strategy: "phantom_snapshot"` or `strategy: "uniswap_v4"` only when
19941
- * explicitly requesting a legacy quote source. Provide exactly one of
19942
- * `amountIn` or `amountOut`.
19943
- *
19944
- * The gateway's source and destination chains resolve the configured order
19945
- * tokens; the indexer supplies the depth-weighted pool rate. Returned
19946
- * `amountIn`/`amountOut` already account for the gateway's protocol fee
19947
- * (`quoteMetadata.protocolFeeBps`), which the gateway deducts from order inputs.
19356
+ * Points quotes, liquidity and rate reads at a HyperFX orderbook other than
19357
+ * the destination chain's network deployment ({@link ORDERBOOK_URLS}).
19358
+ * Returns `this` for chaining.
19948
19359
  *
19949
- * @param params - Token pair, amount, and optional strategy/pool overrides.
19950
- * @returns The quoted amounts plus strategy-specific metadata.
19951
- * @throws {UnsupportedIntentQuoteStrategyError} For unknown strategies.
19952
- * @throws {UnsupportedIntentQuotePairError} When the selected strategy does not support the pair.
19953
- * @throws {IndexedRateUnavailableError} When the requested direction has no indexed rate.
19360
+ * @param orderbook - The orderbook's GraphQL URL, or a configured client.
19954
19361
  */
19362
+ withOrderbook(orderbook) {
19363
+ this.orderbook = typeof orderbook === "string" ? new HyperFxOrderbook(orderbook) : orderbook;
19364
+ return this;
19365
+ }
19955
19366
  async quoteIntent(params) {
19956
- const source = { stateMachineId: this.source.config.stateMachineId, client: this.source.client };
19957
- const destination = { stateMachineId: this.dest.config.stateMachineId, client: this.dest.client };
19958
- const strategy = params.strategy ?? "indexed_rates";
19959
- const handler = this.quoteStrategies[strategy];
19960
- if (!handler) throw new UnsupportedIntentQuoteStrategyError(strategy);
19961
- return handler.quote({ ...params, strategy }, source, destination);
19367
+ return this.market.quoteIntent(params, this.source.config.stateMachineId, this.dest.config.stateMachineId);
19962
19368
  }
19963
19369
  /**
19964
- * Returns indexed destination liquidity and its source-routing slices.
19370
+ * Returns the orderbook liquidity serving a swap of `tokenIn` on the source
19371
+ * chain for `tokenOut` on the destination chain: the best rate, true and
19372
+ * virtual depth, and the largest input the route can fill.
19965
19373
  *
19966
- * Destination, unrestricted, and explicit-route capacity come exclusively
19967
- * from the indexer's pair-centric liquidity entities. The SDK does not decide
19968
- * whether unrestricted bidders cover the source chain. Amounts reflect the
19969
- * latest rolling sample; they are not reservations or fill guarantees.
19970
- *
19971
- * Requires a prior call to {@link withQueryClient}.
19374
+ * Amounts reflect the live book; they are not reservations or fill guarantees.
19972
19375
  */
19973
19376
  async queryAvailableLiquidity(params) {
19974
- const { queryClient } = this.requireIndexer();
19975
- const sourceStateMachineId = getConfigByStateMachineId(this.source.config.stateMachineId)?.stateMachineId;
19976
- const destinationStateMachineId = getConfigByStateMachineId(this.dest.config.stateMachineId)?.stateMachineId;
19977
- if (!sourceStateMachineId) throw new UnsupportedLiquidityChainError(this.source.config.stateMachineId);
19978
- if (!destinationStateMachineId) throw new UnsupportedLiquidityChainError(this.dest.config.stateMachineId);
19979
- const sourceToken = this.source.configService.getAssetMetadataByAddress(sourceStateMachineId, params.tokenIn);
19980
- const destinationToken = this.dest.configService.getAssetMetadataByAddress(
19981
- destinationStateMachineId,
19982
- params.tokenOut
19377
+ return this.market.availableLiquidity(
19378
+ params,
19379
+ this.source.config.stateMachineId,
19380
+ this.dest.config.stateMachineId
19983
19381
  );
19984
- if (!sourceToken) throw new UnsupportedLiquidityAssetError(sourceStateMachineId, params.tokenIn);
19985
- if (!destinationToken) throw new UnsupportedLiquidityAssetError(destinationStateMachineId, params.tokenOut);
19986
- return new LiquidityEngine(queryClient).getAvailableLiquidity({
19987
- source: {
19988
- chain: sourceStateMachineId,
19989
- ...sourceToken
19990
- },
19991
- destination: {
19992
- chain: destinationStateMachineId,
19993
- ...destinationToken
19994
- }
19995
- });
19996
19382
  }
19997
19383
  /**
19998
- * Returns aggregate indexed pool buy and sell rates in less-valued quote-token
19999
- * units without requiring token addresses. Symbols are matched
20000
- * case-insensitively; chain IDs resolve configured token deployments.
19384
+ * Returns the orderbook's best bid and ask for a pair, with the mid and
19385
+ * spread, in quote-token units per one base token. Only orders filled on the
19386
+ * destination chain that accept swaps from the source chain are counted.
19387
+ * Symbols are matched case-insensitively; chain IDs resolve configured token
19388
+ * deployments.
20001
19389
  */
20002
19390
  async queryBuyAndSellRates(params) {
20003
- const { queryClient } = this.requireIndexer();
20004
19391
  const sourceChain = chainConfigs[params.sourceChainId]?.stateMachineId;
20005
19392
  const destinationChain = chainConfigs[params.destinationChainId]?.stateMachineId;
20006
19393
  if (!sourceChain) throw new UnsupportedLiquidityChainError(params.sourceChainId);
20007
19394
  if (!destinationChain) throw new UnsupportedLiquidityChainError(params.destinationChainId);
20008
- const sourceToken = this.source.configService.getAssetMetadataBySymbol(sourceChain, params.tokenInSymbol);
20009
- const destinationToken = this.dest.configService.getAssetMetadataBySymbol(
20010
- destinationChain,
20011
- params.tokenOutSymbol
20012
- );
20013
- if (!sourceToken) throw new UnsupportedLiquidityAssetError(sourceChain, params.tokenInSymbol);
20014
- if (!destinationToken) throw new UnsupportedLiquidityAssetError(destinationChain, params.tokenOutSymbol);
20015
- return new LiquidityEngine(queryClient).getBuyAndSellRates({
19395
+ return this.market.buyAndSellRates({
20016
19396
  sourceChain,
20017
19397
  destinationChain,
20018
- tokenInSymbol: sourceToken.symbol,
20019
- tokenOutSymbol: destinationToken.symbol
19398
+ tokenInSymbol: params.tokenInSymbol,
19399
+ tokenOutSymbol: params.tokenOutSymbol
20020
19400
  });
20021
19401
  }
20022
19402
  /**
@@ -20267,6 +19647,8 @@ var IntentGateway = class _IntentGateway {
20267
19647
  /**
20268
19648
  * Returns both the native token cost and the relayer fee for cancelling an
20269
19649
  * order. Use `relayerFee` to approve the ERC-20 spend before submitting.
19650
+ * Same-chain orders use the direct local route for either `from` option and
19651
+ * return `{ nativeValue: 0n, relayerFee: 0n }`.
20270
19652
  *
20271
19653
  * Delegates to {@link OrderCanceller.quoteCancelOrder}.
20272
19654
  *
@@ -20290,9 +19672,17 @@ var IntentGateway = class _IntentGateway {
20290
19672
  *
20291
19673
  * Delegates to {@link OrderCanceller.cancelOrder}.
20292
19674
  *
19675
+ * Same-chain orders always use the direct source-gateway route. The order
19676
+ * user may call through the deadline; any account may call strictly after
19677
+ * it, pays the transaction gas, and cannot change the refund beneficiary.
19678
+ * The SDK yields unsigned transaction fields and accepts either the caller's
19679
+ * signed raw transaction or its already-broadcast transaction hash.
19680
+ *
20293
19681
  * @param order - The order to cancel.
20294
- * @param indexerClient - Indexer client used for ISMP request status streaming.
20295
- * @param options - Choose the initiation side. Defaults to source-side cancellation.
19682
+ * @param indexerClient - Indexer client used for cross-chain ISMP request
19683
+ * status streaming; the same-chain route does not access it.
19684
+ * @param options - Choose the cross-chain initiation side. Defaults to source;
19685
+ * ignored for routing when source and destination are the same chain.
20296
19686
  * @yields {@link CancelEvent} objects describing each cancellation stage.
20297
19687
  */
20298
19688
  async *cancelOrder(order, indexerClient, options = {}) {
@@ -20422,7 +19812,7 @@ var IntentGateway = class _IntentGateway {
20422
19812
  if (estimate.totalGasCostWei === 0n || estimate.totalGasInFeeToken === 0n) {
20423
19813
  throw new Error("Gas estimation failed");
20424
19814
  }
20425
- const fees = isSameChain ? estimate.totalGasInFeeToken * 2n : (estimate.totalGasInFeeToken + estimate.relayerFeeInSourceFeeToken) * 105n / 100n;
19815
+ const fees = isSameChain ? estimate.totalGasInFeeToken * 2n : ((estimate.totalGasInFeeToken + estimate.relayerFeeInSourceFeeToken) * 105n + 99n) / 100n;
20426
19816
  const { address: feeToken } = await this.source.getFeeTokenWithDecimals();
20427
19817
  return {
20428
19818
  fees,
@@ -20455,17 +19845,27 @@ var IntentGateway = class _IntentGateway {
20455
19845
  return this._crypto.decodeERC7821Execute(callData);
20456
19846
  }
20457
19847
  /**
20458
- * Checks whether an order has been filled on the destination chain.
19848
+ * Checks whether an order is finalized on the destination chain.
20459
19849
  *
20460
19850
  * Delegates to {@link OrderStatusChecker.isOrderFilled}.
20461
19851
  *
20462
19852
  * @param order - The order to check.
20463
- * @returns `true` if the order's commitment slot on the destination chain is
20464
- * non-zero (i.e. `fillOrder` has been called successfully).
19853
+ * @returns `true` once a completing fill or a destination-side cancellation
19854
+ * has finalized the order; `false` while it is open or only partly filled.
20465
19855
  */
20466
19856
  async isOrderFilled(order) {
20467
19857
  return this.orderStatusChecker.isOrderFilled(order);
20468
19858
  }
19859
+ /**
19860
+ * Output credited to the order so far, one entry per leg.
19861
+ *
19862
+ * Delegates to {@link OrderStatusChecker.getFillProgress}.
19863
+ *
19864
+ * @param order - The order to check.
19865
+ */
19866
+ async getFillProgress(order) {
19867
+ return this.orderStatusChecker.getFillProgress(order);
19868
+ }
20469
19869
  /**
20470
19870
  * Checks whether all escrowed inputs for an order have been refunded on the
20471
19871
  * source chain.
@@ -20473,8 +19873,8 @@ var IntentGateway = class _IntentGateway {
20473
19873
  * Delegates to {@link OrderStatusChecker.isOrderRefunded}.
20474
19874
  *
20475
19875
  * @param order - The order to check.
20476
- * @returns `true` if every input token's escrowed amount has been zeroed out
20477
- * in the `_orders` mapping on the source chain.
19876
+ * @returns `true` if every input's escrowed amount has been zeroed out
19877
+ * in the per-leg `_orders` mapping on the source chain.
20478
19878
  */
20479
19879
  async isOrderRefunded(order) {
20480
19880
  return this.orderStatusChecker.isOrderRefunded(order);
@@ -20511,9 +19911,7 @@ var IntentGateway = class _IntentGateway {
20511
19911
  }
20512
19912
  requireIndexer() {
20513
19913
  if (!this.indexer) {
20514
- throw new Error(
20515
- "IntentGateway: call withQueryClient(queryClient) before using indexer-backed methods or Phantom quotes"
20516
- );
19914
+ throw new Error("IntentGateway: call withQueryClient(queryClient) before using indexer-backed methods");
20517
19915
  }
20518
19916
  return this.indexer;
20519
19917
  }
@@ -20536,74 +19934,64 @@ var IntentGateway = class _IntentGateway {
20536
19934
  const { queryClient, pollInterval, logger } = this.requireIndexer();
20537
19935
  const streamLogger = logger.withTag("[orderStatusStream]");
20538
19936
  const TERMINAL = ["FILLED", "REDEEMED", "REFUNDED"];
19937
+ const latestOrderStatus = (order2) => {
19938
+ const latest = order2.statuses[order2.statuses.length - 1];
19939
+ if (latest.status === "CANCELLED") {
19940
+ for (let i = order2.statuses.length - 1; i >= 0; i--) {
19941
+ if (TERMINAL.includes(order2.statuses[i].status)) return order2.statuses[i];
19942
+ }
19943
+ }
19944
+ return latest;
19945
+ };
20539
19946
  let order;
20540
19947
  while (!order) {
20541
19948
  await sleep(pollInterval);
20542
19949
  order = await _queryOrderInternal({ commitmentHash: commitment, queryClient, logger });
20543
19950
  }
20544
19951
  streamLogger.trace("`Order` found");
20545
- const latestStatus = order.statuses[order.statuses.length - 1];
19952
+ let latestStatus = latestOrderStatus(order);
20546
19953
  yield { status: latestStatus.status, metadata: latestStatus.metadata };
20547
19954
  if (TERMINAL.includes(latestStatus.status)) return;
20548
19955
  while (true) {
20549
19956
  await sleep(pollInterval);
20550
19957
  const updatedOrder = await _queryOrderInternal({ commitmentHash: commitment, queryClient, logger });
20551
19958
  if (!updatedOrder) continue;
20552
- const newLatestStatus = updatedOrder.statuses[updatedOrder.statuses.length - 1];
19959
+ const newLatestStatus = latestOrderStatus(updatedOrder);
20553
19960
  if (newLatestStatus.status !== latestStatus.status) {
19961
+ latestStatus = newLatestStatus;
20554
19962
  yield { status: newLatestStatus.status, metadata: newLatestStatus.metadata };
20555
19963
  if (TERMINAL.includes(newLatestStatus.status)) return;
20556
19964
  }
20557
19965
  }
20558
19966
  }
20559
19967
  };
20560
- function rpcFetch() {
20561
- const f = globalThis.fetch;
20562
- if (typeof f !== "function") {
20563
- throw new Error("No fetch available; call setAggregationFetch() before using the aggregation helpers");
20564
- }
20565
- return f;
19968
+
19969
+ // src/protocols/intents/liquidity-pool.ts
19970
+ function sortPoolSymbols(symbolA, symbolB) {
19971
+ return symbolA.toLowerCase() <= symbolB.toLowerCase() ? [symbolA, symbolB] : [symbolB, symbolA];
20566
19972
  }
20567
- var PhantomRpcError = class extends Error {
20568
- constructor(message, cause) {
20569
- super(message);
20570
- this.cause = cause;
20571
- this.name = "PhantomRpcError";
20572
- }
20573
- cause;
20574
- };
20575
- async function rpcCall(url, payload) {
20576
- let lastErr;
20577
- for (let attempt = 0; attempt < 4; attempt++) {
20578
- if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, 150 * attempt));
20579
- let timer;
20580
- try {
20581
- const timeout = new Promise((_, reject) => {
20582
- timer = setTimeout(() => reject(new Error(`rpc timeout: ${url}`)), 12e3);
20583
- });
20584
- const response = await Promise.race([
20585
- rpcFetch()(url, {
20586
- method: "POST",
20587
- headers: { accept: "application/json", "content-type": "application/json" },
20588
- body: JSON.stringify(payload)
20589
- }),
20590
- timeout
20591
- ]);
20592
- const body = await response.json();
20593
- if (body?.error) {
20594
- lastErr = new Error(`rpc error: ${JSON.stringify(body.error).slice(0, 200)}`);
20595
- continue;
20596
- }
20597
- return body;
20598
- } catch (err) {
20599
- lastErr = err;
20600
- } finally {
20601
- if (timer) clearTimeout(timer);
20602
- }
20603
- }
20604
- throw new PhantomRpcError(`RPC call failed after 4 attempts: ${url}`, lastErr);
19973
+ function poolSlug(symbolA, symbolB) {
19974
+ return sortPoolSymbols(symbolA, symbolB).join("-");
19975
+ }
19976
+
19977
+ // src/protocols/intents/rateFill.ts
19978
+ function ceilDiv(numerator, denominator) {
19979
+ return numerator === 0n ? 0n : (numerator - 1n) / denominator + 1n;
19980
+ }
19981
+ function previewRateFill(escrow, required, filled, take, offered) {
19982
+ if (escrow <= 0n || required <= 0n || take <= 0n || offered <= 0n) {
19983
+ throw new Error("RateFillInvalidAmount");
19984
+ }
19985
+ if (filled < 0n || filled >= required) throw new Error("RateFillNoProgress");
19986
+ if (ceilDiv(take * required, escrow) > offered) throw new Error("RateBelowOrder");
19987
+ const uncappedCredit = take * required / escrow;
19988
+ const credit = uncappedCredit < required - filled ? uncappedCredit : required - filled;
19989
+ const release = escrow * (filled + credit) / required - escrow * filled / required;
19990
+ if (credit === 0n || release === 0n) throw new Error("RateFillNoProgress");
19991
+ const ratePayment = ceilDiv(offered * release, take);
19992
+ const delivered = ratePayment > credit ? ratePayment : credit;
19993
+ return { credit, release, delivered, surplus: delivered - credit };
20605
19994
  }
20606
- var FILL_ORDER_ABI = IntentGatewayV2_default.ABI;
20607
19995
  var DECLARATION_V1 = 1;
20608
19996
  var DECLARATION_V2 = 2;
20609
19997
  var PAYMASTER_ADDRESS_BYTES = 20;
@@ -20801,40 +20189,6 @@ function encodeAcceptedSourceChains(chains2) {
20801
20189
  function decodeAcceptedSourceChains(paymasterAndData) {
20802
20190
  return decodePhantomBidDeclaration(paymasterAndData).acceptedSources;
20803
20191
  }
20804
- var UNISWAP_QUOTE_HAIRCUT_BPS = 10n;
20805
- function haircut(amount, bps) {
20806
- return amount * (10000n - bps) / 10000n;
20807
- }
20808
- function applyUniswapQuoteHaircut(amount) {
20809
- return haircut(amount, UNISWAP_QUOTE_HAIRCUT_BPS);
20810
- }
20811
- var SELECTOR_GATEWAY_PARAMS = "0xcff0ab96";
20812
- var GATEWAY_PARAMS_WORDS = 6;
20813
- var GATEWAY_PARAMS_FEE_WORD = 4;
20814
- async function readProtocolFeeHaircutBps(evmRpcUrl, gatewayAddress) {
20815
- const result = await rpcCall(evmRpcUrl, {
20816
- id: 1,
20817
- jsonrpc: "2.0",
20818
- method: "eth_call",
20819
- params: [{ to: gatewayAddress, data: SELECTOR_GATEWAY_PARAMS }, "latest"]
20820
- });
20821
- const hex = result.result;
20822
- if (typeof hex !== "string" || hex.length < 2 + 64 * GATEWAY_PARAMS_WORDS) {
20823
- throw new PhantomRpcError(`IntentGateway.params() returned no usable result from ${gatewayAddress} on ${evmRpcUrl}`);
20824
- }
20825
- const start = 2 + 64 * GATEWAY_PARAMS_FEE_WORD;
20826
- const protocolFeeBps = BigInt(`0x${hex.slice(start, start + 64)}`);
20827
- if (protocolFeeBps >= 10000n) {
20828
- throw new PhantomRpcError(`IntentGateway ${gatewayAddress} reports an implausible protocol fee: ${protocolFeeBps} bps`);
20829
- }
20830
- return protocolFeeBps;
20831
- }
20832
- function applyProtocolFeeHaircut(amount, protocolFeeBps) {
20833
- return haircut(amount, protocolFeeBps);
20834
- }
20835
- FILL_ORDER_ABI.find(
20836
- (item) => item?.type === "function" && item?.name === "fillOrder"
20837
- )?.inputs?.[0];
20838
20192
  var TokenGateway = class {
20839
20193
  source;
20840
20194
  dest;
@@ -25050,9 +24404,9 @@ async function teleport(teleport_param) {
25050
24404
  if (scaleEncodedAssetId === null) {
25051
24405
  throw new Error("Unknown asset id provided");
25052
24406
  }
25053
- const destinationStateMachine = normalizeStateMachineId(params.destination);
25054
- const destination = convertStateMachineIdToEnum(destinationStateMachine);
25055
- const recipient = viem.hexToBytes(normalizeAddressForStateMachine(params.recipient, destinationStateMachine));
24407
+ const destinationStateMachine2 = normalizeStateMachineId(params.destination);
24408
+ const destination = convertStateMachineIdToEnum(destinationStateMachine2);
24409
+ const recipient = viem.hexToBytes(normalizeAddressForStateMachine(params.recipient, destinationStateMachine2));
25056
24410
  const teleportParams = {
25057
24411
  destination,
25058
24412
  recepient: Array.from(recipient),
@@ -25307,7 +24661,7 @@ async function teleportDot(param_) {
25307
24661
 
25308
24662
  exports.ADDRESS_ZERO = ADDRESS_ZERO2;
25309
24663
  exports.BundlerMethod = BundlerMethod;
25310
- exports.CHAINS_WITHOUT_VALID_UNTIL = CHAINS_WITHOUT_VALID_UNTIL;
24664
+ exports.CONTRACT_VERSION_ABI = CONTRACT_VERSION_ABI;
25311
24665
  exports.ChainConfigService = ChainConfigService;
25312
24666
  exports.Chains = Chains;
25313
24667
  exports.CryptoUtils = CryptoUtils;
@@ -25320,29 +24674,31 @@ exports.ERC7821_BATCH_MODE = ERC7821_BATCH_MODE;
25320
24674
  exports.EvmChain = EvmChain;
25321
24675
  exports.EvmHostABI = ABI;
25322
24676
  exports.EvmLanguage = EvmLanguage;
25323
- exports.FILL_ORDER_V1_ABI = FILL_ORDER_V1_ABI;
24677
+ exports.FILL_ORDER_SELECTOR = FILL_ORDER_SELECTOR;
25324
24678
  exports.HyperClientStatus = HyperClientStatus;
25325
24679
  exports.HyperFungibleToken = HyperFungibleToken;
25326
24680
  exports.HyperFungibleTokenABI = HyperFungibleTokenABI;
24681
+ exports.HyperFxOrderbook = HyperFxOrderbook;
25327
24682
  exports.INCLUSION_TIMEOUT_MS = INCLUSION_TIMEOUT_MS;
25328
- exports.IndexedRateUnavailableError = IndexedRateUnavailableError;
24683
+ exports.InsufficientOrderbookLiquidityError = InsufficientOrderbookLiquidityError;
25329
24684
  exports.IntentGateway = IntentGateway;
25330
24685
  exports.IntentGatewayABI = ABI3;
25331
24686
  exports.IntentOrderStatus = IntentOrderStatus;
25332
24687
  exports.IntentsCoprocessor = IntentsCoprocessor;
25333
- exports.InvalidIndexedRateError = InvalidIndexedRateError;
25334
- exports.InvalidLiquidityIndexerResponseError = InvalidLiquidityIndexerResponseError;
25335
- exports.InvalidPhantomSnapshotError = InvalidPhantomSnapshotError;
25336
24688
  exports.IsmpClient = IsmpClient;
25337
- exports.LEGACY_FILL_OPTIONS_IMPLEMENTATIONS = LEGACY_FILL_OPTIONS_IMPLEMENTATIONS;
24689
+ exports.MAX_DECLARED_ENTRIES = MAX_DECLARED_ENTRIES;
25338
24690
  exports.MOCK_ADDRESS = MOCK_ADDRESS;
24691
+ exports.ORDERBOOK_DECIMALS = ORDERBOOK_DECIMALS;
24692
+ exports.ORDERBOOK_QUERIES = ORDERBOOK_QUERIES;
24693
+ exports.ORDERBOOK_URLS = ORDERBOOK_URLS;
25339
24694
  exports.ORDER_V2_PARAM_TYPE = ORDER_V2_PARAM_TYPE;
25340
24695
  exports.OrderStatus = OrderStatus;
25341
24696
  exports.OrderStatusChecker = OrderStatusChecker;
24697
+ exports.OrderbookQuoteNotConvergedError = OrderbookQuoteNotConvergedError;
24698
+ exports.OrderbookRequestError = OrderbookRequestError;
25342
24699
  exports.PACKED_USEROP_TYPEHASH = PACKED_USEROP_TYPEHASH;
25343
24700
  exports.PERMIT2_SPONSORSHIP_BYTES = PERMIT2_SPONSORSHIP_BYTES;
25344
24701
  exports.PLACE_ORDER_SELECTOR = PLACE_ORDER_SELECTOR;
25345
- exports.PhantomSnapshotUnavailableError = PhantomSnapshotUnavailableError;
25346
24702
  exports.PharosChain = PharosChain;
25347
24703
  exports.PolkadotHubChain = PolkadotHubChain;
25348
24704
  exports.REQUEST_COMMITMENTS_SLOT = REQUEST_COMMITMENTS_SLOT;
@@ -25353,6 +24709,7 @@ exports.RequestKind = RequestKind;
25353
24709
  exports.RequestStatus = RequestStatus;
25354
24710
  exports.SELECT_SOLVER_TYPEHASH = SELECT_SOLVER_TYPEHASH;
25355
24711
  exports.STATE_COMMITMENTS_SLOT = STATE_COMMITMENTS_SLOT;
24712
+ exports.SUPPORTED_INTENTS_VERSION = SUPPORTED_INTENTS_VERSION;
25356
24713
  exports.SubstrateChain = SubstrateChain;
25357
24714
  exports.Swap = Swap;
25358
24715
  exports.TESTNET_CHAINS = TESTNET_CHAINS;
@@ -25360,17 +24717,13 @@ exports.TeleportStatus = TeleportStatus;
25360
24717
  exports.TimeoutStatus = TimeoutStatus;
25361
24718
  exports.TokenGateway = TokenGateway;
25362
24719
  exports.TronChain = TronChain;
25363
- exports.UNISWAP_QUOTE_HAIRCUT_BPS = UNISWAP_QUOTE_HAIRCUT_BPS;
25364
24720
  exports.USE_ETHERSCAN_CHAINS = USE_ETHERSCAN_CHAINS;
25365
- exports.UnsupportedIntentQuotePairError = UnsupportedIntentQuotePairError;
25366
- exports.UnsupportedIntentQuoteStrategyError = UnsupportedIntentQuoteStrategyError;
25367
24721
  exports.UnsupportedLiquidityAssetError = UnsupportedLiquidityAssetError;
25368
24722
  exports.UnsupportedLiquidityChainError = UnsupportedLiquidityChainError;
25369
24723
  exports.WrappedHyperFungibleTokenABI = WrappedHyperFungibleTokenABI;
25370
24724
  exports.__test = __test;
25371
24725
  exports.adjustDecimals = adjustDecimals;
25372
- exports.applyProtocolFeeHaircut = applyProtocolFeeHaircut;
25373
- exports.applyUniswapQuoteHaircut = applyUniswapQuoteHaircut;
24726
+ exports.assertGatewayRelease = assertGatewayRelease;
25374
24727
  exports.bytes20ToBytes32 = bytes20ToBytes32;
25375
24728
  exports.bytes32ToBytes20 = bytes32ToBytes20;
25376
24729
  exports.calculateAllowanceMappingLocation = calculateAllowanceMappingLocation;
@@ -25411,7 +24764,6 @@ exports.getChainId = getChainId;
25411
24764
  exports.getConfigByStateMachineId = getConfigByStateMachineId;
25412
24765
  exports.getContractCallInput = getContractCallInput;
25413
24766
  exports.getContractCallInputs = getContractCallInputs;
25414
- exports.getFillOptionsVersion = getFillOptionsVersion;
25415
24767
  exports.getGasPriceFromEtherscan = getGasPriceFromEtherscan;
25416
24768
  exports.getOrFetchStorageSlot = getOrFetchStorageSlot;
25417
24769
  exports.getOrderPlacedFromTx = getOrderPlacedFromTx;
@@ -25424,6 +24776,7 @@ exports.getStorageSlot = getStorageSlot;
25424
24776
  exports.getViemChain = getViemChain;
25425
24777
  exports.hexToString = hexToString;
25426
24778
  exports.hyperbridgeAddress = hyperbridgeAddress;
24779
+ exports.isCanonicalEvmToken = isCanonicalEvmToken;
25427
24780
  exports.maxBigInt = maxBigInt;
25428
24781
  exports.normalizeAddressForEvmBytes32 = normalizeAddressForEvmBytes32;
25429
24782
  exports.normalizeAddressForStateMachine = normalizeAddressForStateMachine;
@@ -25431,6 +24784,7 @@ exports.normalizeEvmAddress = normalizeEvmAddress;
25431
24784
  exports.normalizeEvmChainId = normalizeEvmChainId;
25432
24785
  exports.normalizeStateMachineId = normalizeStateMachineId;
25433
24786
  exports.orderCommitment = orderCommitment;
24787
+ exports.orderbookUrlFor = orderbookUrlFor;
25434
24788
  exports.parseStateMachineId = parseStateMachineId;
25435
24789
  exports.pharosAtlantic = pharosAtlantic;
25436
24790
  exports.pharosMainnet = pharosMainnet;
@@ -25438,16 +24792,18 @@ exports.polkadotAssetHubPaseo = polkadotAssetHubPaseo;
25438
24792
  exports.polkadotHubMainnet = polkadotHubMainnet;
25439
24793
  exports.poolSlug = poolSlug;
25440
24794
  exports.postRequestCommitment = postRequestCommitment;
24795
+ exports.previewRateFill = previewRateFill;
25441
24796
  exports.queryAssetTeleported = queryAssetTeleported;
25442
24797
  exports.queryGetRequest = queryGetRequest;
25443
24798
  exports.queryPostRequest = queryPostRequest;
25444
24799
  exports.quoteUniswap = quoteUniswap;
25445
- exports.readProtocolFeeHaircutBps = readProtocolFeeHaircutBps;
24800
+ exports.readLegEscrow = readLegEscrow;
24801
+ exports.readLegPartialFill = readLegPartialFill;
25446
24802
  exports.requestCommitmentKey = requestCommitmentKey;
25447
- exports.resetFillOptionsVersionCache = resetFillOptionsVersionCache;
25448
24803
  exports.responseCommitmentKey = responseCommitmentKey;
25449
24804
  exports.retryPromise = retryPromise;
25450
24805
  exports.sortPoolSymbols = sortPoolSymbols;
24806
+ exports.supportsRateFills = supportsRateFills;
25451
24807
  exports.teleport = teleport;
25452
24808
  exports.teleportDot = teleportDot;
25453
24809
  exports.transformOrderForContract = transformOrderForContract;