@hyperbridge/sdk 2.8.8 → 2.8.11

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.
@@ -2722,7 +2722,7 @@ var chainConfigs = {
2722
2722
  },
2723
2723
  addresses: {
2724
2724
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
2725
- SolverAccount: "0xfCd233b937D7622AAc63ced3C9A1A12F4a6B64E3",
2725
+ SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
2726
2726
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
2727
2727
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
2728
2728
  UniswapRouter02: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
@@ -2789,7 +2789,7 @@ var chainConfigs = {
2789
2789
  },
2790
2790
  addresses: {
2791
2791
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
2792
- SolverAccount: "0xfCd233b937D7622AAc63ced3C9A1A12F4a6B64E3",
2792
+ SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
2793
2793
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
2794
2794
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
2795
2795
  UniswapRouter02: "0x10ED43C718714eb63d5aA57B78B54704E256024E",
@@ -2850,7 +2850,7 @@ var chainConfigs = {
2850
2850
  },
2851
2851
  addresses: {
2852
2852
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
2853
- SolverAccount: "0xfCd233b937D7622AAc63ced3C9A1A12F4a6B64E3",
2853
+ SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
2854
2854
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
2855
2855
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
2856
2856
  UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
@@ -2924,7 +2924,7 @@ var chainConfigs = {
2924
2924
  },
2925
2925
  addresses: {
2926
2926
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
2927
- SolverAccount: "0xfCd233b937D7622AAc63ced3C9A1A12F4a6B64E3",
2927
+ SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
2928
2928
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
2929
2929
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
2930
2930
  UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
@@ -2999,7 +2999,7 @@ var chainConfigs = {
2999
2999
  },
3000
3000
  addresses: {
3001
3001
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
3002
- SolverAccount: "0xfCd233b937D7622AAc63ced3C9A1A12F4a6B64E3",
3002
+ SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
3003
3003
  TokenGateway: "0x8b536105b6Fae2aE9199f5146D3C57Dfe53b614E",
3004
3004
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
3005
3005
  UniswapRouter02: "0xd2f9496824951D5237cC71245D659E48d0d5f9E8",
@@ -3115,7 +3115,7 @@ var chainConfigs = {
3115
3115
  },
3116
3116
  addresses: {
3117
3117
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
3118
- SolverAccount: "0xfCd233b937D7622AAc63ced3C9A1A12F4a6B64E3",
3118
+ SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
3119
3119
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
3120
3120
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
3121
3121
  UniswapRouter02: "0x4A7b5Da61326A6379179b40d00F57E5bbDC962c2",
@@ -3150,7 +3150,7 @@ var chainConfigs = {
3150
3150
  },
3151
3151
  addresses: {
3152
3152
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
3153
- SolverAccount: "0xfCd233b937D7622AAc63ced3C9A1A12F4a6B64E3",
3153
+ SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
3154
3154
  TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
3155
3155
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
3156
3156
  UniswapRouter02: "0xB2e26652e4BAd1e56055A051f922E06760cA0BFE",
@@ -3306,7 +3306,7 @@ var chainConfigs = {
3306
3306
  },
3307
3307
  addresses: {
3308
3308
  IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
3309
- SolverAccount: "0xfCd233b937D7622AAc63ced3C9A1A12F4a6B64E3",
3309
+ SolverAccount: "0x7cb55539d1144F62422099c3FA3405092022c88C",
3310
3310
  Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
3311
3311
  Calldispatcher: "0xE2C7e576E26E0bE7aC97c6fE925bcDAbD87c4bEd"
3312
3312
  },
@@ -3353,7 +3353,7 @@ var ABI3 = [
3353
3353
  type: "constructor",
3354
3354
  inputs: [
3355
3355
  {
3356
- name: "admin",
3356
+ name: "owner",
3357
3357
  type: "address",
3358
3358
  internalType: "address"
3359
3359
  }
@@ -3428,25 +3428,6 @@ var ABI3 = [
3428
3428
  ],
3429
3429
  stateMutability: "view"
3430
3430
  },
3431
- {
3432
- type: "function",
3433
- name: "_instances",
3434
- inputs: [
3435
- {
3436
- name: "",
3437
- type: "bytes32",
3438
- internalType: "bytes32"
3439
- }
3440
- ],
3441
- outputs: [
3442
- {
3443
- name: "",
3444
- type: "address",
3445
- internalType: "address"
3446
- }
3447
- ],
3448
- stateMutability: "view"
3449
- },
3450
3431
  {
3451
3432
  type: "function",
3452
3433
  name: "_nonce",
@@ -3484,6 +3465,19 @@ var ABI3 = [
3484
3465
  ],
3485
3466
  stateMutability: "view"
3486
3467
  },
3468
+ {
3469
+ type: "function",
3470
+ name: "_owner",
3471
+ inputs: [],
3472
+ outputs: [
3473
+ {
3474
+ name: "",
3475
+ type: "address",
3476
+ internalType: "address"
3477
+ }
3478
+ ],
3479
+ stateMutability: "view"
3480
+ },
3487
3481
  {
3488
3482
  type: "function",
3489
3483
  name: "_partialFills",
@@ -3858,6 +3852,11 @@ var ABI3 = [
3858
3852
  type: "uint256",
3859
3853
  internalType: "uint256"
3860
3854
  },
3855
+ {
3856
+ name: "validUntil",
3857
+ type: "uint256",
3858
+ internalType: "uint256"
3859
+ },
3861
3860
  {
3862
3861
  name: "outputs",
3863
3862
  type: "tuple[]",
@@ -3894,6 +3893,61 @@ var ABI3 = [
3894
3893
  ],
3895
3894
  stateMutability: "view"
3896
3895
  },
3896
+ {
3897
+ type: "function",
3898
+ name: "initialize",
3899
+ inputs: [
3900
+ {
3901
+ name: "p",
3902
+ type: "tuple",
3903
+ internalType: "struct Params",
3904
+ components: [
3905
+ {
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"
3919
+ },
3920
+ {
3921
+ name: "surplusShareBps",
3922
+ type: "uint256",
3923
+ internalType: "uint256"
3924
+ },
3925
+ {
3926
+ name: "protocolFeeBps",
3927
+ type: "uint256",
3928
+ internalType: "uint256"
3929
+ },
3930
+ {
3931
+ name: "priceOracle",
3932
+ type: "address",
3933
+ internalType: "address"
3934
+ }
3935
+ ]
3936
+ },
3937
+ {
3938
+ name: "peerChains",
3939
+ type: "bytes[]",
3940
+ internalType: "bytes[]"
3941
+ },
3942
+ {
3943
+ name: "relayer",
3944
+ type: "address",
3945
+ internalType: "address"
3946
+ }
3947
+ ],
3948
+ outputs: [],
3949
+ stateMutability: "nonpayable"
3950
+ },
3897
3951
  {
3898
3952
  type: "function",
3899
3953
  name: "instance",
@@ -3913,6 +3967,19 @@ var ABI3 = [
3913
3967
  ],
3914
3968
  stateMutability: "view"
3915
3969
  },
3970
+ {
3971
+ type: "function",
3972
+ name: "migrate",
3973
+ inputs: [
3974
+ {
3975
+ name: "relayer",
3976
+ type: "address",
3977
+ internalType: "address"
3978
+ }
3979
+ ],
3980
+ outputs: [],
3981
+ stateMutability: "nonpayable"
3982
+ },
3916
3983
  {
3917
3984
  type: "function",
3918
3985
  name: "onAccept",
@@ -4424,7 +4491,7 @@ var ABI3 = [
4424
4491
  internalType: "uint256"
4425
4492
  }
4426
4493
  ],
4427
- stateMutability: "view"
4494
+ stateMutability: "nonpayable"
4428
4495
  },
4429
4496
  {
4430
4497
  type: "function",
@@ -4480,6 +4547,19 @@ var ABI3 = [
4480
4547
  internalType: "uint256"
4481
4548
  }
4482
4549
  ],
4550
+ stateMutability: "nonpayable"
4551
+ },
4552
+ {
4553
+ type: "function",
4554
+ name: "relayer",
4555
+ inputs: [],
4556
+ outputs: [
4557
+ {
4558
+ name: "",
4559
+ type: "address",
4560
+ internalType: "address"
4561
+ }
4562
+ ],
4483
4563
  stateMutability: "view"
4484
4564
  },
4485
4565
  {
@@ -4520,66 +4600,67 @@ var ABI3 = [
4520
4600
  },
4521
4601
  {
4522
4602
  type: "function",
4523
- name: "init",
4603
+ name: "setRelayer",
4524
4604
  inputs: [
4525
4605
  {
4526
- name: "p",
4527
- type: "tuple",
4528
- internalType: "struct Params",
4529
- components: [
4530
- {
4531
- name: "host",
4532
- type: "address",
4533
- internalType: "address"
4534
- },
4535
- {
4536
- name: "dispatcher",
4537
- type: "address",
4538
- internalType: "address"
4539
- },
4540
- {
4541
- name: "solverSelection",
4542
- type: "bool",
4543
- internalType: "bool"
4544
- },
4545
- {
4546
- name: "surplusShareBps",
4547
- type: "uint256",
4548
- internalType: "uint256"
4549
- },
4550
- {
4551
- name: "protocolFeeBps",
4552
- type: "uint256",
4553
- internalType: "uint256"
4554
- },
4555
- {
4556
- name: "priceOracle",
4557
- type: "address",
4558
- internalType: "address"
4559
- }
4560
- ]
4606
+ name: "relayer",
4607
+ type: "address",
4608
+ internalType: "address"
4609
+ }
4610
+ ],
4611
+ outputs: [],
4612
+ stateMutability: "nonpayable"
4613
+ },
4614
+ {
4615
+ type: "function",
4616
+ name: "upgradeToAndCall",
4617
+ inputs: [
4618
+ {
4619
+ name: "newImplementation",
4620
+ type: "address",
4621
+ internalType: "address"
4561
4622
  },
4562
4623
  {
4563
- name: "deployments",
4564
- type: "tuple[]",
4565
- internalType: "struct Deployment[]",
4566
- components: [
4567
- {
4568
- name: "chain",
4569
- type: "bytes",
4570
- internalType: "bytes"
4571
- },
4572
- {
4573
- name: "gateway",
4574
- type: "address",
4575
- internalType: "address"
4576
- }
4577
- ]
4624
+ name: "data",
4625
+ type: "bytes",
4626
+ internalType: "bytes"
4578
4627
  }
4579
4628
  ],
4580
4629
  outputs: [],
4581
4630
  stateMutability: "nonpayable"
4582
4631
  },
4632
+ {
4633
+ type: "function",
4634
+ name: "version",
4635
+ inputs: [],
4636
+ outputs: [
4637
+ {
4638
+ name: "",
4639
+ type: "uint64",
4640
+ internalType: "uint64"
4641
+ }
4642
+ ],
4643
+ stateMutability: "view"
4644
+ },
4645
+ {
4646
+ type: "event",
4647
+ name: "DeploymentAdded",
4648
+ inputs: [
4649
+ {
4650
+ name: "chain",
4651
+ type: "string",
4652
+ indexed: false,
4653
+ internalType: "string"
4654
+ },
4655
+ {
4656
+ name: "gateway",
4657
+ type: "address",
4658
+ indexed: false,
4659
+ internalType: "address"
4660
+ }
4661
+ ],
4662
+ anonymous: false
4663
+ },
4583
4664
  {
4584
4665
  type: "event",
4585
4666
  name: "DestinationProtocolFeeUpdated",
@@ -4713,16 +4794,29 @@ var ABI3 = [
4713
4794
  },
4714
4795
  {
4715
4796
  type: "event",
4716
- name: "DeploymentAdded",
4797
+ name: "Initialized",
4717
4798
  inputs: [
4718
4799
  {
4719
- name: "chain",
4720
- type: "string",
4800
+ name: "version",
4801
+ type: "uint64",
4721
4802
  indexed: false,
4722
- internalType: "string"
4803
+ internalType: "uint64"
4804
+ }
4805
+ ],
4806
+ anonymous: false
4807
+ },
4808
+ {
4809
+ type: "event",
4810
+ name: "OrderCancelled",
4811
+ inputs: [
4812
+ {
4813
+ name: "commitment",
4814
+ type: "bytes32",
4815
+ indexed: true,
4816
+ internalType: "bytes32"
4723
4817
  },
4724
4818
  {
4725
- name: "gateway",
4819
+ name: "canceller",
4726
4820
  type: "address",
4727
4821
  indexed: false,
4728
4822
  internalType: "address"
@@ -5050,14 +5144,52 @@ var ABI3 = [
5050
5144
  ],
5051
5145
  anonymous: false
5052
5146
  },
5147
+ {
5148
+ type: "event",
5149
+ name: "RelayerUpdated",
5150
+ inputs: [
5151
+ {
5152
+ name: "previous",
5153
+ type: "address",
5154
+ indexed: false,
5155
+ internalType: "address"
5156
+ },
5157
+ {
5158
+ name: "current",
5159
+ type: "address",
5160
+ indexed: false,
5161
+ internalType: "address"
5162
+ }
5163
+ ],
5164
+ anonymous: false
5165
+ },
5166
+ {
5167
+ type: "event",
5168
+ name: "Upgraded",
5169
+ inputs: [
5170
+ {
5171
+ name: "implementation",
5172
+ type: "address",
5173
+ indexed: true,
5174
+ internalType: "address"
5175
+ }
5176
+ ],
5177
+ anonymous: false
5178
+ },
5053
5179
  {
5054
5180
  type: "error",
5055
- name: "Cancelled",
5056
- inputs: []
5181
+ name: "AddressEmptyCode",
5182
+ inputs: [
5183
+ {
5184
+ name: "target",
5185
+ type: "address",
5186
+ internalType: "address"
5187
+ }
5188
+ ]
5057
5189
  },
5058
5190
  {
5059
5191
  type: "error",
5060
- name: "UnknownInstance",
5192
+ name: "Cancelled",
5061
5193
  inputs: []
5062
5194
  },
5063
5195
  {
@@ -5078,18 +5210,44 @@ var ABI3 = [
5078
5210
  },
5079
5211
  {
5080
5212
  type: "error",
5081
- name: "ECDSAInvalidSignatureS",
5082
- inputs: [
5083
- {
5084
- name: "s",
5085
- type: "bytes32",
5086
- internalType: "bytes32"
5087
- }
5088
- ]
5213
+ name: "ECDSAInvalidSignatureS",
5214
+ inputs: [
5215
+ {
5216
+ name: "s",
5217
+ type: "bytes32",
5218
+ internalType: "bytes32"
5219
+ }
5220
+ ]
5221
+ },
5222
+ {
5223
+ 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",
5236
+ inputs: []
5237
+ },
5238
+ {
5239
+ type: "error",
5240
+ name: "Expired",
5241
+ inputs: []
5242
+ },
5243
+ {
5244
+ type: "error",
5245
+ name: "FailedCall",
5246
+ inputs: []
5089
5247
  },
5090
5248
  {
5091
5249
  type: "error",
5092
- name: "Expired",
5250
+ name: "FillExpired",
5093
5251
  inputs: []
5094
5252
  },
5095
5253
  {
@@ -5102,6 +5260,11 @@ var ABI3 = [
5102
5260
  name: "InsufficientNativeToken",
5103
5261
  inputs: []
5104
5262
  },
5263
+ {
5264
+ type: "error",
5265
+ name: "InvalidInitialization",
5266
+ inputs: []
5267
+ },
5105
5268
  {
5106
5269
  type: "error",
5107
5270
  name: "InvalidInput",
@@ -5117,6 +5280,21 @@ var ABI3 = [
5117
5280
  name: "NotExpired",
5118
5281
  inputs: []
5119
5282
  },
5283
+ {
5284
+ type: "error",
5285
+ name: "NotInitializing",
5286
+ inputs: []
5287
+ },
5288
+ {
5289
+ type: "error",
5290
+ name: "PartialFillNotAllowed",
5291
+ inputs: []
5292
+ },
5293
+ {
5294
+ type: "error",
5295
+ name: "ReentrancyGuardReentrantCall",
5296
+ inputs: []
5297
+ },
5120
5298
  {
5121
5299
  type: "error",
5122
5300
  name: "SafeERC20FailedOperation",
@@ -5154,6 +5332,11 @@ var ABI3 = [
5154
5332
  name: "UnexpectedCall",
5155
5333
  inputs: []
5156
5334
  },
5335
+ {
5336
+ type: "error",
5337
+ name: "UnknownInstance",
5338
+ inputs: []
5339
+ },
5157
5340
  {
5158
5341
  type: "error",
5159
5342
  name: "UnknownOrder",
@@ -7825,6 +8008,255 @@ function encodeISMPMessage(message) {
7825
8008
  throw new Error("Failed to encode ISMP message", { cause: error });
7826
8009
  }
7827
8010
  }
8011
+
8012
+ // src/utils/rateLimiter.ts
8013
+ var TokenBucket = class {
8014
+ /**
8015
+ * @param ratePerSecond - sustained requests per second.
8016
+ * @param burst - how many requests may go out back-to-back before pacing starts. Defaults to
8017
+ * one second's worth, which is the shape most limiters police.
8018
+ */
8019
+ constructor(ratePerSecond, burst = ratePerSecond) {
8020
+ this.ratePerSecond = ratePerSecond;
8021
+ this.burst = burst;
8022
+ if (ratePerSecond <= 0) throw new Error(`TokenBucket rate must be positive, got ${ratePerSecond}`);
8023
+ this.tokens = burst;
8024
+ this.lastRefill = Date.now();
8025
+ }
8026
+ ratePerSecond;
8027
+ burst;
8028
+ /** Fractional on purpose: a partial token is real capacity, just not yet a whole request. */
8029
+ tokens;
8030
+ lastRefill;
8031
+ waiting = [];
8032
+ drainTimer = null;
8033
+ /** Resolves once this caller may send. */
8034
+ async acquire() {
8035
+ if (this.waiting.length === 0 && this.take()) return;
8036
+ return new Promise((resolve) => {
8037
+ this.waiting.push(resolve);
8038
+ this.scheduleDrain();
8039
+ });
8040
+ }
8041
+ /** Requests currently waiting on a token. Exposed for tests and diagnostics. */
8042
+ get queued() {
8043
+ return this.waiting.length;
8044
+ }
8045
+ refill() {
8046
+ const now = Date.now();
8047
+ const elapsed = now - this.lastRefill;
8048
+ if (elapsed <= 0) {
8049
+ if (elapsed < 0) this.lastRefill = now;
8050
+ return;
8051
+ }
8052
+ this.tokens = Math.min(this.burst, this.tokens + elapsed * this.ratePerSecond / 1e3);
8053
+ this.lastRefill = now;
8054
+ }
8055
+ take() {
8056
+ this.refill();
8057
+ if (this.tokens < 1) return false;
8058
+ this.tokens -= 1;
8059
+ return true;
8060
+ }
8061
+ scheduleDrain() {
8062
+ if (this.drainTimer) return;
8063
+ this.refill();
8064
+ const deficit = 1 - this.tokens;
8065
+ const waitMs = deficit <= 0 ? 0 : Math.ceil(deficit * 1e3 / this.ratePerSecond);
8066
+ const timer = setTimeout(() => {
8067
+ this.drainTimer = null;
8068
+ this.drain();
8069
+ }, waitMs);
8070
+ timer.unref?.();
8071
+ this.drainTimer = timer;
8072
+ }
8073
+ drain() {
8074
+ while (this.waiting.length > 0 && this.take()) {
8075
+ this.waiting.shift()?.();
8076
+ }
8077
+ if (this.waiting.length > 0) this.scheduleDrain();
8078
+ }
8079
+ };
8080
+ var BATCHES_NOT_SUPPORTED_CODE = -32005;
8081
+ var TOO_BIG_BATCH_REQUEST_CODE = -32010;
8082
+ var DEFAULT_MAX_BATCH_SIZE = 32;
8083
+ function rpcError({ code, message, data }) {
8084
+ const suffix = data === void 0 ? "" : `: ${typeof data === "string" ? data : JSON.stringify(data)}`;
8085
+ const error = new Error(`${code}: ${message}${suffix}`);
8086
+ error.code = code;
8087
+ error.data = data;
8088
+ return error;
8089
+ }
8090
+ var BatchingHttpProvider = class _BatchingHttpProvider extends api.HttpProvider {
8091
+ #endpoint;
8092
+ #headers;
8093
+ #limiter;
8094
+ #maxBatchSize;
8095
+ #batchingSupported = true;
8096
+ #pending = [];
8097
+ #flushTimer = null;
8098
+ #flushing = false;
8099
+ #nextId = 1;
8100
+ constructor(endpoint, headers, limiter, maxBatchSize = DEFAULT_MAX_BATCH_SIZE) {
8101
+ super(endpoint, headers, 0);
8102
+ this.#endpoint = endpoint;
8103
+ this.#headers = headers;
8104
+ this.#limiter = limiter;
8105
+ this.#maxBatchSize = maxBatchSize;
8106
+ }
8107
+ /**
8108
+ * `isCacheable` is accepted for interface compatibility and ignored: this provider has no
8109
+ * response cache, which is deliberate — see the note in `IntentsCoprocessor.http`.
8110
+ */
8111
+ async send(method, params, _isCacheable) {
8112
+ return new Promise((resolve, reject) => {
8113
+ this.#pending.push({
8114
+ id: this.#nextId++,
8115
+ method,
8116
+ params,
8117
+ resolve,
8118
+ reject
8119
+ });
8120
+ if (this.#pending.length >= this.#maxBatchSize) this.#flushNow();
8121
+ else this.#scheduleFlush();
8122
+ });
8123
+ }
8124
+ clone() {
8125
+ return new _BatchingHttpProvider(this.#endpoint, this.#headers, this.#limiter, this.#maxBatchSize);
8126
+ }
8127
+ /** Calls waiting for a flush. Exposed for tests and diagnostics. */
8128
+ get queued() {
8129
+ return this.#pending.length;
8130
+ }
8131
+ /**
8132
+ * Collect for the rest of this macrotask, then send.
8133
+ *
8134
+ * A macrotask rather than a microtask because the bursts worth catching are not synchronous:
8135
+ * `Promise.all` over a set of reads starts them in one synchronous run, but each then advances
8136
+ * through several microtask turns of its own before reaching the provider. A microtask flush
8137
+ * would fire between those turns and split one burst across several requests.
8138
+ */
8139
+ #scheduleFlush() {
8140
+ if (this.#flushTimer || this.#flushing) return;
8141
+ const timer = setTimeout(() => {
8142
+ this.#flushTimer = null;
8143
+ void this.#flush();
8144
+ }, 0);
8145
+ timer.unref?.();
8146
+ this.#flushTimer = timer;
8147
+ }
8148
+ #flushNow() {
8149
+ if (this.#flushing) return;
8150
+ if (this.#flushTimer) {
8151
+ clearTimeout(this.#flushTimer);
8152
+ this.#flushTimer = null;
8153
+ }
8154
+ void this.#flush();
8155
+ }
8156
+ async #flush() {
8157
+ if (this.#flushing) return;
8158
+ const calls = this.#pending.splice(0, this.#maxBatchSize);
8159
+ if (calls.length === 0) return;
8160
+ this.#flushing = true;
8161
+ try {
8162
+ await this.#limiter.acquire();
8163
+ await this.#post(calls);
8164
+ } finally {
8165
+ this.#flushing = false;
8166
+ if (this.#pending.length > 0) this.#scheduleFlush();
8167
+ }
8168
+ }
8169
+ async #post(calls) {
8170
+ const single = calls.length === 1;
8171
+ const payload = calls.map(({ id, method, params }) => ({ id, jsonrpc: "2.0", method, params }));
8172
+ const body = JSON.stringify(single ? payload[0] : payload);
8173
+ let parsed;
8174
+ try {
8175
+ const response = await fetch(this.#endpoint, {
8176
+ body,
8177
+ headers: {
8178
+ Accept: "application/json",
8179
+ "Content-Type": "application/json",
8180
+ ...this.#headers
8181
+ },
8182
+ method: "POST"
8183
+ });
8184
+ if (!response.ok) throw new Error(`[${response.status}]: ${response.statusText}`);
8185
+ parsed = JSON.parse(await response.text());
8186
+ } catch (err) {
8187
+ const error = err instanceof Error ? err : new Error(String(err));
8188
+ error.message = `${error.message}
8189
+ Failed HTTP Request: ${JSON.stringify(
8190
+ calls.map(({ method, params }) => ({ method, params }))
8191
+ )}`;
8192
+ for (const call of calls) call.reject(error);
8193
+ return;
8194
+ }
8195
+ if (Array.isArray(parsed)) {
8196
+ this.#settleBatch(calls, parsed);
8197
+ return;
8198
+ }
8199
+ if (!single) {
8200
+ this.#handleBatchRefusal(calls, parsed);
8201
+ return;
8202
+ }
8203
+ this.#settle(calls[0], parsed);
8204
+ }
8205
+ #settleBatch(calls, responses) {
8206
+ const byId = /* @__PURE__ */ new Map();
8207
+ for (const response of responses) {
8208
+ if (typeof response?.id === "number") byId.set(response.id, response);
8209
+ }
8210
+ for (const call of calls) {
8211
+ const response = byId.get(call.id);
8212
+ if (response) this.#settle(call, response);
8213
+ else call.reject(new Error(`No response for ${call.method} in batch reply`));
8214
+ }
8215
+ }
8216
+ #settle(call, response) {
8217
+ if (response?.error) {
8218
+ call.reject(rpcError(response.error));
8219
+ return;
8220
+ }
8221
+ if (!response || response.result === void 0) {
8222
+ call.reject(new Error("No result found in jsonrpc response"));
8223
+ return;
8224
+ }
8225
+ call.resolve(response.result);
8226
+ }
8227
+ /**
8228
+ * The server rejected the batch itself rather than any call in it. Both forms are recoverable
8229
+ * without losing a call, and neither should ever surface to a caller as a failure.
8230
+ */
8231
+ #handleBatchRefusal(calls, response) {
8232
+ const code = response?.error?.code;
8233
+ if (code === BATCHES_NOT_SUPPORTED_CODE) {
8234
+ this.#batchingSupported = false;
8235
+ this.#maxBatchSize = 1;
8236
+ this.#requeue(calls);
8237
+ return;
8238
+ }
8239
+ if (code === TOO_BIG_BATCH_REQUEST_CODE) {
8240
+ this.#maxBatchSize = Math.max(1, Math.floor(this.#maxBatchSize / 2));
8241
+ this.#requeue(calls);
8242
+ return;
8243
+ }
8244
+ const error = response?.error ? rpcError(response.error) : new Error("Malformed batch reply: neither an array nor an error");
8245
+ for (const call of calls) call.reject(error);
8246
+ }
8247
+ /** Puts calls back at the head of the queue, so a refused batch keeps its place in line. */
8248
+ #requeue(calls) {
8249
+ this.#pending.unshift(...calls);
8250
+ this.#scheduleFlush();
8251
+ }
8252
+ /** Whether batches are still being attempted. Exposed for tests and diagnostics. */
8253
+ get batchingSupported() {
8254
+ return this.#batchingSupported;
8255
+ }
8256
+ };
8257
+
8258
+ // src/chains/intentsCoprocessor.ts
8259
+ var SYSTEM_EVENTS_KEY = util.u8aToHex(util.u8aConcat(utilCrypto.xxhashAsU8a("System", 128), utilCrypto.xxhashAsU8a("Events", 128)));
7828
8260
  var OFFCHAIN_BID_PREFIX = new TextEncoder().encode("intents::bid::");
7829
8261
  var OFFCHAIN_PHANTOM_PREFIX = new TextEncoder().encode("intents::phantom::order::");
7830
8262
  var HYPERBRIDGE_TYPES_BUNDLE = {
@@ -7838,6 +8270,63 @@ var HTTP_CONNECT_TIMEOUT_MS = 2e4;
7838
8270
  var INCLUSION_TIMEOUT_MS = 2e4;
7839
8271
  var PHANTOM_POLL_INTERVAL_MS = 15e3;
7840
8272
  var GARGANTUA_PHANTOM_POLL_INTERVAL_MS = 6e3;
8273
+ var DEFAULT_RPC_MAX_RPS = 8;
8274
+ var DEFAULT_MAX_BLOCKS_PER_POLL = 10;
8275
+ var MAX_RATE_LIMIT_BACKOFF_TICKS = 8;
8276
+ var rpcLimiters = /* @__PURE__ */ new Map();
8277
+ function limiterFor(httpUrl) {
8278
+ const key = new URL(httpUrl).origin;
8279
+ let limiter = rpcLimiters.get(key);
8280
+ if (!limiter) {
8281
+ limiter = new TokenBucket(configuredRpcMaxRps());
8282
+ rpcLimiters.set(key, limiter);
8283
+ }
8284
+ return limiter;
8285
+ }
8286
+ function configuredRpcMaxRps() {
8287
+ const raw = typeof process !== "undefined" ? process.env?.HYPERBRIDGE_RPC_MAX_RPS : void 0;
8288
+ const parsed = raw === void 0 ? Number.NaN : Number(raw);
8289
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_RPC_MAX_RPS;
8290
+ }
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
+ }
7841
8330
  function rejectAfter(ms, message) {
7842
8331
  return new Promise((_resolve, reject) => {
7843
8332
  const timer = setTimeout(() => reject(new Error(message)), ms);
@@ -7909,8 +8398,14 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
7909
8398
  ownsConnection;
7910
8399
  /** Cached result of whether the node exposes intents_* RPC methods */
7911
8400
  hasIntentsRpc = null;
8401
+ /** The pallet's phantom timings, read once. Cleared on failure so the read retries. */
8402
+ phantomTimingsRead = null;
7912
8403
  /** The HTTP-backed api, connected on first use. Cleared after a failed attempt so it retries. */
7913
8404
  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;
7914
8409
  // Serialises every extrinsic submission on this instance's substrate account. All submit/retract
7915
8410
  // methods funnel through signAndSendExtrinsic, each using the API's auto-nonce; fired in parallel
7916
8411
  // (bids for orders on different chains, or several phantom orders in one interval) they would grab
@@ -8010,7 +8505,11 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8010
8505
  // replayed from memory on every tick, faster than the TTL could lapse, and the node never
8011
8506
  // saw a second request. The cache bought nothing here anyway: the poll reads each block
8012
8507
  // once, and `api.at(hash)` reuses registries at the api layer regardless.
8013
- provider: new api.HttpProvider(httpUrl, {}, 0),
8508
+ // Concurrent calls are coalesced into one JSON-RPC batch request, and every request
8509
+ // to this endpoint is paced by a bucket shared with any other coprocessor in this
8510
+ // process pointed at the same host — the limit is the server's, and it counts
8511
+ // requests per address rather than per connection.
8512
+ provider: new BatchingHttpProvider(httpUrl, {}, limiterFor(httpUrl)),
8014
8513
  typesBundle: HYPERBRIDGE_TYPES_BUNDLE,
8015
8514
  // A second connection to the node the ws api already reported on; its init warnings
8016
8515
  // would just be duplicates.
@@ -8578,29 +9077,77 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8578
9077
  }
8579
9078
  /**
8580
9079
  * Reads the PhantomOrderRegistered events emitted in a single block.
9080
+ *
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.
9089
+ *
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.
8581
9093
  */
8582
- async getPhantomOrdersInBlock(blockNumber) {
9094
+ async getPhantomOrdersInBlock(blockNumber, knownVersion) {
8583
9095
  const api = await this.http();
8584
9096
  const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
8585
- const apiAt = await api.at(blockHash);
8586
- const records = await apiAt.query.system.events();
8587
- const orders = [];
8588
- for (const { event } of records) {
8589
- if (event.section !== "intentsCoprocessor" || event.method !== "PhantomOrderRegistered") continue;
8590
- const [commitment, chain, createdAt, legs] = event.data;
8591
- orders.push({
8592
- commitment: commitment.toHex(),
8593
- chain: new TextDecoder().decode(util.hexToU8a(chain.toHex())),
8594
- createdAt: createdAt.toNumber(),
8595
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
8596
- legs: legs.map((leg) => ({
8597
- tokenA: leg.tokenA.toHex(),
8598
- tokenB: leg.tokenB.toHex(),
8599
- standardAmount: BigInt(leg.standardAmount.toString())
8600
- }))
8601
- });
8602
- }
8603
- return orders;
9097
+ return await this.getPhantomOrdersAtHash(blockHash.toHex(), knownVersion);
9098
+ }
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
+ });
8604
9151
  }
8605
9152
  /**
8606
9153
  * Polls for newly registered phantom orders, invoking the callback once per block that carries
@@ -8628,30 +9175,85 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8628
9175
  * socket that looks alive while delivering nothing. It also means a websocket outage does not
8629
9176
  * pause phantom bidding at all — the two transports fail independently.
8630
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
+ *
8631
9191
  * Returns a function that stops polling.
8632
9192
  */
8633
9193
  pollPhantomOrders(callback, options = {}) {
8634
- const { intervalMs, maxBlocksPerPoll = 500, lookbackBlocks = 0, onError } = options;
9194
+ const {
9195
+ intervalMs,
9196
+ maxBlocksPerPoll = DEFAULT_MAX_BLOCKS_PER_POLL,
9197
+ onError,
9198
+ onSkip
9199
+ } = options;
8635
9200
  let cursor = null;
8636
9201
  let inFlight = false;
8637
9202
  let stopped = false;
9203
+ let backoffTicks = 0;
9204
+ let backoffLength = 0;
8638
9205
  const tick = async () => {
8639
9206
  if (inFlight || stopped) return;
9207
+ if (backoffTicks > 0) {
9208
+ backoffTicks -= 1;
9209
+ return;
9210
+ }
8640
9211
  inFlight = true;
8641
9212
  try {
8642
- const head = (await (await this.http()).rpc.chain.getHeader()).number.toNumber();
9213
+ const api = await this.http();
9214
+ const head = (await api.rpc.chain.getHeader()).number.toNumber();
8643
9215
  if (cursor === null) {
8644
- cursor = Math.max(head - 1 - lookbackBlocks, -1);
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
+ }
8645
9224
  }
8646
9225
  if (head <= cursor) return;
9226
+ const knownVersion = await this.confirmedRuntimeVersion();
8647
9227
  const to = Math.min(head, cursor + maxBlocksPerPoll);
8648
- for (let blockNumber = cursor + 1; blockNumber <= to; blockNumber++) {
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++) {
8649
9244
  if (stopped) return;
8650
- const orders = await this.getPhantomOrdersInBlock(blockNumber);
9245
+ const hash = hashes[index];
9246
+ if (hash.status === "rejected") throw hash.reason;
9247
+ const orders = await this.getPhantomOrdersAtHash(hash.value.toHex(), knownVersion);
8651
9248
  if (orders.length > 0) callback(orders);
8652
- cursor = blockNumber;
9249
+ cursor = numbers[index];
8653
9250
  }
9251
+ backoffLength = 0;
8654
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
+ }
8655
9257
  onError?.(err);
8656
9258
  } finally {
8657
9259
  inFlight = false;
@@ -8670,6 +9272,125 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
8670
9272
  if (timer) clearInterval(timer);
8671
9273
  };
8672
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
+ }
8673
9394
  /**
8674
9395
  * The poll cadence for the runtime this instance is connected to: Gargantua polls every block,
8675
9396
  * everything else every 15s. Falls back to the slower cadence if the runtime cannot be read,
@@ -17047,6 +17768,115 @@ var OrderCanceller = class _OrderCanceller {
17047
17768
  return feeInDestFeeToken * 1005n / 1000n;
17048
17769
  }
17049
17770
  };
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],
17779
+ {
17780
+ name: "options",
17781
+ type: "tuple",
17782
+ internalType: "struct FillOptions",
17783
+ 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
+ }
17795
+ ]
17796
+ }
17797
+ ]
17798
+ }
17799
+ ];
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();
17830
+ }
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;
17836
+ }
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;
17846
+ }
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
+ });
17861
+ }
17862
+ function decodeFillOrder(data) {
17863
+ try {
17864
+ 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
+ }
17868
+ } catch {
17869
+ }
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 {
17877
+ }
17878
+ return null;
17879
+ }
17050
17880
  var BidImpl = class {
17051
17881
  solverAddress;
17052
17882
  outputs;
@@ -17477,19 +18307,9 @@ var BidManager = class {
17477
18307
  const innerCalls = this.crypto.decodeERC7821Execute(bid.userOp.callData);
17478
18308
  if (!innerCalls || innerCalls.length === 0) return null;
17479
18309
  for (const call of innerCalls) {
17480
- try {
17481
- const decoded = viem.decodeFunctionData({
17482
- abi: ABI3,
17483
- data: call.data
17484
- });
17485
- if (decoded?.functionName === "fillOrder" && decoded.args && decoded.args.length >= 2) {
17486
- const fillOptions = decoded.args[1];
17487
- if (fillOptions?.outputs?.length > 0) {
17488
- return fillOptions;
17489
- }
17490
- }
17491
- } catch {
17492
- continue;
18310
+ const decoded = decodeFillOrder(call.data);
18311
+ if (decoded && decoded.options?.outputs?.length > 0) {
18312
+ return decoded.options;
17493
18313
  }
17494
18314
  }
17495
18315
  } catch {
@@ -17867,6 +18687,10 @@ var GasEstimator = class {
17867
18687
  relayerFee: crossChainFees.postRequestFee,
17868
18688
  // Always dispatch with the fee token (see the method docs).
17869
18689
  nativeDispatchFee: 0n,
18690
+ // Unbounded for estimation: this call is simulated, never submitted, and a real
18691
+ // bound here would only risk the estimate reverting on a slow bundler round trip.
18692
+ // The caller sets the real one on the options it actually signs.
18693
+ validUntil: 0n,
17870
18694
  outputs: order.output.assets.map((asset) => ({
17871
18695
  ...asset,
17872
18696
  token: normalizeAddressForEvmBytes32(asset.token)
@@ -17879,11 +18703,12 @@ var GasEstimator = class {
17879
18703
  let maxFeePerGas = gasPrice + gasPrice * BigInt(maxFeeBumpPercent) / 100n;
17880
18704
  const orderForEstimation = { ...order, session: solverAccountAddress };
17881
18705
  const commitment = orderCommitment(orderForEstimation);
17882
- const fillOrderCalldata = viem.encodeFunctionData({
17883
- abi: ABI3,
17884
- functionName: "fillOrder",
17885
- args: [transformOrderForContract(orderForEstimation), fillOptions]
17886
- });
18706
+ const fillOptionsVersion = await getFillOptionsVersion(this.ctx.dest.client, intentGatewayV2Address);
18707
+ const fillOrderCalldata = encodeFillOrder(
18708
+ transformOrderForContract(orderForEstimation),
18709
+ fillOptions,
18710
+ fillOptionsVersion
18711
+ );
17887
18712
  let callGasLimit = 500000n;
17888
18713
  let verificationGasLimit = 100000n;
17889
18714
  let preVerificationGas = 100000n;
@@ -18971,7 +19796,18 @@ function buildResult(tradeType, amountIn, amountOut, selectedRate, rates, protoc
18971
19796
  }
18972
19797
 
18973
19798
  // src/protocols/intents/IntentGateway.ts
18974
- var CROSS_CHAIN_ORDER_FEE_GAS_PRICE_BUMP_PERCENT = 10n;
19799
+ var ORDER_FEE_GAS_PRICE_BUMP_POLICY = {
19800
+ defaultPercent: 10n,
19801
+ bySourceStateMachineId: {
19802
+ ["EVM-1" /* MAINNET */]: 50n
19803
+ }
19804
+ };
19805
+ function resolveOrderFeeGasPriceBump(sourceStateMachineId, isSameChain) {
19806
+ if (isSameChain) {
19807
+ return 0n;
19808
+ }
19809
+ return ORDER_FEE_GAS_PRICE_BUMP_POLICY.bySourceStateMachineId[sourceStateMachineId] ?? ORDER_FEE_GAS_PRICE_BUMP_POLICY.defaultPercent;
19810
+ }
18975
19811
  var IntentGateway = class _IntentGateway {
18976
19812
  /** EVM chain on which orders are placed and escrowed. */
18977
19813
  source;
@@ -19190,8 +20026,9 @@ var IntentGateway = class _IntentGateway {
19190
20026
  * **Yield/receive protocol:**
19191
20027
  * 1. If `order.fees` is unset or zero, prices the fee on an internal copy
19192
20028
  * via {@link quoteOrderFees}: same-chain fees are twice the fill-gas
19193
- * estimate without a gas-price bump; cross-chain gas is priced 10% above
19194
- * the live price before attaching (fill gas + the settlement relayer fee)
20029
+ * estimate without a gas-price bump; cross-chain order fees originating on
20030
+ * Ethereum price gas 50% above the live price, while other source chains use
20031
+ * 10%, before attaching (fill gas + the settlement relayer fee)
19195
20032
  * with a further 5% buffer over the whole sum — strictly above the solver's
19196
20033
  * unpadded requirement. Direct solver estimates remain unbumped. The wei
19197
20034
  * cost used for the `value` field receives a 2% buffer.
@@ -19561,7 +20398,8 @@ var IntentGateway = class _IntentGateway {
19561
20398
  * transaction (check the native balance).
19562
20399
  *
19563
20400
  * @param order - The order to quote. `order.fees` is ignored and not mutated.
19564
- * Gas prices used to derive cross-chain `fees` receive 10% SDK-only headroom.
20401
+ * Gas prices used to derive cross-chain `fees` receive 50% SDK-only headroom
20402
+ * when the source chain is Ethereum mainnet and 10% for other source chains.
19565
20403
  * Same-chain quotes and direct calls to {@link estimateFillOrder}, including
19566
20404
  * Simplex solver estimates, remain unbumped.
19567
20405
  *
@@ -19572,15 +20410,14 @@ var IntentGateway = class _IntentGateway {
19572
20410
  */
19573
20411
  async quoteOrderFees(order, options) {
19574
20412
  const isSameChain = this.source.config.stateMachineId === this.dest.config.stateMachineId;
20413
+ const orderFeeGasPriceBumpPercent = resolveOrderFeeGasPriceBump(this.source.config.stateMachineId, isSameChain);
19575
20414
  const estimate = await this.gasEstimator.estimateFillOrder(
19576
20415
  {
19577
20416
  order,
19578
20417
  maxPriorityFeePerGasBumpPercent: options?.maxPriorityFeePerGasBumpPercent,
19579
20418
  maxFeePerGasBumpPercent: options?.maxFeePerGasBumpPercent
19580
20419
  },
19581
- {
19582
- orderFeeGasPriceBumpPercent: isSameChain ? 0n : CROSS_CHAIN_ORDER_FEE_GAS_PRICE_BUMP_PERCENT
19583
- }
20420
+ { orderFeeGasPriceBumpPercent }
19584
20421
  );
19585
20422
  if (estimate.totalGasCostWei === 0n || estimate.totalGasInFeeToken === 0n) {
19586
20423
  throw new Error("Gas estimation failed");
@@ -19809,9 +20646,16 @@ function encodeAcceptedSourceChains(chains2) {
19809
20646
  function decodeAcceptedSourceChains(paymasterAndData) {
19810
20647
  return decodePhantomBidDeclaration(paymasterAndData).acceptedSources;
19811
20648
  }
19812
- var UNISWAP_QUOTE_HAIRCUT_BPS = 30n;
20649
+ var UNISWAP_QUOTE_HAIRCUT_BPS = 10n;
20650
+ var PHANTOM_QUOTE_HAIRCUT_BPS = 5n;
20651
+ function haircut(amount, bps) {
20652
+ return amount * (10000n - bps) / 10000n;
20653
+ }
19813
20654
  function applyUniswapQuoteHaircut(amount) {
19814
- return amount * (10000n - UNISWAP_QUOTE_HAIRCUT_BPS) / 10000n;
20655
+ return haircut(amount, UNISWAP_QUOTE_HAIRCUT_BPS);
20656
+ }
20657
+ function applyPhantomQuoteHaircut(amount) {
20658
+ return haircut(amount, PHANTOM_QUOTE_HAIRCUT_BPS);
19815
20659
  }
19816
20660
  FILL_ORDER_ABI.find(
19817
20661
  (item) => item?.type === "function" && item?.name === "fillOrder"
@@ -24288,6 +25132,7 @@ async function teleportDot(param_) {
24288
25132
 
24289
25133
  exports.ADDRESS_ZERO = ADDRESS_ZERO2;
24290
25134
  exports.BundlerMethod = BundlerMethod;
25135
+ exports.CHAINS_WITHOUT_VALID_UNTIL = CHAINS_WITHOUT_VALID_UNTIL;
24291
25136
  exports.ChainConfigService = ChainConfigService;
24292
25137
  exports.Chains = Chains;
24293
25138
  exports.CryptoUtils = CryptoUtils;
@@ -24300,6 +25145,7 @@ exports.ERC7821_BATCH_MODE = ERC7821_BATCH_MODE;
24300
25145
  exports.EvmChain = EvmChain;
24301
25146
  exports.EvmHostABI = ABI;
24302
25147
  exports.EvmLanguage = EvmLanguage;
25148
+ exports.FILL_ORDER_V1_ABI = FILL_ORDER_V1_ABI;
24303
25149
  exports.HyperClientStatus = HyperClientStatus;
24304
25150
  exports.HyperFungibleToken = HyperFungibleToken;
24305
25151
  exports.HyperFungibleTokenABI = HyperFungibleTokenABI;
@@ -24313,11 +25159,13 @@ exports.InvalidIndexedRateError = InvalidIndexedRateError;
24313
25159
  exports.InvalidLiquidityIndexerResponseError = InvalidLiquidityIndexerResponseError;
24314
25160
  exports.InvalidPhantomSnapshotError = InvalidPhantomSnapshotError;
24315
25161
  exports.IsmpClient = IsmpClient;
25162
+ exports.LEGACY_FILL_OPTIONS_IMPLEMENTATIONS = LEGACY_FILL_OPTIONS_IMPLEMENTATIONS;
24316
25163
  exports.MOCK_ADDRESS = MOCK_ADDRESS;
24317
25164
  exports.ORDER_V2_PARAM_TYPE = ORDER_V2_PARAM_TYPE;
24318
25165
  exports.OrderStatus = OrderStatus;
24319
25166
  exports.OrderStatusChecker = OrderStatusChecker;
24320
25167
  exports.PACKED_USEROP_TYPEHASH = PACKED_USEROP_TYPEHASH;
25168
+ exports.PHANTOM_QUOTE_HAIRCUT_BPS = PHANTOM_QUOTE_HAIRCUT_BPS;
24321
25169
  exports.PLACE_ORDER_SELECTOR = PLACE_ORDER_SELECTOR;
24322
25170
  exports.PhantomSnapshotUnavailableError = PhantomSnapshotUnavailableError;
24323
25171
  exports.PharosChain = PharosChain;
@@ -24346,6 +25194,7 @@ exports.UnsupportedLiquidityChainError = UnsupportedLiquidityChainError;
24346
25194
  exports.WrappedHyperFungibleTokenABI = WrappedHyperFungibleTokenABI;
24347
25195
  exports.__test = __test;
24348
25196
  exports.adjustDecimals = adjustDecimals;
25197
+ exports.applyPhantomQuoteHaircut = applyPhantomQuoteHaircut;
24349
25198
  exports.applyUniswapQuoteHaircut = applyUniswapQuoteHaircut;
24350
25199
  exports.bytes20ToBytes32 = bytes20ToBytes32;
24351
25200
  exports.bytes32ToBytes20 = bytes32ToBytes20;
@@ -24365,11 +25214,13 @@ exports.createEvmChain = createEvmChain;
24365
25214
  exports.createQueryClient = createQueryClient;
24366
25215
  exports.decodeAcceptedSourceChains = decodeAcceptedSourceChains;
24367
25216
  exports.decodeERC7821ExecuteBatch = decodeERC7821ExecuteBatch;
25217
+ exports.decodeFillOrder = decodeFillOrder;
24368
25218
  exports.decodePhantomBidDeclaration = decodePhantomBidDeclaration;
24369
25219
  exports.decodeUserOpScale = decodeUserOpScale;
24370
25220
  exports.deriveHttpUrl = deriveHttpUrl;
24371
25221
  exports.encodeAcceptedSourceChains = encodeAcceptedSourceChains;
24372
25222
  exports.encodeERC7821ExecuteBatch = encodeERC7821ExecuteBatch;
25223
+ exports.encodeFillOrder = encodeFillOrder;
24373
25224
  exports.encodeISMPMessage = encodeISMPMessage;
24374
25225
  exports.encodePhantomBidDeclaration = encodePhantomBidDeclaration;
24375
25226
  exports.encodeStateMachineId = encodeStateMachineId;
@@ -24383,6 +25234,7 @@ exports.getChainId = getChainId;
24383
25234
  exports.getConfigByStateMachineId = getConfigByStateMachineId;
24384
25235
  exports.getContractCallInput = getContractCallInput;
24385
25236
  exports.getContractCallInputs = getContractCallInputs;
25237
+ exports.getFillOptionsVersion = getFillOptionsVersion;
24386
25238
  exports.getGasPriceFromEtherscan = getGasPriceFromEtherscan;
24387
25239
  exports.getOrFetchStorageSlot = getOrFetchStorageSlot;
24388
25240
  exports.getOrderPlacedFromTx = getOrderPlacedFromTx;
@@ -24414,6 +25266,7 @@ exports.queryGetRequest = queryGetRequest;
24414
25266
  exports.queryPostRequest = queryPostRequest;
24415
25267
  exports.quoteUniswap = quoteUniswap;
24416
25268
  exports.requestCommitmentKey = requestCommitmentKey;
25269
+ exports.resetFillOptionsVersionCache = resetFillOptionsVersionCache;
24417
25270
  exports.responseCommitmentKey = responseCommitmentKey;
24418
25271
  exports.retryPromise = retryPromise;
24419
25272
  exports.sortPoolSymbols = sortPoolSymbols;