@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.
- package/dist/browser/index.d.ts +839 -911
- package/dist/browser/index.js +1435 -2081
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +1455 -2099
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +492 -180
- package/dist/node/index.d.ts +492 -180
- package/dist/node/index.js +1435 -2081
- package/dist/node/index.js.map +1 -1
- package/dist/node/{intents-helpers-fntBuoYM.d.cts → intents-helpers-BHl2If1T.d.cts} +909 -2967
- package/dist/node/{intents-helpers-fntBuoYM.d.ts → intents-helpers-BHl2If1T.d.ts} +909 -2967
- package/dist/node/intents-helpers.cjs +371 -1215
- package/dist/node/intents-helpers.cjs.map +1 -1
- package/dist/node/intents-helpers.d.cts +1 -3
- package/dist/node/intents-helpers.d.ts +1 -3
- package/dist/node/intents-helpers.js +373 -1200
- package/dist/node/intents-helpers.js.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createConsola, LogLevels } from 'consola';
|
|
2
|
-
import { defineChain, keccak256, toHex, createPublicClient, http, hexToBytes, toFunctionSelector, bytesToHex, encodeFunctionData, erc20Abi, bytesToBigInt, pad, toBytes, numberToBytes,
|
|
2
|
+
import { defineChain, keccak256, toHex, parseAbi, createPublicClient, http, hexToBytes, toFunctionSelector, bytesToHex, encodeFunctionData, erc20Abi, bytesToBigInt, pad, toBytes, numberToBytes, getAddress, encodeAbiParameters, isHex, isAddress, maxUint256, parseAbiParameters, encodePacked, parseAbiItem, concat, decodeFunctionData, decodeAbiParameters, hexToString as hexToString$1, decodeEventLog, parseEventLogs, parseUnits, concatHex, formatUnits, toEventSelector } from 'viem';
|
|
3
3
|
import { baseSepolia, optimismSepolia, arbitrumSepolia, soneium, gnosis, optimism, polygonAmoy, polygon, base, arbitrum, bsc, mainnet, sepolia, gnosisChiado, bscTestnet, tron, unichain } from 'viem/chains';
|
|
4
|
-
import {
|
|
4
|
+
import { base58Decode, keccakAsU8a, decodeAddress, keccakAsHex, xxhashAsU8a, blake2AsU8a } from '@polkadot/util-crypto';
|
|
5
5
|
import { flatten, zip, capitalize, maxBy, isNil } from 'lodash-es';
|
|
6
6
|
import { match } from 'ts-pattern';
|
|
7
7
|
import { WsProvider, ApiPromise, Keyring, HttpProvider } from '@polkadot/api';
|
|
8
8
|
import { Struct, Vector, u8, Bytes, Enum, Tuple, _void, u64, u32, Option, bool, u128 } from 'scale-ts';
|
|
9
|
-
import {
|
|
10
|
-
import { u8aToHex, u8aConcat, hexToU8a, isHex as isHex$1 } from '@polkadot/util';
|
|
9
|
+
import { hexToU8a, u8aToHex, u8aConcat, isHex as isHex$1 } from '@polkadot/util';
|
|
11
10
|
import PQueue from 'p-queue';
|
|
11
|
+
import { TronWeb } from 'tronweb';
|
|
12
12
|
import { hasWindow, isNode, env } from 'std-env';
|
|
13
13
|
import mergeRace from '@async-generator/merge-race';
|
|
14
14
|
import { GraphQLClient } from 'graphql-request';
|
|
@@ -319,6 +319,7 @@ var OrderStatus = /* @__PURE__ */ ((OrderStatus2) => {
|
|
|
319
319
|
OrderStatus2["FILLED"] = "FILLED";
|
|
320
320
|
OrderStatus2["REDEEMED"] = "REDEEMED";
|
|
321
321
|
OrderStatus2["REFUNDED"] = "REFUNDED";
|
|
322
|
+
OrderStatus2["CANCELLED"] = "CANCELLED";
|
|
322
323
|
return OrderStatus2;
|
|
323
324
|
})(OrderStatus || {});
|
|
324
325
|
var TeleportStatus = /* @__PURE__ */ ((TeleportStatus2) => {
|
|
@@ -333,6 +334,8 @@ var RequestKind = /* @__PURE__ */ ((RequestKind2) => {
|
|
|
333
334
|
RequestKind2[RequestKind2["UpdateParams"] = 2] = "UpdateParams";
|
|
334
335
|
RequestKind2[RequestKind2["SweepDust"] = 3] = "SweepDust";
|
|
335
336
|
RequestKind2[RequestKind2["RefundEscrow"] = 4] = "RefundEscrow";
|
|
337
|
+
RequestKind2[RequestKind2["Execute"] = 5] = "Execute";
|
|
338
|
+
RequestKind2[RequestKind2["RedeemEscrowPartial"] = 6] = "RedeemEscrowPartial";
|
|
336
339
|
return RequestKind2;
|
|
337
340
|
})(RequestKind || {});
|
|
338
341
|
var IntentOrderStatus = Object.freeze({
|
|
@@ -345,6 +348,7 @@ var IntentOrderStatus = Object.freeze({
|
|
|
345
348
|
BID_SELECTED: "BID_SELECTED",
|
|
346
349
|
FILLED: "FILLED",
|
|
347
350
|
PARTIAL_FILL: "PARTIAL_FILL",
|
|
351
|
+
CANCELLED: "CANCELLED",
|
|
348
352
|
EXPIRED: "EXPIRED",
|
|
349
353
|
FAILED: "FAILED"
|
|
350
354
|
});
|
|
@@ -2527,7 +2531,12 @@ var ABI2 = [
|
|
|
2527
2531
|
];
|
|
2528
2532
|
var handlerV2_default = { ABI: ABI2 };
|
|
2529
2533
|
function tronAddress(base58) {
|
|
2530
|
-
|
|
2534
|
+
const decoded = base58Decode(base58);
|
|
2535
|
+
if (decoded.length !== 25 || decoded[0] !== 65) {
|
|
2536
|
+
throw new Error(`Not a Tron base58 address: ${base58}`);
|
|
2537
|
+
}
|
|
2538
|
+
const hex = Array.from(decoded.slice(1, 21), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
2539
|
+
return `0x${hex}`;
|
|
2531
2540
|
}
|
|
2532
2541
|
var Chains = /* @__PURE__ */ ((Chains2) => {
|
|
2533
2542
|
Chains2["BSC_CHAPEL"] = "EVM-97";
|
|
@@ -2614,11 +2623,13 @@ var chainConfigs = {
|
|
|
2614
2623
|
WETH: "0xae13d989dac2f0debff460ac112a837c89baa7cd",
|
|
2615
2624
|
DAI: "0x1938165569a5463327fb206be06d8d9253aa06b7",
|
|
2616
2625
|
USDC: "0xA801da100bF16D07F668F4A49E1f71fc54D05177",
|
|
2617
|
-
USDT: "0xc043f483373072f7f27420d6e7d7ad269c018e18"
|
|
2626
|
+
USDT: "0xc043f483373072f7f27420d6e7d7ad269c018e18",
|
|
2627
|
+
cNGN: "0x2bbbd701cfc25d37f18127e51df0933566d5778a"
|
|
2618
2628
|
},
|
|
2619
2629
|
tokenDecimals: {
|
|
2620
2630
|
USDC: 18,
|
|
2621
|
-
USDT: 18
|
|
2631
|
+
USDT: 18,
|
|
2632
|
+
cNGN: 6
|
|
2622
2633
|
},
|
|
2623
2634
|
tokenStorageSlots: {
|
|
2624
2635
|
USDC: { balanceSlot: 1, allowanceSlot: 2 },
|
|
@@ -2635,7 +2646,7 @@ var chainConfigs = {
|
|
|
2635
2646
|
UniswapV3Quoter: "0x0000000000000000000000000000000000000000",
|
|
2636
2647
|
UniswapV4Quoter: "0x0000000000000000000000000000000000000000",
|
|
2637
2648
|
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
2638
|
-
SolverAccount: "
|
|
2649
|
+
SolverAccount: "0x153DB990FE3b761B54ad71D0f1A0987dF11740DB"
|
|
2639
2650
|
},
|
|
2640
2651
|
rpcEnvKey: "BSC_CHAPEL",
|
|
2641
2652
|
defaultRpcUrl: "https://bnb-testnet.api.onfinality.io/public",
|
|
@@ -2761,7 +2772,7 @@ var chainConfigs = {
|
|
|
2761
2772
|
},
|
|
2762
2773
|
addresses: {
|
|
2763
2774
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2764
|
-
SolverAccount: "
|
|
2775
|
+
SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
|
|
2765
2776
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
2766
2777
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2767
2778
|
UniswapRouter02: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
|
|
@@ -2828,7 +2839,7 @@ var chainConfigs = {
|
|
|
2828
2839
|
},
|
|
2829
2840
|
addresses: {
|
|
2830
2841
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2831
|
-
SolverAccount: "
|
|
2842
|
+
SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
|
|
2832
2843
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
2833
2844
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2834
2845
|
UniswapRouter02: "0x10ED43C718714eb63d5aA57B78B54704E256024E",
|
|
@@ -2889,7 +2900,7 @@ var chainConfigs = {
|
|
|
2889
2900
|
},
|
|
2890
2901
|
addresses: {
|
|
2891
2902
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2892
|
-
SolverAccount: "
|
|
2903
|
+
SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
|
|
2893
2904
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
2894
2905
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2895
2906
|
UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
|
|
@@ -2963,7 +2974,7 @@ var chainConfigs = {
|
|
|
2963
2974
|
},
|
|
2964
2975
|
addresses: {
|
|
2965
2976
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2966
|
-
SolverAccount: "
|
|
2977
|
+
SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
|
|
2967
2978
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
2968
2979
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2969
2980
|
UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
|
|
@@ -2988,7 +2999,6 @@ var chainConfigs = {
|
|
|
2988
2999
|
consensusStateId: "ETH0",
|
|
2989
3000
|
coingeckoId: "base",
|
|
2990
3001
|
layerZeroEid: 30184,
|
|
2991
|
-
uniswapV4Pools: [{ tokens: ["USDC", "cNGN"], fee: 1500, tickSpacing: 30 }],
|
|
2992
3002
|
erc4626Vaults: [
|
|
2993
3003
|
{ label: "Aave stataUSDC", address: "0xC768c589647798a6EE01A91FdE98EF2ed046DBD6", asset: "USDC" },
|
|
2994
3004
|
{ label: "Yield Bearing cNGN", address: "0xa82A3531021317240Fb32E67f9c7bC091F737D3b", asset: "cNGN" }
|
|
@@ -3038,7 +3048,7 @@ var chainConfigs = {
|
|
|
3038
3048
|
},
|
|
3039
3049
|
addresses: {
|
|
3040
3050
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
3041
|
-
SolverAccount: "
|
|
3051
|
+
SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
|
|
3042
3052
|
TokenGateway: "0x8b536105b6Fae2aE9199f5146D3C57Dfe53b614E",
|
|
3043
3053
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
3044
3054
|
UniswapRouter02: "0xd2f9496824951D5237cC71245D659E48d0d5f9E8",
|
|
@@ -3113,11 +3123,13 @@ var chainConfigs = {
|
|
|
3113
3123
|
//wmatic, change it to wpol
|
|
3114
3124
|
DAI: "0x0000000000000000000000000000000000000000",
|
|
3115
3125
|
USDC: "0xBE97E73126D66188d72fbF99029126D0340a7f18",
|
|
3116
|
-
USDT: "0x0000000000000000000000000000000000000000"
|
|
3126
|
+
USDT: "0x0000000000000000000000000000000000000000",
|
|
3127
|
+
cNGN: "0xe4ff5d2ae65c10f530c000215029919961e8a758"
|
|
3117
3128
|
},
|
|
3118
3129
|
tokenDecimals: {
|
|
3119
3130
|
USDC: 18,
|
|
3120
|
-
USDT: 18
|
|
3131
|
+
USDT: 18,
|
|
3132
|
+
cNGN: 6
|
|
3121
3133
|
},
|
|
3122
3134
|
tokenStorageSlots: {
|
|
3123
3135
|
USDT: { balanceSlot: 0, allowanceSlot: 1 },
|
|
@@ -3130,7 +3142,7 @@ var chainConfigs = {
|
|
|
3130
3142
|
Calldispatcher: "0x876F1891982E260026630c233A4897160A281Fb8",
|
|
3131
3143
|
Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
3132
3144
|
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
3133
|
-
SolverAccount: "
|
|
3145
|
+
SolverAccount: "0x153DB990FE3b761B54ad71D0f1A0987dF11740DB"
|
|
3134
3146
|
},
|
|
3135
3147
|
rpcEnvKey: "POLYGON_AMOY",
|
|
3136
3148
|
defaultRpcUrl: "https://rpc-amoy.polygon.technology",
|
|
@@ -3154,7 +3166,7 @@ var chainConfigs = {
|
|
|
3154
3166
|
},
|
|
3155
3167
|
addresses: {
|
|
3156
3168
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
3157
|
-
SolverAccount: "
|
|
3169
|
+
SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
|
|
3158
3170
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
3159
3171
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
3160
3172
|
UniswapRouter02: "0x4A7b5Da61326A6379179b40d00F57E5bbDC962c2",
|
|
@@ -3189,7 +3201,7 @@ var chainConfigs = {
|
|
|
3189
3201
|
},
|
|
3190
3202
|
addresses: {
|
|
3191
3203
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
3192
|
-
SolverAccount: "
|
|
3204
|
+
SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
|
|
3193
3205
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
3194
3206
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
3195
3207
|
UniswapRouter02: "0xB2e26652e4BAd1e56055A051f922E06760cA0BFE",
|
|
@@ -3345,7 +3357,7 @@ var chainConfigs = {
|
|
|
3345
3357
|
},
|
|
3346
3358
|
addresses: {
|
|
3347
3359
|
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
3348
|
-
SolverAccount: "
|
|
3360
|
+
SolverAccount: "0xd5535d4DeB17F050e52B6efda2fDe00435f39279",
|
|
3349
3361
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
3350
3362
|
Calldispatcher: "0xE2C7e576E26E0bE7aC97c6fE925bcDAbD87c4bEd"
|
|
3351
3363
|
},
|
|
@@ -3392,7 +3404,12 @@ var ABI3 = [
|
|
|
3392
3404
|
type: "constructor",
|
|
3393
3405
|
inputs: [
|
|
3394
3406
|
{
|
|
3395
|
-
name: "
|
|
3407
|
+
name: "intrinsic",
|
|
3408
|
+
type: "address",
|
|
3409
|
+
internalType: "address"
|
|
3410
|
+
},
|
|
3411
|
+
{
|
|
3412
|
+
name: "extrinsic",
|
|
3396
3413
|
type: "address",
|
|
3397
3414
|
internalType: "address"
|
|
3398
3415
|
}
|
|
@@ -3491,8 +3508,8 @@ var ABI3 = [
|
|
|
3491
3508
|
},
|
|
3492
3509
|
{
|
|
3493
3510
|
name: "",
|
|
3494
|
-
type: "
|
|
3495
|
-
internalType: "
|
|
3511
|
+
type: "uint256",
|
|
3512
|
+
internalType: "uint256"
|
|
3496
3513
|
}
|
|
3497
3514
|
],
|
|
3498
3515
|
outputs: [
|
|
@@ -3506,20 +3523,31 @@ var ABI3 = [
|
|
|
3506
3523
|
},
|
|
3507
3524
|
{
|
|
3508
3525
|
type: "function",
|
|
3509
|
-
name: "
|
|
3510
|
-
inputs: [
|
|
3526
|
+
name: "_partialFills",
|
|
3527
|
+
inputs: [
|
|
3528
|
+
{
|
|
3529
|
+
name: "",
|
|
3530
|
+
type: "bytes32",
|
|
3531
|
+
internalType: "bytes32"
|
|
3532
|
+
},
|
|
3533
|
+
{
|
|
3534
|
+
name: "",
|
|
3535
|
+
type: "uint256",
|
|
3536
|
+
internalType: "uint256"
|
|
3537
|
+
}
|
|
3538
|
+
],
|
|
3511
3539
|
outputs: [
|
|
3512
3540
|
{
|
|
3513
3541
|
name: "",
|
|
3514
|
-
type: "
|
|
3515
|
-
internalType: "
|
|
3542
|
+
type: "uint256",
|
|
3543
|
+
internalType: "uint256"
|
|
3516
3544
|
}
|
|
3517
3545
|
],
|
|
3518
3546
|
stateMutability: "view"
|
|
3519
3547
|
},
|
|
3520
3548
|
{
|
|
3521
3549
|
type: "function",
|
|
3522
|
-
name: "
|
|
3550
|
+
name: "_protocolFees",
|
|
3523
3551
|
inputs: [
|
|
3524
3552
|
{
|
|
3525
3553
|
name: "",
|
|
@@ -3528,19 +3556,31 @@ var ABI3 = [
|
|
|
3528
3556
|
},
|
|
3529
3557
|
{
|
|
3530
3558
|
name: "",
|
|
3531
|
-
type: "
|
|
3532
|
-
internalType: "
|
|
3559
|
+
type: "uint256",
|
|
3560
|
+
internalType: "uint256"
|
|
3533
3561
|
}
|
|
3534
3562
|
],
|
|
3535
3563
|
outputs: [
|
|
3536
3564
|
{
|
|
3537
|
-
name: "",
|
|
3565
|
+
name: "amount",
|
|
3566
|
+
type: "uint256",
|
|
3567
|
+
internalType: "uint256"
|
|
3568
|
+
},
|
|
3569
|
+
{
|
|
3570
|
+
name: "committed",
|
|
3538
3571
|
type: "uint256",
|
|
3539
3572
|
internalType: "uint256"
|
|
3540
3573
|
}
|
|
3541
3574
|
],
|
|
3542
3575
|
stateMutability: "view"
|
|
3543
3576
|
},
|
|
3577
|
+
{
|
|
3578
|
+
type: "function",
|
|
3579
|
+
name: "acceptOwnership",
|
|
3580
|
+
inputs: [],
|
|
3581
|
+
outputs: [],
|
|
3582
|
+
stateMutability: "nonpayable"
|
|
3583
|
+
},
|
|
3544
3584
|
{
|
|
3545
3585
|
type: "function",
|
|
3546
3586
|
name: "calculateCommitmentSlotHash",
|
|
@@ -3750,6 +3790,19 @@ var ABI3 = [
|
|
|
3750
3790
|
],
|
|
3751
3791
|
stateMutability: "view"
|
|
3752
3792
|
},
|
|
3793
|
+
{
|
|
3794
|
+
type: "function",
|
|
3795
|
+
name: "extrinsicModule",
|
|
3796
|
+
inputs: [],
|
|
3797
|
+
outputs: [
|
|
3798
|
+
{
|
|
3799
|
+
name: "",
|
|
3800
|
+
type: "address",
|
|
3801
|
+
internalType: "address"
|
|
3802
|
+
}
|
|
3803
|
+
],
|
|
3804
|
+
stateMutability: "view"
|
|
3805
|
+
},
|
|
3753
3806
|
{
|
|
3754
3807
|
type: "function",
|
|
3755
3808
|
name: "fillOrder",
|
|
@@ -3912,6 +3965,23 @@ var ABI3 = [
|
|
|
3912
3965
|
internalType: "uint256"
|
|
3913
3966
|
}
|
|
3914
3967
|
]
|
|
3968
|
+
},
|
|
3969
|
+
{
|
|
3970
|
+
name: "inputs",
|
|
3971
|
+
type: "tuple[]",
|
|
3972
|
+
internalType: "struct TokenInfo[]",
|
|
3973
|
+
components: [
|
|
3974
|
+
{
|
|
3975
|
+
name: "token",
|
|
3976
|
+
type: "bytes32",
|
|
3977
|
+
internalType: "bytes32"
|
|
3978
|
+
},
|
|
3979
|
+
{
|
|
3980
|
+
name: "amount",
|
|
3981
|
+
type: "uint256",
|
|
3982
|
+
internalType: "uint256"
|
|
3983
|
+
}
|
|
3984
|
+
]
|
|
3915
3985
|
}
|
|
3916
3986
|
]
|
|
3917
3987
|
}
|
|
@@ -3937,51 +4007,63 @@ var ABI3 = [
|
|
|
3937
4007
|
name: "initialize",
|
|
3938
4008
|
inputs: [
|
|
3939
4009
|
{
|
|
3940
|
-
name: "
|
|
4010
|
+
name: "init",
|
|
3941
4011
|
type: "tuple",
|
|
3942
|
-
internalType: "struct
|
|
4012
|
+
internalType: "struct InitParams",
|
|
3943
4013
|
components: [
|
|
3944
4014
|
{
|
|
3945
|
-
name: "
|
|
3946
|
-
type: "
|
|
3947
|
-
internalType: "
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
4015
|
+
name: "params",
|
|
4016
|
+
type: "tuple",
|
|
4017
|
+
internalType: "struct Params",
|
|
4018
|
+
components: [
|
|
4019
|
+
{
|
|
4020
|
+
name: "host",
|
|
4021
|
+
type: "address",
|
|
4022
|
+
internalType: "address"
|
|
4023
|
+
},
|
|
4024
|
+
{
|
|
4025
|
+
name: "dispatcher",
|
|
4026
|
+
type: "address",
|
|
4027
|
+
internalType: "address"
|
|
4028
|
+
},
|
|
4029
|
+
{
|
|
4030
|
+
name: "solverSelection",
|
|
4031
|
+
type: "bool",
|
|
4032
|
+
internalType: "bool"
|
|
4033
|
+
},
|
|
4034
|
+
{
|
|
4035
|
+
name: "surplusShareBps",
|
|
4036
|
+
type: "uint256",
|
|
4037
|
+
internalType: "uint256"
|
|
4038
|
+
},
|
|
4039
|
+
{
|
|
4040
|
+
name: "protocolFeeBps",
|
|
4041
|
+
type: "uint256",
|
|
4042
|
+
internalType: "uint256"
|
|
4043
|
+
},
|
|
4044
|
+
{
|
|
4045
|
+
name: "priceOracle",
|
|
4046
|
+
type: "address",
|
|
4047
|
+
internalType: "address"
|
|
4048
|
+
}
|
|
4049
|
+
]
|
|
3958
4050
|
},
|
|
3959
4051
|
{
|
|
3960
|
-
name: "
|
|
3961
|
-
type: "
|
|
3962
|
-
internalType: "
|
|
4052
|
+
name: "peerChains",
|
|
4053
|
+
type: "bytes[]",
|
|
4054
|
+
internalType: "bytes[]"
|
|
3963
4055
|
},
|
|
3964
4056
|
{
|
|
3965
|
-
name: "
|
|
3966
|
-
type: "
|
|
3967
|
-
internalType: "
|
|
4057
|
+
name: "relayer",
|
|
4058
|
+
type: "address",
|
|
4059
|
+
internalType: "address"
|
|
3968
4060
|
},
|
|
3969
4061
|
{
|
|
3970
|
-
name: "
|
|
4062
|
+
name: "owner",
|
|
3971
4063
|
type: "address",
|
|
3972
4064
|
internalType: "address"
|
|
3973
4065
|
}
|
|
3974
4066
|
]
|
|
3975
|
-
},
|
|
3976
|
-
{
|
|
3977
|
-
name: "peerChains",
|
|
3978
|
-
type: "bytes[]",
|
|
3979
|
-
internalType: "bytes[]"
|
|
3980
|
-
},
|
|
3981
|
-
{
|
|
3982
|
-
name: "relayer",
|
|
3983
|
-
type: "address",
|
|
3984
|
-
internalType: "address"
|
|
3985
4067
|
}
|
|
3986
4068
|
],
|
|
3987
4069
|
outputs: [],
|
|
@@ -4006,12 +4088,25 @@ var ABI3 = [
|
|
|
4006
4088
|
],
|
|
4007
4089
|
stateMutability: "view"
|
|
4008
4090
|
},
|
|
4091
|
+
{
|
|
4092
|
+
type: "function",
|
|
4093
|
+
name: "intrinsicModule",
|
|
4094
|
+
inputs: [],
|
|
4095
|
+
outputs: [
|
|
4096
|
+
{
|
|
4097
|
+
name: "",
|
|
4098
|
+
type: "address",
|
|
4099
|
+
internalType: "address"
|
|
4100
|
+
}
|
|
4101
|
+
],
|
|
4102
|
+
stateMutability: "view"
|
|
4103
|
+
},
|
|
4009
4104
|
{
|
|
4010
4105
|
type: "function",
|
|
4011
4106
|
name: "migrate",
|
|
4012
4107
|
inputs: [
|
|
4013
4108
|
{
|
|
4014
|
-
name: "
|
|
4109
|
+
name: "owner_",
|
|
4015
4110
|
type: "address",
|
|
4016
4111
|
internalType: "address"
|
|
4017
4112
|
}
|
|
@@ -4086,7 +4181,7 @@ var ABI3 = [
|
|
|
4086
4181
|
name: "onGetResponse",
|
|
4087
4182
|
inputs: [
|
|
4088
4183
|
{
|
|
4089
|
-
name: "
|
|
4184
|
+
name: "",
|
|
4090
4185
|
type: "tuple",
|
|
4091
4186
|
internalType: "struct IncomingGetResponse",
|
|
4092
4187
|
components: [
|
|
@@ -4301,6 +4396,19 @@ var ABI3 = [
|
|
|
4301
4396
|
outputs: [],
|
|
4302
4397
|
stateMutability: "nonpayable"
|
|
4303
4398
|
},
|
|
4399
|
+
{
|
|
4400
|
+
type: "function",
|
|
4401
|
+
name: "owner",
|
|
4402
|
+
inputs: [],
|
|
4403
|
+
outputs: [
|
|
4404
|
+
{
|
|
4405
|
+
name: "",
|
|
4406
|
+
type: "address",
|
|
4407
|
+
internalType: "address"
|
|
4408
|
+
}
|
|
4409
|
+
],
|
|
4410
|
+
stateMutability: "view"
|
|
4411
|
+
},
|
|
4304
4412
|
{
|
|
4305
4413
|
type: "function",
|
|
4306
4414
|
name: "params",
|
|
@@ -4346,6 +4454,39 @@ var ABI3 = [
|
|
|
4346
4454
|
],
|
|
4347
4455
|
stateMutability: "view"
|
|
4348
4456
|
},
|
|
4457
|
+
{
|
|
4458
|
+
type: "function",
|
|
4459
|
+
name: "pause",
|
|
4460
|
+
inputs: [],
|
|
4461
|
+
outputs: [],
|
|
4462
|
+
stateMutability: "nonpayable"
|
|
4463
|
+
},
|
|
4464
|
+
{
|
|
4465
|
+
type: "function",
|
|
4466
|
+
name: "paused",
|
|
4467
|
+
inputs: [],
|
|
4468
|
+
outputs: [
|
|
4469
|
+
{
|
|
4470
|
+
name: "",
|
|
4471
|
+
type: "bool",
|
|
4472
|
+
internalType: "bool"
|
|
4473
|
+
}
|
|
4474
|
+
],
|
|
4475
|
+
stateMutability: "view"
|
|
4476
|
+
},
|
|
4477
|
+
{
|
|
4478
|
+
type: "function",
|
|
4479
|
+
name: "pendingOwner",
|
|
4480
|
+
inputs: [],
|
|
4481
|
+
outputs: [
|
|
4482
|
+
{
|
|
4483
|
+
name: "",
|
|
4484
|
+
type: "address",
|
|
4485
|
+
internalType: "address"
|
|
4486
|
+
}
|
|
4487
|
+
],
|
|
4488
|
+
stateMutability: "view"
|
|
4489
|
+
},
|
|
4349
4490
|
{
|
|
4350
4491
|
type: "function",
|
|
4351
4492
|
name: "placeOrder",
|
|
@@ -4601,6 +4742,13 @@ var ABI3 = [
|
|
|
4601
4742
|
],
|
|
4602
4743
|
stateMutability: "view"
|
|
4603
4744
|
},
|
|
4745
|
+
{
|
|
4746
|
+
type: "function",
|
|
4747
|
+
name: "renounceOwnership",
|
|
4748
|
+
inputs: [],
|
|
4749
|
+
outputs: [],
|
|
4750
|
+
stateMutability: "nonpayable"
|
|
4751
|
+
},
|
|
4604
4752
|
{
|
|
4605
4753
|
type: "function",
|
|
4606
4754
|
name: "select",
|
|
@@ -4639,10 +4787,10 @@ var ABI3 = [
|
|
|
4639
4787
|
},
|
|
4640
4788
|
{
|
|
4641
4789
|
type: "function",
|
|
4642
|
-
name: "
|
|
4790
|
+
name: "transferOwnership",
|
|
4643
4791
|
inputs: [
|
|
4644
4792
|
{
|
|
4645
|
-
name: "
|
|
4793
|
+
name: "newOwner",
|
|
4646
4794
|
type: "address",
|
|
4647
4795
|
internalType: "address"
|
|
4648
4796
|
}
|
|
@@ -4652,19 +4800,8 @@ var ABI3 = [
|
|
|
4652
4800
|
},
|
|
4653
4801
|
{
|
|
4654
4802
|
type: "function",
|
|
4655
|
-
name: "
|
|
4656
|
-
inputs: [
|
|
4657
|
-
{
|
|
4658
|
-
name: "newImplementation",
|
|
4659
|
-
type: "address",
|
|
4660
|
-
internalType: "address"
|
|
4661
|
-
},
|
|
4662
|
-
{
|
|
4663
|
-
name: "data",
|
|
4664
|
-
type: "bytes",
|
|
4665
|
-
internalType: "bytes"
|
|
4666
|
-
}
|
|
4667
|
-
],
|
|
4803
|
+
name: "unpause",
|
|
4804
|
+
inputs: [],
|
|
4668
4805
|
outputs: [],
|
|
4669
4806
|
stateMutability: "nonpayable"
|
|
4670
4807
|
},
|
|
@@ -4810,6 +4947,12 @@ var ABI3 = [
|
|
|
4810
4947
|
indexed: true,
|
|
4811
4948
|
internalType: "bytes32"
|
|
4812
4949
|
},
|
|
4950
|
+
{
|
|
4951
|
+
name: "solver",
|
|
4952
|
+
type: "address",
|
|
4953
|
+
indexed: false,
|
|
4954
|
+
internalType: "address"
|
|
4955
|
+
},
|
|
4813
4956
|
{
|
|
4814
4957
|
name: "tokens",
|
|
4815
4958
|
type: "tuple[]",
|
|
@@ -5047,39 +5190,77 @@ var ABI3 = [
|
|
|
5047
5190
|
},
|
|
5048
5191
|
{
|
|
5049
5192
|
type: "event",
|
|
5050
|
-
name: "
|
|
5193
|
+
name: "OwnershipTransferStarted",
|
|
5051
5194
|
inputs: [
|
|
5052
5195
|
{
|
|
5053
|
-
name: "
|
|
5054
|
-
type: "
|
|
5055
|
-
indexed:
|
|
5056
|
-
internalType: "
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5196
|
+
name: "previousOwner",
|
|
5197
|
+
type: "address",
|
|
5198
|
+
indexed: true,
|
|
5199
|
+
internalType: "address"
|
|
5200
|
+
},
|
|
5201
|
+
{
|
|
5202
|
+
name: "newOwner",
|
|
5203
|
+
type: "address",
|
|
5204
|
+
indexed: true,
|
|
5205
|
+
internalType: "address"
|
|
5206
|
+
}
|
|
5207
|
+
],
|
|
5208
|
+
anonymous: false
|
|
5209
|
+
},
|
|
5210
|
+
{
|
|
5211
|
+
type: "event",
|
|
5212
|
+
name: "OwnershipTransferred",
|
|
5213
|
+
inputs: [
|
|
5214
|
+
{
|
|
5215
|
+
name: "previousOwner",
|
|
5216
|
+
type: "address",
|
|
5217
|
+
indexed: true,
|
|
5218
|
+
internalType: "address"
|
|
5219
|
+
},
|
|
5220
|
+
{
|
|
5221
|
+
name: "newOwner",
|
|
5222
|
+
type: "address",
|
|
5223
|
+
indexed: true,
|
|
5224
|
+
internalType: "address"
|
|
5225
|
+
}
|
|
5226
|
+
],
|
|
5227
|
+
anonymous: false
|
|
5228
|
+
},
|
|
5229
|
+
{
|
|
5230
|
+
type: "event",
|
|
5231
|
+
name: "ParamsUpdated",
|
|
5232
|
+
inputs: [
|
|
5233
|
+
{
|
|
5234
|
+
name: "previous",
|
|
5235
|
+
type: "tuple",
|
|
5236
|
+
indexed: false,
|
|
5237
|
+
internalType: "struct Params",
|
|
5238
|
+
components: [
|
|
5239
|
+
{
|
|
5240
|
+
name: "host",
|
|
5241
|
+
type: "address",
|
|
5242
|
+
internalType: "address"
|
|
5243
|
+
},
|
|
5244
|
+
{
|
|
5245
|
+
name: "dispatcher",
|
|
5246
|
+
type: "address",
|
|
5247
|
+
internalType: "address"
|
|
5248
|
+
},
|
|
5249
|
+
{
|
|
5250
|
+
name: "solverSelection",
|
|
5251
|
+
type: "bool",
|
|
5252
|
+
internalType: "bool"
|
|
5253
|
+
},
|
|
5254
|
+
{
|
|
5255
|
+
name: "surplusShareBps",
|
|
5256
|
+
type: "uint256",
|
|
5257
|
+
internalType: "uint256"
|
|
5258
|
+
},
|
|
5259
|
+
{
|
|
5260
|
+
name: "protocolFeeBps",
|
|
5261
|
+
type: "uint256",
|
|
5262
|
+
internalType: "uint256"
|
|
5263
|
+
},
|
|
5083
5264
|
{
|
|
5084
5265
|
name: "priceOracle",
|
|
5085
5266
|
type: "address",
|
|
@@ -5185,46 +5366,73 @@ var ABI3 = [
|
|
|
5185
5366
|
},
|
|
5186
5367
|
{
|
|
5187
5368
|
type: "event",
|
|
5188
|
-
name: "
|
|
5369
|
+
name: "Paused",
|
|
5189
5370
|
inputs: [
|
|
5190
5371
|
{
|
|
5191
|
-
name: "
|
|
5372
|
+
name: "account",
|
|
5192
5373
|
type: "address",
|
|
5193
5374
|
indexed: false,
|
|
5194
5375
|
internalType: "address"
|
|
5376
|
+
}
|
|
5377
|
+
],
|
|
5378
|
+
anonymous: false
|
|
5379
|
+
},
|
|
5380
|
+
{
|
|
5381
|
+
type: "event",
|
|
5382
|
+
name: "ProtocolFeeRefunded",
|
|
5383
|
+
inputs: [
|
|
5384
|
+
{
|
|
5385
|
+
name: "commitment",
|
|
5386
|
+
type: "bytes32",
|
|
5387
|
+
indexed: true,
|
|
5388
|
+
internalType: "bytes32"
|
|
5195
5389
|
},
|
|
5196
5390
|
{
|
|
5197
|
-
name: "
|
|
5391
|
+
name: "token",
|
|
5198
5392
|
type: "address",
|
|
5199
|
-
indexed:
|
|
5393
|
+
indexed: true,
|
|
5200
5394
|
internalType: "address"
|
|
5395
|
+
},
|
|
5396
|
+
{
|
|
5397
|
+
name: "amount",
|
|
5398
|
+
type: "uint256",
|
|
5399
|
+
indexed: false,
|
|
5400
|
+
internalType: "uint256"
|
|
5201
5401
|
}
|
|
5202
5402
|
],
|
|
5203
5403
|
anonymous: false
|
|
5204
5404
|
},
|
|
5205
5405
|
{
|
|
5206
5406
|
type: "event",
|
|
5207
|
-
name: "
|
|
5407
|
+
name: "RelayerUpdated",
|
|
5208
5408
|
inputs: [
|
|
5209
5409
|
{
|
|
5210
|
-
name: "
|
|
5410
|
+
name: "previous",
|
|
5411
|
+
type: "address",
|
|
5412
|
+
indexed: false,
|
|
5413
|
+
internalType: "address"
|
|
5414
|
+
},
|
|
5415
|
+
{
|
|
5416
|
+
name: "current",
|
|
5211
5417
|
type: "address",
|
|
5212
|
-
indexed:
|
|
5418
|
+
indexed: false,
|
|
5213
5419
|
internalType: "address"
|
|
5214
5420
|
}
|
|
5215
5421
|
],
|
|
5216
5422
|
anonymous: false
|
|
5217
5423
|
},
|
|
5218
5424
|
{
|
|
5219
|
-
type: "
|
|
5220
|
-
name: "
|
|
5425
|
+
type: "event",
|
|
5426
|
+
name: "Unpaused",
|
|
5221
5427
|
inputs: [
|
|
5222
5428
|
{
|
|
5223
|
-
name: "
|
|
5429
|
+
name: "account",
|
|
5224
5430
|
type: "address",
|
|
5431
|
+
indexed: false,
|
|
5225
5432
|
internalType: "address"
|
|
5226
5433
|
}
|
|
5227
|
-
]
|
|
5434
|
+
],
|
|
5435
|
+
anonymous: false
|
|
5228
5436
|
},
|
|
5229
5437
|
{
|
|
5230
5438
|
type: "error",
|
|
@@ -5260,28 +5468,17 @@ var ABI3 = [
|
|
|
5260
5468
|
},
|
|
5261
5469
|
{
|
|
5262
5470
|
type: "error",
|
|
5263
|
-
name: "
|
|
5264
|
-
inputs: [
|
|
5265
|
-
{
|
|
5266
|
-
name: "implementation",
|
|
5267
|
-
type: "address",
|
|
5268
|
-
internalType: "address"
|
|
5269
|
-
}
|
|
5270
|
-
]
|
|
5271
|
-
},
|
|
5272
|
-
{
|
|
5273
|
-
type: "error",
|
|
5274
|
-
name: "ERC1967NonPayable",
|
|
5471
|
+
name: "EnforcedPause",
|
|
5275
5472
|
inputs: []
|
|
5276
5473
|
},
|
|
5277
5474
|
{
|
|
5278
5475
|
type: "error",
|
|
5279
|
-
name: "
|
|
5476
|
+
name: "ExpectedPause",
|
|
5280
5477
|
inputs: []
|
|
5281
5478
|
},
|
|
5282
5479
|
{
|
|
5283
5480
|
type: "error",
|
|
5284
|
-
name: "
|
|
5481
|
+
name: "Expired",
|
|
5285
5482
|
inputs: []
|
|
5286
5483
|
},
|
|
5287
5484
|
{
|
|
@@ -5324,11 +5521,43 @@ var ABI3 = [
|
|
|
5324
5521
|
name: "NotInitializing",
|
|
5325
5522
|
inputs: []
|
|
5326
5523
|
},
|
|
5524
|
+
{
|
|
5525
|
+
type: "error",
|
|
5526
|
+
name: "OwnableInvalidOwner",
|
|
5527
|
+
inputs: [
|
|
5528
|
+
{
|
|
5529
|
+
name: "owner",
|
|
5530
|
+
type: "address",
|
|
5531
|
+
internalType: "address"
|
|
5532
|
+
}
|
|
5533
|
+
]
|
|
5534
|
+
},
|
|
5535
|
+
{
|
|
5536
|
+
type: "error",
|
|
5537
|
+
name: "OwnableUnauthorizedAccount",
|
|
5538
|
+
inputs: [
|
|
5539
|
+
{
|
|
5540
|
+
name: "account",
|
|
5541
|
+
type: "address",
|
|
5542
|
+
internalType: "address"
|
|
5543
|
+
}
|
|
5544
|
+
]
|
|
5545
|
+
},
|
|
5327
5546
|
{
|
|
5328
5547
|
type: "error",
|
|
5329
5548
|
name: "PartialFillNotAllowed",
|
|
5330
5549
|
inputs: []
|
|
5331
5550
|
},
|
|
5551
|
+
{
|
|
5552
|
+
type: "error",
|
|
5553
|
+
name: "RateBelowOrder",
|
|
5554
|
+
inputs: []
|
|
5555
|
+
},
|
|
5556
|
+
{
|
|
5557
|
+
type: "error",
|
|
5558
|
+
name: "RateFillTooSmall",
|
|
5559
|
+
inputs: []
|
|
5560
|
+
},
|
|
5332
5561
|
{
|
|
5333
5562
|
type: "error",
|
|
5334
5563
|
name: "ReentrancyGuardReentrantCall",
|
|
@@ -5556,9 +5785,6 @@ var ChainConfigService = class {
|
|
|
5556
5785
|
getUniswapV4StateViewAddress(chain) {
|
|
5557
5786
|
return this.getConfig(chain)?.addresses.UniswapV4StateView ?? "0x";
|
|
5558
5787
|
}
|
|
5559
|
-
getUniswapV4PoolConfigs(chain) {
|
|
5560
|
-
return this.getConfig(chain)?.uniswapV4Pools ?? [];
|
|
5561
|
-
}
|
|
5562
5788
|
getPermit2Address(chain) {
|
|
5563
5789
|
return this.getConfig(chain)?.addresses.Permit2 ?? "0x";
|
|
5564
5790
|
}
|
|
@@ -8295,9 +8521,7 @@ Failed HTTP Request: ${JSON.stringify(
|
|
|
8295
8521
|
};
|
|
8296
8522
|
|
|
8297
8523
|
// src/chains/intentsCoprocessor.ts
|
|
8298
|
-
var SYSTEM_EVENTS_KEY = u8aToHex(u8aConcat(xxhashAsU8a("System", 128), xxhashAsU8a("Events", 128)));
|
|
8299
8524
|
var OFFCHAIN_BID_PREFIX = new TextEncoder().encode("intents::bid::");
|
|
8300
|
-
var OFFCHAIN_PHANTOM_PREFIX = new TextEncoder().encode("intents::phantom::order::");
|
|
8301
8525
|
var HYPERBRIDGE_TYPES_BUNDLE = {
|
|
8302
8526
|
spec: {
|
|
8303
8527
|
nexus: { hasher: keccakAsU8a },
|
|
@@ -8307,11 +8531,7 @@ var HYPERBRIDGE_TYPES_BUNDLE = {
|
|
|
8307
8531
|
var BASE_TIP = 1000000000n;
|
|
8308
8532
|
var HTTP_CONNECT_TIMEOUT_MS = 2e4;
|
|
8309
8533
|
var INCLUSION_TIMEOUT_MS = 2e4;
|
|
8310
|
-
var PHANTOM_POLL_INTERVAL_MS = 15e3;
|
|
8311
|
-
var GARGANTUA_PHANTOM_POLL_INTERVAL_MS = 6e3;
|
|
8312
8534
|
var DEFAULT_RPC_MAX_RPS = 8;
|
|
8313
|
-
var DEFAULT_MAX_BLOCKS_PER_POLL = 10;
|
|
8314
|
-
var MAX_RATE_LIMIT_BACKOFF_TICKS = 8;
|
|
8315
8535
|
var rpcLimiters = /* @__PURE__ */ new Map();
|
|
8316
8536
|
function limiterFor(httpUrl) {
|
|
8317
8537
|
const key = new URL(httpUrl).origin;
|
|
@@ -8327,45 +8547,6 @@ function configuredRpcMaxRps() {
|
|
|
8327
8547
|
const parsed = raw === void 0 ? Number.NaN : Number(raw);
|
|
8328
8548
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_RPC_MAX_RPS;
|
|
8329
8549
|
}
|
|
8330
|
-
function isRateLimited(err) {
|
|
8331
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
8332
|
-
return message.includes("[429]") || /too many requests/i.test(message);
|
|
8333
|
-
}
|
|
8334
|
-
function isMethodUnavailable(err) {
|
|
8335
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
8336
|
-
const code = err?.code;
|
|
8337
|
-
return code === -32601 || /method not found|unsafe to be called externally/i.test(message);
|
|
8338
|
-
}
|
|
8339
|
-
var EventDecodeError = class extends Error {
|
|
8340
|
-
};
|
|
8341
|
-
function phantomOrdersFrom(records) {
|
|
8342
|
-
if (records == null || typeof records[Symbol.iterator] !== "function") {
|
|
8343
|
-
throw new EventDecodeError(`Expected a decoded event vector, got ${typeof records}`);
|
|
8344
|
-
}
|
|
8345
|
-
const orders = [];
|
|
8346
|
-
for (const record of records) {
|
|
8347
|
-
if (typeof record !== "object" || record === null || !("event" in record)) {
|
|
8348
|
-
throw new EventDecodeError(
|
|
8349
|
-
"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"
|
|
8350
|
-
);
|
|
8351
|
-
}
|
|
8352
|
-
const { event } = record;
|
|
8353
|
-
if (event.section !== "intentsCoprocessor" || event.method !== "PhantomOrderRegistered") continue;
|
|
8354
|
-
const [commitment, chain, createdAt, legs] = event.data;
|
|
8355
|
-
orders.push({
|
|
8356
|
-
commitment: commitment.toHex(),
|
|
8357
|
-
chain: new TextDecoder().decode(hexToU8a(chain.toHex())),
|
|
8358
|
-
createdAt: createdAt.toNumber(),
|
|
8359
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8360
|
-
legs: legs.map((leg) => ({
|
|
8361
|
-
tokenA: leg.tokenA.toHex(),
|
|
8362
|
-
tokenB: leg.tokenB.toHex(),
|
|
8363
|
-
standardAmount: BigInt(leg.standardAmount.toString())
|
|
8364
|
-
}))
|
|
8365
|
-
});
|
|
8366
|
-
}
|
|
8367
|
-
return orders;
|
|
8368
|
-
}
|
|
8369
8550
|
function rejectAfter(ms, message) {
|
|
8370
8551
|
return new Promise((_resolve, reject) => {
|
|
8371
8552
|
const timer = setTimeout(() => reject(new Error(message)), ms);
|
|
@@ -8437,19 +8618,16 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8437
8618
|
ownsConnection;
|
|
8438
8619
|
/** Cached result of whether the node exposes intents_* RPC methods */
|
|
8439
8620
|
hasIntentsRpc = null;
|
|
8440
|
-
/** The pallet's phantom timings, read once. Cleared on failure so the read retries. */
|
|
8441
|
-
phantomTimingsRead = null;
|
|
8442
8621
|
/** The HTTP-backed api, connected on first use. Cleared after a failed attempt so it retries. */
|
|
8443
8622
|
httpApi = null;
|
|
8444
|
-
/** Last runtime version read from the node, for {@link confirmedRuntimeVersion} to compare against. */
|
|
8445
|
-
lastRuntimeVersion;
|
|
8446
|
-
/** Set once the node refuses `state_queryStorage`, so the poll stops asking for it. */
|
|
8447
|
-
rangeQueryUnavailable = false;
|
|
8448
8623
|
// Serialises every extrinsic submission on this instance's substrate account. All submit/retract
|
|
8449
|
-
// methods funnel through signAndSendExtrinsic
|
|
8450
|
-
//
|
|
8451
|
-
//
|
|
8624
|
+
// methods funnel through signAndSendExtrinsic; fired in parallel (bids for orders on different
|
|
8625
|
+
// chains, or several bids on one order) they would grab the same nonce and all but one would
|
|
8626
|
+
// fail. Concurrency 1 sequences them, and {@link nextNonce} keeps consecutive submissions off
|
|
8627
|
+
// each other's nonce even while the first is still in the pool.
|
|
8452
8628
|
submissionQueue = new PQueue({ concurrency: 1 });
|
|
8629
|
+
/** The highest nonce this instance has put into the pool, so the next submission clears it. */
|
|
8630
|
+
lastPooledNonce;
|
|
8453
8631
|
/**
|
|
8454
8632
|
* Creates and connects an IntentsCoprocessor to a Hyperbridge node.
|
|
8455
8633
|
* This creates and manages its own API connection.
|
|
@@ -8511,10 +8689,10 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8511
8689
|
* The HTTP api is always created here, so it is always ours to close.
|
|
8512
8690
|
*/
|
|
8513
8691
|
async disconnect() {
|
|
8514
|
-
const
|
|
8692
|
+
const http3 = this.httpApi;
|
|
8515
8693
|
this.httpApi = null;
|
|
8516
|
-
if (
|
|
8517
|
-
await
|
|
8694
|
+
if (http3) {
|
|
8695
|
+
await http3.then((api) => api.disconnect()).catch(() => {
|
|
8518
8696
|
});
|
|
8519
8697
|
}
|
|
8520
8698
|
if (this.ownsConnection) {
|
|
@@ -8528,9 +8706,8 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8528
8706
|
*
|
|
8529
8707
|
* The connection attempt is bounded on both sides. `isReadyOrError` rejects on a failed
|
|
8530
8708
|
* handshake, where plain `isReady` would simply never resolve, and the timeout covers an
|
|
8531
|
-
* endpoint that accepts the request and then goes quiet
|
|
8532
|
-
*
|
|
8533
|
-
* failed attempt is not cached, so the next call tries again.
|
|
8709
|
+
* endpoint that accepts the request and then goes quiet, so a caller awaiting it never hangs
|
|
8710
|
+
* silently. A failed attempt is not cached, so the next call tries again.
|
|
8534
8711
|
*/
|
|
8535
8712
|
async http() {
|
|
8536
8713
|
if (!this.httpApi) {
|
|
@@ -8539,11 +8716,9 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8539
8716
|
// Response cache off (third argument, capacity 0). polkadot-js caches every request that
|
|
8540
8717
|
// names a block hash — `chain_getHeader(hash)`, `state_getRuntimeVersion(hash)`, storage
|
|
8541
8718
|
// reads at a hash — by storing the request promise itself, a rejected one included, for a
|
|
8542
|
-
// 30s TTL that every hit refreshes.
|
|
8543
|
-
//
|
|
8544
|
-
//
|
|
8545
|
-
// saw a second request. The cache bought nothing here anyway: the poll reads each block
|
|
8546
|
-
// once, and `api.at(hash)` reuses registries at the api layer regardless.
|
|
8719
|
+
// 30s TTL that every hit refreshes. A caller retrying a failed read with identical
|
|
8720
|
+
// parameters would get the same rejection replayed from memory for as long as it kept
|
|
8721
|
+
// retrying inside the TTL, and the node would never see a second request.
|
|
8547
8722
|
// Concurrent calls are coalesced into one JSON-RPC batch request, and every request
|
|
8548
8723
|
// to this endpoint is paced by a bucket shared with any other coprocessor in this
|
|
8549
8724
|
// process pointed at the same host — the limit is the server's, and it counts
|
|
@@ -8618,7 +8793,12 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8618
8793
|
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
8619
8794
|
}
|
|
8620
8795
|
}
|
|
8621
|
-
return await this.sendExtrinsicWithRetries(
|
|
8796
|
+
return await this.sendExtrinsicWithRetries(
|
|
8797
|
+
build(this.api),
|
|
8798
|
+
maxRetries,
|
|
8799
|
+
timeoutMs,
|
|
8800
|
+
await this.nextNonce(this.api)
|
|
8801
|
+
);
|
|
8622
8802
|
});
|
|
8623
8803
|
return result ?? { success: false, error: "Submission queue returned no result" };
|
|
8624
8804
|
}
|
|
@@ -8637,7 +8817,10 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8637
8817
|
*/
|
|
8638
8818
|
async sendViaHttp(api, build) {
|
|
8639
8819
|
try {
|
|
8640
|
-
const
|
|
8820
|
+
const nonce = await this.nextNonce(api);
|
|
8821
|
+
const options = nonce === void 0 ? { tip: BASE_TIP } : { tip: BASE_TIP, nonce };
|
|
8822
|
+
const hash = await build(api).signAndSend(this.getKeyPair(), options);
|
|
8823
|
+
if (nonce !== void 0) this.lastPooledNonce = nonce;
|
|
8641
8824
|
return { success: false, pending: true, extrinsicHash: hash.toHex() };
|
|
8642
8825
|
} catch (err) {
|
|
8643
8826
|
return this.classifySubmissionError(err instanceof Error ? err : new Error(String(err)));
|
|
@@ -8650,7 +8833,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8650
8833
|
* Two kinds of failure are retried, and the difference is the nonce.
|
|
8651
8834
|
*
|
|
8652
8835
|
* An attempt that verifiably went nowhere (rejected before the pool, dropped, invalid) leaves
|
|
8653
|
-
* the account nonce free, so the next attempt simply re-signs with the
|
|
8836
|
+
* the account nonce free, so the next attempt simply re-signs with the pool-aware nonce.
|
|
8654
8837
|
*
|
|
8655
8838
|
* An attempt that reached the pool and was still there when the watch timed out (`stalled`) is
|
|
8656
8839
|
* retried as a *replacement*: the same nonce it was signed with, and double the tip. Substrate's
|
|
@@ -8669,16 +8852,19 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8669
8852
|
* copy is in flight and its outcome is unknown here, so the `pending` result goes back to the
|
|
8670
8853
|
* caller to confirm later.
|
|
8671
8854
|
*/
|
|
8672
|
-
async sendExtrinsicWithRetries(extrinsic, maxRetries, timeoutMs) {
|
|
8855
|
+
async sendExtrinsicWithRetries(extrinsic, maxRetries, timeoutMs, initialNonce) {
|
|
8673
8856
|
const keyPair = this.getKeyPair();
|
|
8674
8857
|
let attempt = 0;
|
|
8675
|
-
let nonce;
|
|
8858
|
+
let nonce = initialNonce;
|
|
8676
8859
|
let stalled;
|
|
8677
8860
|
while (attempt < maxRetries) {
|
|
8678
8861
|
const currentTip = BASE_TIP * BigInt(2 ** attempt);
|
|
8679
8862
|
attempt++;
|
|
8680
8863
|
try {
|
|
8681
8864
|
const result = await this.sendWithTimeout(extrinsic, keyPair, currentTip, timeoutMs, nonce);
|
|
8865
|
+
if (nonce !== void 0 && (result.success || result.pending || result.stalled)) {
|
|
8866
|
+
this.lastPooledNonce = Math.max(nonce, this.lastPooledNonce ?? nonce);
|
|
8867
|
+
}
|
|
8682
8868
|
if (result.success || result.error?.includes("Dispatch error")) {
|
|
8683
8869
|
return result;
|
|
8684
8870
|
}
|
|
@@ -8738,6 +8924,35 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8738
8924
|
* replacement rather than a second extrinsic queued behind the first. Left undefined on the
|
|
8739
8925
|
* first attempt, where the api's auto-nonce is correct.
|
|
8740
8926
|
*/
|
|
8927
|
+
/**
|
|
8928
|
+
* The nonce the next submission signs with, or undefined when the node cannot be asked.
|
|
8929
|
+
*
|
|
8930
|
+
* Submissions are serialised, but a watch gives up while its extrinsic is still in the pool
|
|
8931
|
+
* (`stalled`, reported as `pending`), and a pooled extrinsic does not advance the account's
|
|
8932
|
+
* on-chain nonce. An extrinsic signed against on-chain state would then take the nonce of the
|
|
8933
|
+
* one ahead of it and bounce off it (1013/1014) instead of queueing behind it.
|
|
8934
|
+
*
|
|
8935
|
+
* `system_accountNextIndex` is the only pool-aware source here. `signAndSend`'s own `nonce: -1`
|
|
8936
|
+
* is not: on a runtime carrying `AccountNonceApi`, which Hyperbridge does, polkadot-js reads the
|
|
8937
|
+
* nonce through that runtime call and never sees the pool.
|
|
8938
|
+
*
|
|
8939
|
+
* The RPC covers the ready queue, so anything of this instance's that has left it — retried,
|
|
8940
|
+
* queued as future, or simply not counted yet — is covered by `lastPooledNonce` instead, which
|
|
8941
|
+
* is the highest nonce this instance has put into the pool. The two are combined rather than
|
|
8942
|
+
* trusted separately, so a burst never reuses a nonce and a restart still picks up the chain's.
|
|
8943
|
+
*/
|
|
8944
|
+
async nextNonce(api) {
|
|
8945
|
+
try {
|
|
8946
|
+
const accountNextIndex = api.rpc?.system?.accountNextIndex;
|
|
8947
|
+
if (!accountNextIndex) return void 0;
|
|
8948
|
+
const next = await accountNextIndex(this.getKeyPair().address);
|
|
8949
|
+
const fromPool = Number(next.toString());
|
|
8950
|
+
if (!Number.isFinite(fromPool)) return void 0;
|
|
8951
|
+
return this.lastPooledNonce === void 0 ? fromPool : Math.max(fromPool, this.lastPooledNonce + 1);
|
|
8952
|
+
} catch {
|
|
8953
|
+
return void 0;
|
|
8954
|
+
}
|
|
8955
|
+
}
|
|
8741
8956
|
async sendWithTimeout(extrinsic, keyPair, tip, timeoutMs, nonce) {
|
|
8742
8957
|
return new Promise((resolve) => {
|
|
8743
8958
|
let resolved = false;
|
|
@@ -8780,25 +8995,10 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8780
8995
|
} else if (result.status.isInBlock || result.status.isFinalized) {
|
|
8781
8996
|
resolved = true;
|
|
8782
8997
|
clearTimeout(timeoutId);
|
|
8783
|
-
const interrupted = result.events.find(
|
|
8784
|
-
({ event }) => event.section === "utility" && event.method === "BatchInterrupted"
|
|
8785
|
-
);
|
|
8786
|
-
if (interrupted) {
|
|
8787
|
-
const [indexCodec, dispatchError] = interrupted.event.data;
|
|
8788
|
-
if (Number(indexCodec.toString()) === 0) {
|
|
8789
|
-
resolve({
|
|
8790
|
-
success: false,
|
|
8791
|
-
error: `Dispatch error: ${this.describeDispatchError(dispatchError)}`
|
|
8792
|
-
});
|
|
8793
|
-
return;
|
|
8794
|
-
}
|
|
8795
|
-
}
|
|
8796
8998
|
resolve({
|
|
8797
8999
|
success: true,
|
|
8798
9000
|
blockHash: (result.status.isInBlock ? result.status.asInBlock : result.status.asFinalized).toHex(),
|
|
8799
|
-
extrinsicHash: extrinsic.hash.toHex()
|
|
8800
|
-
// Carried so a batch caller can attribute each item's outcome.
|
|
8801
|
-
events: result.events
|
|
9001
|
+
extrinsicHash: extrinsic.hash.toHex()
|
|
8802
9002
|
});
|
|
8803
9003
|
}
|
|
8804
9004
|
}).then((unsub) => {
|
|
@@ -8819,13 +9019,19 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8819
9019
|
/**
|
|
8820
9020
|
* Submits a bid to Hyperbridge's pallet-intents
|
|
8821
9021
|
*
|
|
9022
|
+
* A filler can hold several bids on one order, one per price it offers, and `bid` tells them
|
|
9023
|
+
* apart: submitting again under an identifier the filler already holds replaces that bid alone.
|
|
9024
|
+
* By convention it is `keccak256` of the UserOp's `callData`, which is also what gives each bid
|
|
9025
|
+
* its own nonce key on the `SolverAccount` (see `CryptoUtils.bidId`).
|
|
9026
|
+
*
|
|
8822
9027
|
* @param commitment - The order commitment hash (bytes32)
|
|
8823
9028
|
* @param userOp - The encoded PackedUserOperation as hex string
|
|
9029
|
+
* @param bid - Identifies this bid among the filler's bids on the order (bytes32)
|
|
8824
9030
|
* @returns BidSubmissionResult with success status and block/extrinsic hash
|
|
8825
9031
|
*/
|
|
8826
|
-
async submitBid(commitment, userOp) {
|
|
9032
|
+
async submitBid(commitment, userOp, bid) {
|
|
8827
9033
|
try {
|
|
8828
|
-
return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.placeBid(commitment, userOp));
|
|
9034
|
+
return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.placeBid(commitment, bid, userOp));
|
|
8829
9035
|
} catch (error) {
|
|
8830
9036
|
return {
|
|
8831
9037
|
success: false,
|
|
@@ -8839,46 +9045,12 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8839
9045
|
* Use this to remove unused quotes and claim back deposited BRIDGE tokens.
|
|
8840
9046
|
*
|
|
8841
9047
|
* @param commitment - The order commitment hash (bytes32)
|
|
9048
|
+
* @param bid - Which of the filler's bids on the order to retract (bytes32)
|
|
8842
9049
|
* @returns BidSubmissionResult with success status and block/extrinsic hash
|
|
8843
9050
|
*/
|
|
8844
|
-
async retractBid(commitment) {
|
|
8845
|
-
try {
|
|
8846
|
-
return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.retractBid(commitment));
|
|
8847
|
-
} catch (error) {
|
|
8848
|
-
return {
|
|
8849
|
-
success: false,
|
|
8850
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
8851
|
-
};
|
|
8852
|
-
}
|
|
8853
|
-
}
|
|
8854
|
-
/**
|
|
8855
|
-
* Places a new bid and retracts a previous one in a single transaction via utility.batch.
|
|
8856
|
-
*
|
|
8857
|
-
* The new bid is the primary operation, so `placeBid` MUST run first. `utility.batch` is
|
|
8858
|
-
* non-atomic: a failing call interrupts the batch (via a BatchInterrupted event) without
|
|
8859
|
-
* reverting the calls that already succeeded. Placing first guarantees the new bid lands even
|
|
8860
|
-
* when the retraction then fails — which it routinely does, because a previous commitment's bid
|
|
8861
|
-
* may already be gone (or was itself never placed), making `retractBid` return `BidNotFound`.
|
|
8862
|
-
*
|
|
8863
|
-
* Ordering retraction first (the previous behaviour) caused a self-sustaining cascade: a
|
|
8864
|
-
* `BidNotFound` on the leading retract skipped the trailing `placeBid`, so the current bid never
|
|
8865
|
-
* landed, so the *next* interval's retract of that never-placed commitment also failed, and so
|
|
8866
|
-
* on — silently, because the batch extrinsic itself reports success. The deposit reclaim is
|
|
8867
|
-
* best-effort; landing the bid is not.
|
|
8868
|
-
*
|
|
8869
|
-
* @param retractCommitment - The order commitment of the bid to retract (bytes32)
|
|
8870
|
-
* @param bidCommitment - The order commitment of the new bid (bytes32)
|
|
8871
|
-
* @param userOp - The encoded PackedUserOperation as hex string
|
|
8872
|
-
* @returns BidSubmissionResult with success status and block/extrinsic hash
|
|
8873
|
-
*/
|
|
8874
|
-
async submitBidWithRetraction(retractCommitment, bidCommitment, userOp) {
|
|
9051
|
+
async retractBid(commitment, bid) {
|
|
8875
9052
|
try {
|
|
8876
|
-
return await this.signAndSendExtrinsic(
|
|
8877
|
-
(api) => api.tx.utility.batch([
|
|
8878
|
-
api.tx.intentsCoprocessor.placeBid(bidCommitment, userOp),
|
|
8879
|
-
api.tx.intentsCoprocessor.retractBid(retractCommitment)
|
|
8880
|
-
])
|
|
8881
|
-
);
|
|
9053
|
+
return await this.signAndSendExtrinsic((api) => api.tx.intentsCoprocessor.retractBid(commitment, bid));
|
|
8882
9054
|
} catch (error) {
|
|
8883
9055
|
return {
|
|
8884
9056
|
success: false,
|
|
@@ -8886,89 +9058,6 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8886
9058
|
};
|
|
8887
9059
|
}
|
|
8888
9060
|
}
|
|
8889
|
-
/**
|
|
8890
|
-
* Places every phantom bid of one interval in a single extrinsic, retracting each chain's
|
|
8891
|
-
* previous bid alongside it.
|
|
8892
|
-
*
|
|
8893
|
-
* The pallet registers one phantom order per configured chain in the same block, so this is the
|
|
8894
|
-
* whole interval's set. Submitting them one at a time costs a block per chain: submissions are
|
|
8895
|
-
* serialised on the account nonce and each waits for inclusion, so the last chain's bid is many
|
|
8896
|
-
* blocks behind the first, against a bid window measured in tens of blocks. Batched, every bid
|
|
8897
|
-
* lands in the same block.
|
|
8898
|
-
*
|
|
8899
|
-
* Uses `utility.force_batch`, not `utility.batch`. `batch` stops at the first failing call, so
|
|
8900
|
-
* one rejected bid — a closed window, a duplicate, an insufficient deposit — would silently
|
|
8901
|
-
* drop every bid after it. `force_batch` runs them all and reports each outcome. It needs no
|
|
8902
|
-
* special origin: any signed account may call it, exactly like `batch`.
|
|
8903
|
-
*
|
|
8904
|
-
* @param bids - The bids to place; an empty list is a no-op
|
|
8905
|
-
* @returns Per-bid outcomes, in the order given
|
|
8906
|
-
*/
|
|
8907
|
-
async submitPhantomBids(bids) {
|
|
8908
|
-
if (bids.length === 0) return { bids: [] };
|
|
8909
|
-
const placeIndexByBid = [];
|
|
8910
|
-
let callCount = 0;
|
|
8911
|
-
for (const bid of bids) {
|
|
8912
|
-
placeIndexByBid.push(callCount);
|
|
8913
|
-
callCount += bid.retractCommitment ? 2 : 1;
|
|
8914
|
-
}
|
|
8915
|
-
const outcome = await this.signAndSendExtrinsic(
|
|
8916
|
-
(api) => api.tx.utility.forceBatch(
|
|
8917
|
-
bids.flatMap((bid) => {
|
|
8918
|
-
const calls = [api.tx.intentsCoprocessor.placeBid(bid.commitment, bid.userOp)];
|
|
8919
|
-
if (bid.retractCommitment) {
|
|
8920
|
-
calls.push(api.tx.intentsCoprocessor.retractBid(bid.retractCommitment));
|
|
8921
|
-
}
|
|
8922
|
-
return calls;
|
|
8923
|
-
})
|
|
8924
|
-
)
|
|
8925
|
-
);
|
|
8926
|
-
if (!outcome.success) {
|
|
8927
|
-
return {
|
|
8928
|
-
bids: bids.map((bid) => ({ commitment: bid.commitment, success: false, error: outcome.error })),
|
|
8929
|
-
pending: outcome.pending,
|
|
8930
|
-
extrinsicHash: outcome.extrinsicHash,
|
|
8931
|
-
error: outcome.error
|
|
8932
|
-
};
|
|
8933
|
-
}
|
|
8934
|
-
const items = this.readForceBatchItems(outcome.events ?? [], callCount);
|
|
8935
|
-
return {
|
|
8936
|
-
bids: bids.map((bid, index) => {
|
|
8937
|
-
const error = items.errors[placeIndexByBid[index]];
|
|
8938
|
-
return { commitment: bid.commitment, success: !error, error };
|
|
8939
|
-
}),
|
|
8940
|
-
blockHash: outcome.blockHash,
|
|
8941
|
-
extrinsicHash: outcome.extrinsicHash,
|
|
8942
|
-
error: items.error
|
|
8943
|
-
};
|
|
8944
|
-
}
|
|
8945
|
-
/**
|
|
8946
|
-
* Reads one outcome per call out of a force_batch's events.
|
|
8947
|
-
*
|
|
8948
|
-
* `ItemFailed` carries no index — pallet-utility emits exactly one `ItemCompleted` or
|
|
8949
|
-
* `ItemFailed` per call, in call order, so the k-th item event belongs to call k.
|
|
8950
|
-
*
|
|
8951
|
-
* A count that does not match the calls submitted means the events are not the ones assumed
|
|
8952
|
-
* here, and every attribution after the discrepancy would be off by one. The bids are then
|
|
8953
|
-
* reported as placed: a bid wrongly recorded as landed is retracted next interval and the
|
|
8954
|
-
* retraction harmlessly fails with `BidNotFound`, whereas one wrongly recorded as failed is
|
|
8955
|
-
* never retracted at all and leaves its deposit reserved.
|
|
8956
|
-
*/
|
|
8957
|
-
readForceBatchItems(events, callCount) {
|
|
8958
|
-
const errors = [];
|
|
8959
|
-
for (const { event } of events) {
|
|
8960
|
-
if (event.section !== "utility") continue;
|
|
8961
|
-
if (event.method === "ItemCompleted") errors.push(void 0);
|
|
8962
|
-
else if (event.method === "ItemFailed") errors.push(this.describeDispatchError(event.data[0]));
|
|
8963
|
-
}
|
|
8964
|
-
if (errors.length !== callCount) {
|
|
8965
|
-
return {
|
|
8966
|
-
errors: new Array(callCount).fill(void 0),
|
|
8967
|
-
error: `force_batch reported ${errors.length} item events for ${callCount} calls; outcomes not attributed`
|
|
8968
|
-
};
|
|
8969
|
-
}
|
|
8970
|
-
return { errors };
|
|
8971
|
-
}
|
|
8972
9061
|
/** Renders a DispatchError as `pallet::Error`, falling back to its raw form. */
|
|
8973
9062
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8974
9063
|
describeDispatchError(dispatchError) {
|
|
@@ -8987,10 +9076,11 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
8987
9076
|
*/
|
|
8988
9077
|
async getBidStorageEntries(commitment) {
|
|
8989
9078
|
const api = await this.http();
|
|
8990
|
-
const entries = await api.query.intentsCoprocessor.
|
|
9079
|
+
const entries = await api.query.intentsCoprocessor.orderBids.entries(commitment);
|
|
8991
9080
|
return entries.map(([storageKey, depositValue]) => ({
|
|
8992
9081
|
commitment,
|
|
8993
9082
|
filler: storageKey.args[1].toString(),
|
|
9083
|
+
bid: storageKey.args[2].toHex(),
|
|
8994
9084
|
deposit: BigInt(depositValue.toString())
|
|
8995
9085
|
}));
|
|
8996
9086
|
}
|
|
@@ -9022,7 +9112,7 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
9022
9112
|
return result.map((entry) => {
|
|
9023
9113
|
const userOp = decodeUserOpScale(entry.user_op);
|
|
9024
9114
|
const filler = new Keyring({ type: "sr25519" }).encodeAddress(hexToU8a(entry.filler));
|
|
9025
|
-
return { filler, userOp, deposit: 0n };
|
|
9115
|
+
return { filler, bid: entry.bid, userOp, deposit: 0n };
|
|
9026
9116
|
});
|
|
9027
9117
|
}
|
|
9028
9118
|
/**
|
|
@@ -9031,19 +9121,20 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
9031
9121
|
*/
|
|
9032
9122
|
async getBidsViaStorage(commitment) {
|
|
9033
9123
|
const api = await this.http();
|
|
9034
|
-
const entries = await api.query.intentsCoprocessor.
|
|
9124
|
+
const entries = await api.query.intentsCoprocessor.orderBids.entries(commitment);
|
|
9035
9125
|
if (entries.length === 0) return [];
|
|
9036
9126
|
const bidPromises = entries.map(async ([storageKey, depositValue]) => {
|
|
9037
9127
|
try {
|
|
9038
9128
|
const filler = storageKey.args[1].toString();
|
|
9129
|
+
const bid = storageKey.args[2].toHex();
|
|
9039
9130
|
const deposit = BigInt(depositValue.toString());
|
|
9040
|
-
const offchainKey = this.buildOffchainBidKey(commitment, filler);
|
|
9131
|
+
const offchainKey = this.buildOffchainBidKey(commitment, filler, bid);
|
|
9041
9132
|
const offchainKeyHex = u8aToHex(offchainKey);
|
|
9042
9133
|
const offchainResult = await api.rpc.offchain.localStorageGet("PERSISTENT", offchainKeyHex);
|
|
9043
9134
|
if (!offchainResult || offchainResult.isNone) return null;
|
|
9044
9135
|
const bidData = offchainResult.unwrap().toHex();
|
|
9045
9136
|
const decoded = this.decodeBid(bidData);
|
|
9046
|
-
return { filler: decoded.filler, userOp: decoded.userOp, deposit };
|
|
9137
|
+
return { filler: decoded.filler, bid, userOp: decoded.userOp, deposit };
|
|
9047
9138
|
} catch {
|
|
9048
9139
|
return null;
|
|
9049
9140
|
}
|
|
@@ -9059,411 +9150,31 @@ var IntentsCoprocessor = class _IntentsCoprocessor {
|
|
|
9059
9150
|
const userOp = decodeUserOpScale(userOpHex);
|
|
9060
9151
|
return { filler, userOp };
|
|
9061
9152
|
}
|
|
9062
|
-
/** Builds offchain storage key: "intents::bid::" + commitment + filler */
|
|
9063
|
-
buildOffchainBidKey(commitment, filler) {
|
|
9064
|
-
return u8aConcat(OFFCHAIN_BID_PREFIX, hexToU8a(commitment), decodeAddress(filler));
|
|
9153
|
+
/** Builds offchain storage key: "intents::bid::" + commitment + filler + bid */
|
|
9154
|
+
buildOffchainBidKey(commitment, filler, bid) {
|
|
9155
|
+
return u8aConcat(OFFCHAIN_BID_PREFIX, hexToU8a(commitment), decodeAddress(filler), hexToU8a(bid));
|
|
9065
9156
|
}
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
* or the commitment has expired and been cleared).
|
|
9073
|
-
*/
|
|
9074
|
-
async fetchPhantomOrder(commitment) {
|
|
9075
|
-
const key = u8aConcat(OFFCHAIN_PHANTOM_PREFIX, hexToU8a(commitment));
|
|
9076
|
-
const api = await this.http();
|
|
9077
|
-
const result = await api.rpc.offchain.localStorageGet("PERSISTENT", u8aToHex(key));
|
|
9078
|
-
if (!result || result.isNone) return null;
|
|
9079
|
-
const rawHex = result.unwrap().toHex();
|
|
9080
|
-
if (rawHex === "0x" || rawHex === "0x00") return null;
|
|
9081
|
-
const placeOrderAbi = IntentGatewayV2_default.ABI.find((item) => item.type === "function" && item.name === "placeOrder");
|
|
9082
|
-
const orderType = placeOrderAbi?.inputs?.[0];
|
|
9083
|
-
if (!orderType) return null;
|
|
9084
|
-
const [decoded] = decodeAbiParameters([orderType], rawHex);
|
|
9085
|
-
const d = decoded;
|
|
9086
|
-
const textDecoder = new TextDecoder();
|
|
9087
|
-
return {
|
|
9088
|
-
id: commitment,
|
|
9089
|
-
user: d.user,
|
|
9090
|
-
source: textDecoder.decode(hexToBytes(d.source)),
|
|
9091
|
-
destination: textDecoder.decode(hexToBytes(d.destination)),
|
|
9092
|
-
deadline: d.deadline,
|
|
9093
|
-
nonce: d.nonce,
|
|
9094
|
-
fees: d.fees,
|
|
9095
|
-
session: d.session,
|
|
9096
|
-
predispatch: {
|
|
9097
|
-
assets: d.predispatch.assets.map((a) => ({
|
|
9098
|
-
token: a.token,
|
|
9099
|
-
amount: a.amount
|
|
9100
|
-
})),
|
|
9101
|
-
call: d.predispatch.call
|
|
9102
|
-
},
|
|
9103
|
-
inputs: d.inputs.map((i) => ({
|
|
9104
|
-
token: i.token,
|
|
9105
|
-
amount: i.amount
|
|
9106
|
-
})),
|
|
9107
|
-
output: {
|
|
9108
|
-
beneficiary: d.output.beneficiary,
|
|
9109
|
-
assets: d.output.assets.map((a) => ({
|
|
9110
|
-
token: a.token,
|
|
9111
|
-
amount: a.amount
|
|
9112
|
-
})),
|
|
9113
|
-
call: d.output.call
|
|
9114
|
-
}
|
|
9115
|
-
};
|
|
9157
|
+
};
|
|
9158
|
+
var TronChain = class _TronChain {
|
|
9159
|
+
constructor(params, evm) {
|
|
9160
|
+
this.params = params;
|
|
9161
|
+
this.evm = evm;
|
|
9162
|
+
this.tronWebInstance = new TronWeb({ fullHost: params.rpcUrl });
|
|
9116
9163
|
}
|
|
9164
|
+
params;
|
|
9165
|
+
evm;
|
|
9166
|
+
tronWebInstance;
|
|
9117
9167
|
/**
|
|
9118
|
-
*
|
|
9119
|
-
*
|
|
9120
|
-
* Costs two RPCs per block when `knownVersion` is supplied and four without it, which is why the
|
|
9121
|
-
* poll goes to the trouble of establishing one. `api.at(hash)` has to work out which metadata to
|
|
9122
|
-
* decode the block against, and with nothing to go on it fetches the header and then the runtime
|
|
9123
|
-
* version at its parent — every block, forever. Its cheaper paths are a registry already pinned
|
|
9124
|
-
* to this exact hash (only ever the previous block's) or one matching a version the caller
|
|
9125
|
-
* names, so naming the version is the only way out. See `getBlockRegistry` in
|
|
9126
|
-
* `@polkadot/api/base/Init`; the `getUpgradeVersion` shortcut that would otherwise skip the
|
|
9127
|
-
* lookup only covers chains hardcoded in `@polkadot/types-known`, which Hyperbridge is not.
|
|
9168
|
+
* Creates a `TronChain` instance from a fully-specified config object.
|
|
9128
9169
|
*
|
|
9129
|
-
*
|
|
9130
|
-
* established one. Passing a version the block does not actually run decodes it against the
|
|
9131
|
-
* wrong metadata, so this is for callers that have checked, not a place to pass a guess.
|
|
9132
|
-
*/
|
|
9133
|
-
async getPhantomOrdersInBlock(blockNumber, knownVersion) {
|
|
9134
|
-
const api = await this.http();
|
|
9135
|
-
const blockHash = await api.rpc.chain.getBlockHash(blockNumber);
|
|
9136
|
-
return await this.getPhantomOrdersAtHash(blockHash.toHex(), knownVersion);
|
|
9137
|
-
}
|
|
9138
|
-
/**
|
|
9139
|
-
* The same read, for a caller that already holds the block's hash.
|
|
9170
|
+
* This is the only public way to construct a `TronChain` — the constructor is private.
|
|
9140
9171
|
*
|
|
9141
|
-
*
|
|
9142
|
-
*
|
|
9143
|
-
* its hash. `chain_getBlockHash` is the half of the pair that parallelises safely: it takes no
|
|
9144
|
-
* historic block hash, so it never triggers polkadot-js's per-hash registry resolution, and
|
|
9145
|
-
* concurrent calls cannot race each other's registry state.
|
|
9172
|
+
* @param params - Tron chain configuration
|
|
9173
|
+
* @returns A `TronChain` instance
|
|
9146
9174
|
*/
|
|
9147
|
-
async
|
|
9148
|
-
const
|
|
9149
|
-
|
|
9150
|
-
return phantomOrdersFrom(await apiAt.query.system.events());
|
|
9151
|
-
}
|
|
9152
|
-
/**
|
|
9153
|
-
* Every block's phantom orders across a whole range, in one `state_queryStorage` call.
|
|
9154
|
-
*
|
|
9155
|
-
* This is the cheap path: the request cost of a scan stops depending on how many blocks it
|
|
9156
|
-
* covers. The events key is the only key queried, and both bounds are block hashes the caller
|
|
9157
|
-
* already holds.
|
|
9158
|
-
*
|
|
9159
|
-
* Two properties of the RPC shape the result.
|
|
9160
|
-
*
|
|
9161
|
-
* It returns *diffs*: `query_storage_unfiltered` in `sc-rpc` pushes a change set for a block only
|
|
9162
|
-
* when the value differs from the previous block in the range (`has_changed`, and the set is
|
|
9163
|
-
* dropped when empty), so a block whose events encode byte-for-byte identically to its
|
|
9164
|
-
* predecessor's is simply absent. That happens on a quiet chain, where consecutive blocks carry
|
|
9165
|
-
* nothing but the timestamp inherent's `ExtrinsicSuccess`. It is safe here because an absent
|
|
9166
|
-
* block provably carries no phantom orders: a `PhantomOrderRegistered` commitment is derived from
|
|
9167
|
-
* the block number (`phantom_order_commitment`), so a block that registered orders can never
|
|
9168
|
-
* encode identically to any other block. Absent therefore means "same as the previous block",
|
|
9169
|
-
* and the previous block having orders would contradict that.
|
|
9170
|
-
*
|
|
9171
|
-
* And it is gated by `--rpc-methods` (`check_if_safe` in `sc-rpc`), which answers a denied call
|
|
9172
|
-
* with `Method not found`. The node this reads from must already run unsafe RPC to serve
|
|
9173
|
-
* `offchain_localStorageGet` for the orders themselves, so this is normally available; the poll
|
|
9174
|
-
* falls back to reading block by block when it is not.
|
|
9175
|
-
*
|
|
9176
|
-
* @returns one entry per block the node reported a change for, in ascending block order.
|
|
9177
|
-
*/
|
|
9178
|
-
async getPhantomOrdersInRange(fromBlockHash, toBlockHash) {
|
|
9179
|
-
const api = await this.http();
|
|
9180
|
-
const changeSets = await api.rpc.state.queryStorage.raw(
|
|
9181
|
-
[SYSTEM_EVENTS_KEY],
|
|
9182
|
-
fromBlockHash,
|
|
9183
|
-
toBlockHash
|
|
9184
|
-
);
|
|
9185
|
-
return changeSets.map((changeSet) => {
|
|
9186
|
-
const value = changeSet?.changes?.find(([key]) => key === SYSTEM_EVENTS_KEY)?.[1];
|
|
9187
|
-
if (!value) return [];
|
|
9188
|
-
return phantomOrdersFrom(api.registry.createType("Vec<EventRecord>", value));
|
|
9189
|
-
});
|
|
9190
|
-
}
|
|
9191
|
-
/**
|
|
9192
|
-
* Polls for newly registered phantom orders, invoking the callback once per block that carries
|
|
9193
|
-
* any, with all of that block's orders.
|
|
9194
|
-
*
|
|
9195
|
-
* Per block rather than per order because that is how the pallet writes them: one order per
|
|
9196
|
-
* configured chain, all registered in the same `on_initialize`. Delivering them together lets a
|
|
9197
|
-
* caller bid on the whole interval in one extrinsic (see {@link submitPhantomBids}) instead of
|
|
9198
|
-
* one per chain.
|
|
9199
|
-
*
|
|
9200
|
-
* Each tick reads the current head and scans every block between the last one processed and that
|
|
9201
|
-
* head, so the block cursor — not the connection — determines what has been seen. This replaced a
|
|
9202
|
-
* system.events subscription, which was only as reliable as its socket: polkadot-js reconnects
|
|
9203
|
-
* the transport but does not reliably re-establish storage subscriptions, and anything emitted
|
|
9204
|
-
* while disconnected was lost silently. With a bid window measured in a handful of blocks, that
|
|
9205
|
-
* meant silently missed bids.
|
|
9206
|
-
*
|
|
9207
|
-
* Scanning a block range is gap-free rather than merely self-healing: an outage delays orders but
|
|
9208
|
-
* cannot drop them, because the cursor only advances past a block whose events were actually
|
|
9209
|
-
* read. Recovery replays the backlog.
|
|
9210
|
-
*
|
|
9211
|
-
* Every read here goes over HTTP, never the websocket. Polling is a sequence of independent
|
|
9212
|
-
* one-shot requests with no state to lose between them, which is exactly what a stateless
|
|
9213
|
-
* transport does well: a request either answers or fails loudly on this tick, instead of a
|
|
9214
|
-
* socket that looks alive while delivering nothing. It also means a websocket outage does not
|
|
9215
|
-
* pause phantom bidding at all — the two transports fail independently.
|
|
9216
|
-
*
|
|
9217
|
-
* What the cadence does *not* describe is the request rate, which is what rate limiters police.
|
|
9218
|
-
* A tick costs four requests whatever the range covers — the head, the runtime version, the two
|
|
9219
|
-
* bounding block hashes as one batched request, and one `state_queryStorage` for every block's
|
|
9220
|
-
* events — and they go out back-to-back, so an interval well under any per-second limit could
|
|
9221
|
-
* still arrive as a burst over it. Three things keep that in bounds: the provider coalesces
|
|
9222
|
-
* concurrent calls into one request and paces requests through the endpoint's token bucket (see
|
|
9223
|
-
* `http`), and `maxBlocksPerPoll` bounds the range. A 429 that gets through anyway backs the
|
|
9224
|
-
* poll off for a doubling number of ticks, so a limiter that is already shedding load is not
|
|
9225
|
-
* handed the next window's budget in rejections too.
|
|
9226
|
-
*
|
|
9227
|
-
* Where the node will not serve `state_queryStorage` the poll reads block by block instead, at
|
|
9228
|
-
* three requests plus one per block; see {@link scanRangeAtOnce}.
|
|
9229
|
-
*
|
|
9230
|
-
* Returns a function that stops polling.
|
|
9231
|
-
*/
|
|
9232
|
-
pollPhantomOrders(callback, options = {}) {
|
|
9233
|
-
const {
|
|
9234
|
-
intervalMs,
|
|
9235
|
-
maxBlocksPerPoll = DEFAULT_MAX_BLOCKS_PER_POLL,
|
|
9236
|
-
onError,
|
|
9237
|
-
onSkip
|
|
9238
|
-
} = options;
|
|
9239
|
-
let cursor = null;
|
|
9240
|
-
let inFlight = false;
|
|
9241
|
-
let stopped = false;
|
|
9242
|
-
let backoffTicks = 0;
|
|
9243
|
-
let backoffLength = 0;
|
|
9244
|
-
const tick = async () => {
|
|
9245
|
-
if (inFlight || stopped) return;
|
|
9246
|
-
if (backoffTicks > 0) {
|
|
9247
|
-
backoffTicks -= 1;
|
|
9248
|
-
return;
|
|
9249
|
-
}
|
|
9250
|
-
inFlight = true;
|
|
9251
|
-
try {
|
|
9252
|
-
const api = await this.http();
|
|
9253
|
-
const head = (await api.rpc.chain.getHeader()).number.toNumber();
|
|
9254
|
-
if (cursor === null) {
|
|
9255
|
-
cursor = Math.max(head - 1, -1);
|
|
9256
|
-
} else {
|
|
9257
|
-
const { bidWindowBlocks, intervalBlocks } = await this.phantomTimings();
|
|
9258
|
-
if (head - cursor > bidWindowBlocks + Math.max(intervalBlocks, bidWindowBlocks)) {
|
|
9259
|
-
const from = cursor + 1;
|
|
9260
|
-
cursor = Math.max(head - 1 - bidWindowBlocks, -1);
|
|
9261
|
-
onSkip?.({ from, to: cursor, head });
|
|
9262
|
-
}
|
|
9263
|
-
}
|
|
9264
|
-
if (head <= cursor) return;
|
|
9265
|
-
const knownVersion = await this.confirmedRuntimeVersion();
|
|
9266
|
-
const to = Math.min(head, cursor + maxBlocksPerPoll);
|
|
9267
|
-
const ranged = await this.scanRangeAtOnce(api, cursor + 1, to, knownVersion, onError);
|
|
9268
|
-
if (ranged) {
|
|
9269
|
-
for (const orders of ranged) {
|
|
9270
|
-
if (stopped) return;
|
|
9271
|
-
if (orders.length > 0) callback(orders);
|
|
9272
|
-
}
|
|
9273
|
-
cursor = to;
|
|
9274
|
-
backoffLength = 0;
|
|
9275
|
-
return;
|
|
9276
|
-
}
|
|
9277
|
-
const numbers = [];
|
|
9278
|
-
for (let blockNumber = cursor + 1; blockNumber <= to; blockNumber++) numbers.push(blockNumber);
|
|
9279
|
-
const hashes = await Promise.allSettled(
|
|
9280
|
-
numbers.map((blockNumber) => api.rpc.chain.getBlockHash(blockNumber))
|
|
9281
|
-
);
|
|
9282
|
-
for (let index = 0; index < numbers.length; index++) {
|
|
9283
|
-
if (stopped) return;
|
|
9284
|
-
const hash = hashes[index];
|
|
9285
|
-
if (hash.status === "rejected") throw hash.reason;
|
|
9286
|
-
const orders = await this.getPhantomOrdersAtHash(hash.value.toHex(), knownVersion);
|
|
9287
|
-
if (orders.length > 0) callback(orders);
|
|
9288
|
-
cursor = numbers[index];
|
|
9289
|
-
}
|
|
9290
|
-
backoffLength = 0;
|
|
9291
|
-
} catch (err) {
|
|
9292
|
-
if (isRateLimited(err)) {
|
|
9293
|
-
backoffLength = Math.min(backoffLength === 0 ? 1 : backoffLength * 2, MAX_RATE_LIMIT_BACKOFF_TICKS);
|
|
9294
|
-
backoffTicks = backoffLength;
|
|
9295
|
-
}
|
|
9296
|
-
onError?.(err);
|
|
9297
|
-
} finally {
|
|
9298
|
-
inFlight = false;
|
|
9299
|
-
}
|
|
9300
|
-
};
|
|
9301
|
-
void tick();
|
|
9302
|
-
let timer = null;
|
|
9303
|
-
const startTimer = (ms) => {
|
|
9304
|
-
if (stopped) return;
|
|
9305
|
-
timer = setInterval(() => void tick(), ms);
|
|
9306
|
-
};
|
|
9307
|
-
if (intervalMs !== void 0) startTimer(intervalMs);
|
|
9308
|
-
else void this.phantomPollIntervalMs().then(startTimer);
|
|
9309
|
-
return () => {
|
|
9310
|
-
stopped = true;
|
|
9311
|
-
if (timer) clearInterval(timer);
|
|
9312
|
-
};
|
|
9313
|
-
}
|
|
9314
|
-
/**
|
|
9315
|
-
* The Hyperbridge head, over HTTP like every other read here.
|
|
9316
|
-
*
|
|
9317
|
-
* Exposed for callers that have to know how old something is: a phantom order carries the block
|
|
9318
|
-
* it was registered at, and only against the head does that become "still biddable" or "long
|
|
9319
|
-
* expired".
|
|
9320
|
-
*/
|
|
9321
|
-
async latestBlockNumber() {
|
|
9322
|
-
const api = await this.http();
|
|
9323
|
-
return (await api.rpc.chain.getHeader()).number.toNumber();
|
|
9324
|
-
}
|
|
9325
|
-
/**
|
|
9326
|
-
* The pallet's phantom timings, read from chain state.
|
|
9327
|
-
*
|
|
9328
|
-
* Both are governance-settable and neither is derivable: on Nexus today the window is 15 while
|
|
9329
|
-
* the runtime constant behind it is 25, so anything hard-coded is wrong in one direction or the
|
|
9330
|
-
* other — too tight and live orders are dropped, too loose and bids are sent into a closed
|
|
9331
|
-
* window for the pallet to reject.
|
|
9332
|
-
*
|
|
9333
|
-
* Read once per instance and cached, because a governance change to either is rare and a read
|
|
9334
|
-
* per poll tick would be a request per tick forever. The cost is that a change is picked up on
|
|
9335
|
-
* the next restart rather than immediately. A failed read is not cached, so it retries.
|
|
9336
|
-
*/
|
|
9337
|
-
async phantomTimings() {
|
|
9338
|
-
if (!this.phantomTimingsRead) {
|
|
9339
|
-
this.phantomTimingsRead = this.readPhantomTimings().catch((err) => {
|
|
9340
|
-
this.phantomTimingsRead = null;
|
|
9341
|
-
throw err;
|
|
9342
|
-
});
|
|
9343
|
-
}
|
|
9344
|
-
return this.phantomTimingsRead;
|
|
9345
|
-
}
|
|
9346
|
-
async readPhantomTimings() {
|
|
9347
|
-
const api = await this.http();
|
|
9348
|
-
const [window, interval] = await Promise.all([
|
|
9349
|
-
api.query.intentsCoprocessor.phantomBidWindow(),
|
|
9350
|
-
api.query.intentsCoprocessor.phantomOrderInterval()
|
|
9351
|
-
]);
|
|
9352
|
-
const stored = Number(window.toString());
|
|
9353
|
-
return {
|
|
9354
|
-
bidWindowBlocks: stored === 0 ? Number(api.consts.intentsCoprocessor.phantomOrderBidWindowBlocks.toString()) : stored,
|
|
9355
|
-
intervalBlocks: Number(interval.toString())
|
|
9356
|
-
};
|
|
9357
|
-
}
|
|
9358
|
-
/**
|
|
9359
|
-
* A whole range of blocks in one `state_queryStorage` call, or `null` when that is not available
|
|
9360
|
-
* and the caller should read block by block.
|
|
9361
|
-
*
|
|
9362
|
-
* Two conditions have to hold, and both are about decoding rather than the range itself.
|
|
9363
|
-
*
|
|
9364
|
-
* The version must be confirmed for this tick — an upgrade inside the range means blocks decode
|
|
9365
|
-
* against different metadata, and one call cannot do that.
|
|
9366
|
-
*
|
|
9367
|
-
* And that confirmed version must still be the one the api's own registry was built for.
|
|
9368
|
-
* `state_queryStorage` declares no historic block hash, so rpc-core skips its registry swap and
|
|
9369
|
-
* decodes the reply against the default registry — fixed at connect, with no
|
|
9370
|
-
* `subscribeRuntimeVersion` on an HTTP api to refresh it. After an upgrade the two diverge, and
|
|
9371
|
-
* the per-block path takes over for good: `api.at(hash, version)` resolves, and builds, the right
|
|
9372
|
-
* registry. That costs a restart to get the cheap path back, which is the correct direction to
|
|
9373
|
-
* fail in.
|
|
9374
|
-
*/
|
|
9375
|
-
async scanRangeAtOnce(api, from, to, knownVersion, onError) {
|
|
9376
|
-
if (this.rangeQueryUnavailable || !knownVersion) return null;
|
|
9377
|
-
const registryVersion = api.runtimeVersion?.specVersion;
|
|
9378
|
-
if (!registryVersion || !knownVersion.specVersion.eq(registryVersion)) return null;
|
|
9379
|
-
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)]);
|
|
9380
|
-
try {
|
|
9381
|
-
return await this.getPhantomOrdersInRange(fromHash.toHex(), toHash.toHex());
|
|
9382
|
-
} catch (err) {
|
|
9383
|
-
if (isMethodUnavailable(err)) {
|
|
9384
|
-
this.rangeQueryUnavailable = true;
|
|
9385
|
-
return null;
|
|
9386
|
-
}
|
|
9387
|
-
if (err instanceof EventDecodeError) {
|
|
9388
|
-
this.rangeQueryUnavailable = true;
|
|
9389
|
-
onError?.(err);
|
|
9390
|
-
return null;
|
|
9391
|
-
}
|
|
9392
|
-
throw err;
|
|
9393
|
-
}
|
|
9394
|
-
}
|
|
9395
|
-
/**
|
|
9396
|
-
* The runtime version this tick's blocks may be decoded against, or `undefined` when that cannot
|
|
9397
|
-
* be established and each block must resolve its own.
|
|
9398
|
-
*
|
|
9399
|
-
* Naming a version to `api.at` is what removes two of the four RPCs a block scan costs, and it
|
|
9400
|
-
* is only sound while the version is actually the block's. Getting that wrong is not a loud
|
|
9401
|
-
* failure: events decoded against the wrong metadata come back as a shape the scan does not
|
|
9402
|
-
* recognise, so the block reads as carrying no phantom orders and the cursor advances past it —
|
|
9403
|
-
* exactly the silent miss the block cursor exists to rule out.
|
|
9404
|
-
*
|
|
9405
|
-
* So the version is read fresh each tick and only used when it matches the previous reading.
|
|
9406
|
-
* `specVersion` only ever increases, and this read happens *after* the head read, so two equal
|
|
9407
|
-
* readings mean no upgrade landed anywhere in between — and therefore none in the range about to
|
|
9408
|
-
* be scanned. A reading that differs means an upgrade landed inside the range: that tick falls
|
|
9409
|
-
* back to per-block resolution, which is exact, and the version is used from the next tick on
|
|
9410
|
-
* once it has been seen twice.
|
|
9411
|
-
*
|
|
9412
|
-
* The gap this leaves is a backlog reaching back past an upgrade, whose oldest blocks predate
|
|
9413
|
-
* even the previous reading. Recovering from an outage that long means those bid windows closed
|
|
9414
|
-
* many upgrades ago, so nothing is lost that was still winnable.
|
|
9415
|
-
*
|
|
9416
|
-
* A version that cannot be read at all yields `undefined` rather than an error: the scan is
|
|
9417
|
-
* about to make the same request against the same endpoint and is the better place to report it.
|
|
9418
|
-
*/
|
|
9419
|
-
async confirmedRuntimeVersion() {
|
|
9420
|
-
let api;
|
|
9421
|
-
let current;
|
|
9422
|
-
try {
|
|
9423
|
-
api = await this.http();
|
|
9424
|
-
current = await api.rpc.state.getRuntimeVersion();
|
|
9425
|
-
} catch {
|
|
9426
|
-
return void 0;
|
|
9427
|
-
}
|
|
9428
|
-
const previous = this.lastRuntimeVersion ?? api.runtimeVersion;
|
|
9429
|
-
this.lastRuntimeVersion = current;
|
|
9430
|
-
if (!previous?.specVersion || !previous?.specName) return void 0;
|
|
9431
|
-
return current.specVersion.eq(previous.specVersion) && current.specName.eq(previous.specName) ? current : void 0;
|
|
9432
|
-
}
|
|
9433
|
-
/**
|
|
9434
|
-
* The poll cadence for the runtime this instance is connected to: Gargantua polls every block,
|
|
9435
|
-
* everything else every 15s. Falls back to the slower cadence if the runtime cannot be read,
|
|
9436
|
-
* since an unreachable node is the poll's problem to report, not the cadence lookup's.
|
|
9437
|
-
*/
|
|
9438
|
-
async phantomPollIntervalMs() {
|
|
9439
|
-
try {
|
|
9440
|
-
const specName = (await this.http()).runtimeVersion.specName.toString();
|
|
9441
|
-
return specName === "gargantua" ? GARGANTUA_PHANTOM_POLL_INTERVAL_MS : PHANTOM_POLL_INTERVAL_MS;
|
|
9442
|
-
} catch {
|
|
9443
|
-
return PHANTOM_POLL_INTERVAL_MS;
|
|
9444
|
-
}
|
|
9445
|
-
}
|
|
9446
|
-
};
|
|
9447
|
-
var TronChain = class _TronChain {
|
|
9448
|
-
constructor(params, evm) {
|
|
9449
|
-
this.params = params;
|
|
9450
|
-
this.evm = evm;
|
|
9451
|
-
this.tronWebInstance = new TronWeb({ fullHost: params.rpcUrl });
|
|
9452
|
-
}
|
|
9453
|
-
params;
|
|
9454
|
-
evm;
|
|
9455
|
-
tronWebInstance;
|
|
9456
|
-
/**
|
|
9457
|
-
* Creates a `TronChain` instance from a fully-specified config object.
|
|
9458
|
-
*
|
|
9459
|
-
* This is the only public way to construct a `TronChain` — the constructor is private.
|
|
9460
|
-
*
|
|
9461
|
-
* @param params - Tron chain configuration
|
|
9462
|
-
* @returns A `TronChain` instance
|
|
9463
|
-
*/
|
|
9464
|
-
static async fromParams(params) {
|
|
9465
|
-
const evm = await EvmChain.create(params.rpcUrl, params.bundlerUrl);
|
|
9466
|
-
return new _TronChain(params, evm);
|
|
9175
|
+
static async fromParams(params) {
|
|
9176
|
+
const evm = await EvmChain.create(params.rpcUrl, params.bundlerUrl);
|
|
9177
|
+
return new _TronChain(params, evm);
|
|
9467
9178
|
}
|
|
9468
9179
|
// -------------------------------------------------------------------------
|
|
9469
9180
|
// Public accessors
|
|
@@ -10526,8 +10237,8 @@ var PharosChain = class _PharosChain {
|
|
|
10526
10237
|
};
|
|
10527
10238
|
async function generateRootWithProof(postRequest, treeSize) {
|
|
10528
10239
|
const { generate_root_with_proof: generate_root_with_proof2 } = await load_ckb_mmr();
|
|
10529
|
-
const { commitment: hash, encodePacked:
|
|
10530
|
-
const result = JSON.parse(generate_root_with_proof2(hexToBytes(
|
|
10240
|
+
const { commitment: hash, encodePacked: encodePacked5 } = postRequestCommitment(postRequest);
|
|
10241
|
+
const result = JSON.parse(generate_root_with_proof2(hexToBytes(encodePacked5), treeSize));
|
|
10531
10242
|
const { root, proof, mmr_size, keccak_hash_calldata } = result;
|
|
10532
10243
|
if (keccak_hash_calldata !== hash) {
|
|
10533
10244
|
console.log("keccak_hash", keccak_hash_calldata);
|
|
@@ -10744,7 +10455,8 @@ async function retryPromise(operation, retryConfig) {
|
|
|
10744
10455
|
return await operation();
|
|
10745
10456
|
} catch (error) {
|
|
10746
10457
|
if (shouldRetry && !shouldRetry(error)) throw error;
|
|
10747
|
-
|
|
10458
|
+
const detail = error instanceof Error ? error.message.split("\n")[0] : String(error);
|
|
10459
|
+
logger.trace(`Retrying(${i}) > ${logMessage}: ${detail.slice(0, 200)}`);
|
|
10748
10460
|
lastError = error;
|
|
10749
10461
|
if (i === retryConfig.maxRetries - 1) break;
|
|
10750
10462
|
await new Promise((resolve) => setTimeout(resolve, retryConfig.backoffMs * 2 ** i));
|
|
@@ -12790,92 +12502,6 @@ query OrderStatus($commitment: String!) {
|
|
|
12790
12502
|
}
|
|
12791
12503
|
}
|
|
12792
12504
|
}`;
|
|
12793
|
-
var LATEST_PHANTOM_ORDER_PRICE_SNAPSHOT = `
|
|
12794
|
-
query LatestPhantomOrderPriceSnapshot($tokenA: String!, $tokenB: String!) {
|
|
12795
|
-
phantomOrderPriceSnapshots(
|
|
12796
|
-
filter: {
|
|
12797
|
-
and: [
|
|
12798
|
-
{ tokenA: { equalTo: $tokenA } }
|
|
12799
|
-
{ tokenB: { equalTo: $tokenB } }
|
|
12800
|
-
{ medianPrice: { isNull: false } }
|
|
12801
|
-
]
|
|
12802
|
-
}
|
|
12803
|
-
orderBy: BLOCK_NUMBER_DESC
|
|
12804
|
-
first: 1
|
|
12805
|
-
) {
|
|
12806
|
-
nodes {
|
|
12807
|
-
commitment
|
|
12808
|
-
tokenA
|
|
12809
|
-
tokenB
|
|
12810
|
-
standardAmount
|
|
12811
|
-
blockNumber
|
|
12812
|
-
medianPrice
|
|
12813
|
-
lowestPrice
|
|
12814
|
-
highestPrice
|
|
12815
|
-
bidCount
|
|
12816
|
-
snapshotTime
|
|
12817
|
-
}
|
|
12818
|
-
}
|
|
12819
|
-
}`;
|
|
12820
|
-
var AVAILABLE_LIQUIDITY = `
|
|
12821
|
-
query AvailableLiquidity(
|
|
12822
|
-
$poolId: String!
|
|
12823
|
-
$sourceChain: String!
|
|
12824
|
-
$destinationChain: String!
|
|
12825
|
-
$direction: String!
|
|
12826
|
-
) {
|
|
12827
|
-
poolChainLiquidities(
|
|
12828
|
-
filter: {
|
|
12829
|
-
and: [
|
|
12830
|
-
{ poolId: { equalToInsensitive: $poolId } }
|
|
12831
|
-
{ chain: { equalTo: $destinationChain } }
|
|
12832
|
-
{ direction: { equalTo: $direction } }
|
|
12833
|
-
]
|
|
12834
|
-
}
|
|
12835
|
-
first: 1
|
|
12836
|
-
) {
|
|
12837
|
-
nodes {
|
|
12838
|
-
depth
|
|
12839
|
-
bidCount
|
|
12840
|
-
unrestrictedDepth
|
|
12841
|
-
unrestrictedBidCount
|
|
12842
|
-
lastUpdatedAt
|
|
12843
|
-
}
|
|
12844
|
-
}
|
|
12845
|
-
poolRoutes(
|
|
12846
|
-
filter: {
|
|
12847
|
-
and: [
|
|
12848
|
-
{ poolId: { equalToInsensitive: $poolId } }
|
|
12849
|
-
{ sourceChain: { equalTo: $sourceChain } }
|
|
12850
|
-
{ chain: { equalTo: $destinationChain } }
|
|
12851
|
-
{ direction: { equalTo: $direction } }
|
|
12852
|
-
]
|
|
12853
|
-
}
|
|
12854
|
-
first: 1
|
|
12855
|
-
) {
|
|
12856
|
-
nodes {
|
|
12857
|
-
depth
|
|
12858
|
-
bidCount
|
|
12859
|
-
lastUpdatedAt
|
|
12860
|
-
}
|
|
12861
|
-
}
|
|
12862
|
-
}`;
|
|
12863
|
-
var BUY_AND_SELL_RATES = `
|
|
12864
|
-
query GetLiquidityPoolRate($poolId: String!) {
|
|
12865
|
-
liquidityPools(
|
|
12866
|
-
first: 1
|
|
12867
|
-
filter: { id: { equalToInsensitive: $poolId } }
|
|
12868
|
-
) {
|
|
12869
|
-
nodes {
|
|
12870
|
-
id
|
|
12871
|
-
token0Symbol
|
|
12872
|
-
token1Symbol
|
|
12873
|
-
sellRate
|
|
12874
|
-
buyRate
|
|
12875
|
-
lastUpdatedAt
|
|
12876
|
-
}
|
|
12877
|
-
}
|
|
12878
|
-
}`;
|
|
12879
12505
|
var TOKEN_GATEWAY_ASSET_TELEPORTED_STATUS = `
|
|
12880
12506
|
query TokenGatewayAssetTeleportedStatus($commitment: String!) {
|
|
12881
12507
|
tokenGatewayAssetTeleportedV2s(
|
|
@@ -12937,29 +12563,6 @@ async function queryAssetTeleported(params) {
|
|
|
12937
12563
|
function queryGetRequest(params) {
|
|
12938
12564
|
return _queryGetRequestInternal(params);
|
|
12939
12565
|
}
|
|
12940
|
-
async function _queryLatestPhantomOrderPriceSnapshot(params) {
|
|
12941
|
-
const response = await params.queryClient.request(
|
|
12942
|
-
LATEST_PHANTOM_ORDER_PRICE_SNAPSHOT,
|
|
12943
|
-
{
|
|
12944
|
-
tokenA: params.tokenA.toLowerCase(),
|
|
12945
|
-
tokenB: params.tokenB.toLowerCase()
|
|
12946
|
-
}
|
|
12947
|
-
);
|
|
12948
|
-
const snapshot = response.phantomOrderPriceSnapshots.nodes[0];
|
|
12949
|
-
if (!snapshot?.medianPrice) return;
|
|
12950
|
-
return {
|
|
12951
|
-
commitment: snapshot.commitment,
|
|
12952
|
-
tokenA: snapshot.tokenA,
|
|
12953
|
-
tokenB: snapshot.tokenB,
|
|
12954
|
-
standardAmount: BigInt(snapshot.standardAmount),
|
|
12955
|
-
blockNumber: BigInt(snapshot.blockNumber),
|
|
12956
|
-
medianPrice: BigInt(snapshot.medianPrice),
|
|
12957
|
-
lowestPrice: snapshot.lowestPrice === null ? void 0 : BigInt(snapshot.lowestPrice),
|
|
12958
|
-
highestPrice: snapshot.highestPrice === null ? void 0 : BigInt(snapshot.highestPrice),
|
|
12959
|
-
bidCount: snapshot.bidCount,
|
|
12960
|
-
snapshotTime: new Date(snapshot.snapshotTime)
|
|
12961
|
-
};
|
|
12962
|
-
}
|
|
12963
12566
|
async function _queryTokenGatewayAssetTeleportedInternal(params) {
|
|
12964
12567
|
const { commitmentHash, queryClient: client, logger = DEFAULT_LOGGER } = params;
|
|
12965
12568
|
const response = await retryPromise(
|
|
@@ -16243,18 +15846,39 @@ var CryptoUtils = class _CryptoUtils {
|
|
|
16243
15846
|
}
|
|
16244
15847
|
/**
|
|
16245
15848
|
* Derives the ERC-4337 nonce key that binds a bid UserOperation to its
|
|
16246
|
-
* order
|
|
16247
|
-
* `keccak256(commitment ‖ sessionKey)`. `SolverAccount`
|
|
16248
|
-
* operations whose nonce key differs — this is what lets the
|
|
16249
|
-
* the plain userOpHash while staying committed to the order and
|
|
16250
|
-
* session key it bid against.
|
|
15849
|
+
* order, session key and calldata: the lower 192 bits of
|
|
15850
|
+
* `keccak256(commitment ‖ sessionKey ‖ keccak256(callData))`. `SolverAccount`
|
|
15851
|
+
* rejects bid operations whose nonce key differs — this is what lets the
|
|
15852
|
+
* solver sign the plain userOpHash while staying committed to the order and
|
|
15853
|
+
* to the session key it bid against.
|
|
15854
|
+
*
|
|
15855
|
+
* The calldata gives every bid its own key. A solver bidding several prices
|
|
15856
|
+
* on one order signs one op per price, and each is sequence 0 of its own key,
|
|
15857
|
+
* so they execute independently rather than in a sequence where one bid that
|
|
15858
|
+
* is never selected blocks the rest.
|
|
16251
15859
|
*
|
|
16252
15860
|
* @param commitment - The order commitment (`order.id`).
|
|
16253
15861
|
* @param sessionKey - The order's session key address (`order.session`).
|
|
15862
|
+
* @param callData - The UserOperation's `callData`.
|
|
16254
15863
|
* @returns The 192-bit nonce key as a bigint (pass to `EntryPoint.getNonce`).
|
|
16255
15864
|
*/
|
|
16256
|
-
static bidNonceKey(commitment, sessionKey) {
|
|
16257
|
-
return BigInt(
|
|
15865
|
+
static bidNonceKey(commitment, sessionKey, callData) {
|
|
15866
|
+
return BigInt(
|
|
15867
|
+
keccak256(
|
|
15868
|
+
encodePacked(["bytes32", "address", "bytes32"], [commitment, sessionKey, keccak256(callData)])
|
|
15869
|
+
)
|
|
15870
|
+
) & (1n << 192n) - 1n;
|
|
15871
|
+
}
|
|
15872
|
+
/**
|
|
15873
|
+
* The identifier Hyperbridge files a bid under: `keccak256` of the UserOp's
|
|
15874
|
+
* `callData`. It is the same hash {@link bidNonceKey} takes, so a bid's
|
|
15875
|
+
* identifier and its nonce key both follow from the op itself, and one
|
|
15876
|
+
* solver's bids on one order are kept apart for the same reason on both sides.
|
|
15877
|
+
*
|
|
15878
|
+
* @param callData - The UserOperation's `callData`.
|
|
15879
|
+
*/
|
|
15880
|
+
static bidId(callData) {
|
|
15881
|
+
return keccak256(callData);
|
|
16258
15882
|
}
|
|
16259
15883
|
/**
|
|
16260
15884
|
* Builds the EIP-712 typed-data payload whose digest is the EntryPoint v0.8
|
|
@@ -16813,9 +16437,98 @@ var OrderPlacer = class {
|
|
|
16813
16437
|
return { order: finalizedOrder, receipt };
|
|
16814
16438
|
}
|
|
16815
16439
|
};
|
|
16440
|
+
var TOKEN_KEYED_GATEWAY_ABI = parseAbi([
|
|
16441
|
+
"function _orders(bytes32 commitment, address token) view returns (uint256)",
|
|
16442
|
+
"function _partialFills(bytes32 commitment, bytes32 token) view returns (uint256)"
|
|
16443
|
+
]);
|
|
16444
|
+
var keyingByClient = /* @__PURE__ */ new WeakMap();
|
|
16445
|
+
async function readLegEscrow(client, gateway, commitment, index, token) {
|
|
16446
|
+
return readKeyed(
|
|
16447
|
+
client,
|
|
16448
|
+
gateway,
|
|
16449
|
+
(keying) => keying === "leg" ? client.readContract({
|
|
16450
|
+
address: gateway,
|
|
16451
|
+
abi: ABI3,
|
|
16452
|
+
functionName: "_orders",
|
|
16453
|
+
args: [commitment, BigInt(index)]
|
|
16454
|
+
}) : client.readContract({
|
|
16455
|
+
address: gateway,
|
|
16456
|
+
abi: TOKEN_KEYED_GATEWAY_ABI,
|
|
16457
|
+
functionName: "_orders",
|
|
16458
|
+
args: [commitment, bytes32ToBytes20(token)]
|
|
16459
|
+
})
|
|
16460
|
+
);
|
|
16461
|
+
}
|
|
16462
|
+
async function readLegPartialFill(client, gateway, commitment, index, token) {
|
|
16463
|
+
return readKeyed(
|
|
16464
|
+
client,
|
|
16465
|
+
gateway,
|
|
16466
|
+
(keying) => keying === "leg" ? client.readContract({
|
|
16467
|
+
address: gateway,
|
|
16468
|
+
abi: ABI3,
|
|
16469
|
+
functionName: "_partialFills",
|
|
16470
|
+
args: [commitment, BigInt(index)]
|
|
16471
|
+
}) : client.readContract({
|
|
16472
|
+
address: gateway,
|
|
16473
|
+
abi: TOKEN_KEYED_GATEWAY_ABI,
|
|
16474
|
+
functionName: "_partialFills",
|
|
16475
|
+
args: [commitment, token]
|
|
16476
|
+
})
|
|
16477
|
+
);
|
|
16478
|
+
}
|
|
16479
|
+
async function readKeyed(client, gateway, read) {
|
|
16480
|
+
let known = keyingByClient.get(client);
|
|
16481
|
+
if (!known) {
|
|
16482
|
+
known = /* @__PURE__ */ new Map();
|
|
16483
|
+
keyingByClient.set(client, known);
|
|
16484
|
+
}
|
|
16485
|
+
const key = gateway.toLowerCase();
|
|
16486
|
+
const first = known.get(key) ?? "leg";
|
|
16487
|
+
try {
|
|
16488
|
+
const value = await read(first);
|
|
16489
|
+
known.set(key, first);
|
|
16490
|
+
return value;
|
|
16491
|
+
} catch (error) {
|
|
16492
|
+
if (!isRevert(error)) throw error;
|
|
16493
|
+
const second = first === "leg" ? "token" : "leg";
|
|
16494
|
+
try {
|
|
16495
|
+
const value = await read(second);
|
|
16496
|
+
known.set(key, second);
|
|
16497
|
+
return value;
|
|
16498
|
+
} catch (secondError) {
|
|
16499
|
+
throw isRevert(secondError) ? error : secondError;
|
|
16500
|
+
}
|
|
16501
|
+
}
|
|
16502
|
+
}
|
|
16503
|
+
function isRevert(error) {
|
|
16504
|
+
let current = error;
|
|
16505
|
+
while (current && typeof current === "object") {
|
|
16506
|
+
const item = current;
|
|
16507
|
+
if (item.name === "ContractFunctionZeroDataError") return true;
|
|
16508
|
+
if (item.code === 3) return true;
|
|
16509
|
+
if (!item.cause || typeof item.cause !== "object") {
|
|
16510
|
+
return /^(?:execution reverted\b|VM Exception while processing transaction:\s*revert\b|function selector was not recognized\b)/i.test(
|
|
16511
|
+
item.message ?? ""
|
|
16512
|
+
);
|
|
16513
|
+
}
|
|
16514
|
+
current = item.cause;
|
|
16515
|
+
}
|
|
16516
|
+
return false;
|
|
16517
|
+
}
|
|
16518
|
+
var PARTIAL_FILLS_SLOT = 11n;
|
|
16519
|
+
function partialFillSlot(commitment, index) {
|
|
16520
|
+
const inner = keccak256(encodePacked(["bytes32", "uint256"], [commitment, PARTIAL_FILLS_SLOT]));
|
|
16521
|
+
return keccak256(encodePacked(["uint256", "bytes32"], [BigInt(index), inner]));
|
|
16522
|
+
}
|
|
16816
16523
|
|
|
16817
16524
|
// src/protocols/intents/OrderExecutor.ts
|
|
16818
16525
|
var USED_USEROPS_STORAGE_KEY = (commitment) => `used-userops:${commitment.toLowerCase()}`;
|
|
16526
|
+
function remainingAfter(targets, filled) {
|
|
16527
|
+
return targets.map((target, index) => {
|
|
16528
|
+
const credited = filled[index]?.amount ?? 0n;
|
|
16529
|
+
return { token: target.token, amount: credited >= target.amount ? 0n : target.amount - credited };
|
|
16530
|
+
});
|
|
16531
|
+
}
|
|
16819
16532
|
var OrderExecutor = class {
|
|
16820
16533
|
constructor(ctx, bidManager) {
|
|
16821
16534
|
this.ctx = ctx;
|
|
@@ -16865,6 +16578,38 @@ var OrderExecutor = class {
|
|
|
16865
16578
|
JSON.stringify([...usedUserOps])
|
|
16866
16579
|
);
|
|
16867
16580
|
}
|
|
16581
|
+
/**
|
|
16582
|
+
* Reads the order's state on its destination chain into `totalFilledAssets`: the output credited
|
|
16583
|
+
* per leg from `_partialFills`, never below what is already counted, so a lagging RPC node cannot
|
|
16584
|
+
* take back a fill this executor has seen. Other solvers can fill the order too, so the chain, not
|
|
16585
|
+
* this executor's own fills, says how much is left.
|
|
16586
|
+
*
|
|
16587
|
+
* Returns the terminal update once `_filled` is set: `FILLED` when every leg is complete,
|
|
16588
|
+
* `CANCELLED` otherwise. `_filled` is read first because progress cannot change after it is set,
|
|
16589
|
+
* so a finalized order is never paired with progress from before its last fill.
|
|
16590
|
+
*/
|
|
16591
|
+
async syncWithDestination(order, commitment, targetAssets, totalFilledAssets) {
|
|
16592
|
+
const client = this.ctx.dest.client;
|
|
16593
|
+
const gateway = this.ctx.dest.configService.getIntentGatewayAddress(normalizeStateMachineId(order.destination));
|
|
16594
|
+
const finalizer = await client.readContract({
|
|
16595
|
+
address: gateway,
|
|
16596
|
+
abi: ABI3,
|
|
16597
|
+
functionName: "_filled",
|
|
16598
|
+
args: [commitment]
|
|
16599
|
+
});
|
|
16600
|
+
const filled = await Promise.all(
|
|
16601
|
+
order.output.assets.map(async (asset, index) => {
|
|
16602
|
+
const credited = await readLegPartialFill(client, gateway, commitment, index, asset.token);
|
|
16603
|
+
const counted = totalFilledAssets[index]?.amount ?? 0n;
|
|
16604
|
+
return { token: asset.token, amount: credited > counted ? credited : counted };
|
|
16605
|
+
})
|
|
16606
|
+
);
|
|
16607
|
+
const remainingAssets = remainingAfter(targetAssets, filled);
|
|
16608
|
+
if (/^0x0{40}$/i.test(finalizer)) return { totalFilledAssets: filled, remainingAssets };
|
|
16609
|
+
const progress = { commitment, totalFilledAssets: filled, remainingAssets };
|
|
16610
|
+
const final = remainingAssets.every((asset) => asset.amount === 0n) ? { status: "FILLED", selectedSolver: finalizer, ...progress } : { status: "CANCELLED", ...progress };
|
|
16611
|
+
return { totalFilledAssets: filled, remainingAssets, final };
|
|
16612
|
+
}
|
|
16868
16613
|
/**
|
|
16869
16614
|
* Creates a closure that computes the deduplication hash key for a
|
|
16870
16615
|
* UserOperation, pre-bound to the order's destination chain and entry point.
|
|
@@ -16923,12 +16668,12 @@ var OrderExecutor = class {
|
|
|
16923
16668
|
}
|
|
16924
16669
|
if (result.fillStatus === "partial") {
|
|
16925
16670
|
const filledAssets = result.filledAssets ?? [];
|
|
16926
|
-
totalFilledAssets = totalFilledAssets.map((a) => {
|
|
16927
|
-
const filled = filledAssets
|
|
16671
|
+
totalFilledAssets = totalFilledAssets.map((a, index) => {
|
|
16672
|
+
const filled = filledAssets[index];
|
|
16928
16673
|
return filled ? { token: a.token, amount: a.amount + filled.amount } : { ...a };
|
|
16929
16674
|
});
|
|
16930
|
-
remainingAssets = targetAssets.map((target) => {
|
|
16931
|
-
const filled = totalFilledAssets
|
|
16675
|
+
remainingAssets = targetAssets.map((target, index) => {
|
|
16676
|
+
const filled = totalFilledAssets[index];
|
|
16932
16677
|
const filledAmt = filled?.amount ?? 0n;
|
|
16933
16678
|
return {
|
|
16934
16679
|
token: target.token,
|
|
@@ -16994,8 +16739,27 @@ var OrderExecutor = class {
|
|
|
16994
16739
|
const usedUserOps = await this.loadUsedUserOps(commitment);
|
|
16995
16740
|
const userOpHashKey = this.createUserOpHasher(order);
|
|
16996
16741
|
const targetAssets = order.output.assets.map((a) => ({ token: a.token, amount: a.amount }));
|
|
16997
|
-
let
|
|
16998
|
-
|
|
16742
|
+
let initial;
|
|
16743
|
+
try {
|
|
16744
|
+
initial = await this.syncWithDestination(
|
|
16745
|
+
order,
|
|
16746
|
+
commitment,
|
|
16747
|
+
targetAssets,
|
|
16748
|
+
order.output.assets.map((a) => ({ token: a.token, amount: 0n }))
|
|
16749
|
+
);
|
|
16750
|
+
} catch (err) {
|
|
16751
|
+
yield {
|
|
16752
|
+
status: "FAILED",
|
|
16753
|
+
commitment,
|
|
16754
|
+
error: `Could not read the order on its destination chain: ${err instanceof Error ? err.message : String(err)}`
|
|
16755
|
+
};
|
|
16756
|
+
return;
|
|
16757
|
+
}
|
|
16758
|
+
if (initial.final) {
|
|
16759
|
+
yield initial.final;
|
|
16760
|
+
return;
|
|
16761
|
+
}
|
|
16762
|
+
const { totalFilledAssets, remainingAssets } = initial;
|
|
16999
16763
|
const executionStream = this.executionStream({
|
|
17000
16764
|
order,
|
|
17001
16765
|
sessionPrivateKey,
|
|
@@ -17027,7 +16791,7 @@ var OrderExecutor = class {
|
|
|
17027
16791
|
if (done) return;
|
|
17028
16792
|
const fed = yield value;
|
|
17029
16793
|
if (value.status === "BIDS_RECEIVED") input = fed;
|
|
17030
|
-
if (value.status === "EXPIRED" || value.status === "FILLED") return;
|
|
16794
|
+
if (value.status === "EXPIRED" || value.status === "FILLED" || value.status === "CANCELLED") return;
|
|
17031
16795
|
}
|
|
17032
16796
|
} finally {
|
|
17033
16797
|
console.log(`[OrderExecutor] Tearing down streams for commitment=${commitment}`);
|
|
@@ -17086,18 +16850,29 @@ var OrderExecutor = class {
|
|
|
17086
16850
|
}
|
|
17087
16851
|
while (true) {
|
|
17088
16852
|
let freshBids;
|
|
16853
|
+
let final;
|
|
17089
16854
|
try {
|
|
17090
|
-
|
|
17091
|
-
|
|
16855
|
+
;
|
|
16856
|
+
({ totalFilledAssets, remainingAssets, final } = await this.syncWithDestination(
|
|
16857
|
+
order,
|
|
16858
|
+
commitment,
|
|
16859
|
+
targetAssets,
|
|
16860
|
+
totalFilledAssets
|
|
16861
|
+
));
|
|
16862
|
+
freshBids = final ? [] : (await this.fetchBids({ commitment, solver, solverLockStartTime })).filter(isFreshBid);
|
|
17092
16863
|
} catch {
|
|
17093
16864
|
await sleep(pollIntervalMs);
|
|
17094
16865
|
continue;
|
|
17095
16866
|
}
|
|
16867
|
+
if (final) {
|
|
16868
|
+
yield final;
|
|
16869
|
+
return;
|
|
16870
|
+
}
|
|
17096
16871
|
if (freshBids.length === 0) {
|
|
17097
16872
|
await sleep(pollIntervalMs);
|
|
17098
16873
|
continue;
|
|
17099
16874
|
}
|
|
17100
|
-
const bids = this.bidManager.buildBids(order, freshBids, sessionPrivateKey);
|
|
16875
|
+
const bids = this.bidManager.buildBids(order, freshBids, sessionPrivateKey).filter((bid) => servesOpenLeg(bid, remainingAssets));
|
|
17101
16876
|
if (bids.length === 0) {
|
|
17102
16877
|
await sleep(pollIntervalMs);
|
|
17103
16878
|
continue;
|
|
@@ -17140,6 +16915,10 @@ var OrderExecutor = class {
|
|
|
17140
16915
|
}
|
|
17141
16916
|
}
|
|
17142
16917
|
};
|
|
16918
|
+
function servesOpenLeg(bid, remainingAssets) {
|
|
16919
|
+
if (bid.outputs.length !== remainingAssets.length) return true;
|
|
16920
|
+
return bid.outputs.some((output, leg) => output.amount > 0n && remainingAssets[leg].amount > 0n);
|
|
16921
|
+
}
|
|
17143
16922
|
var OrderCanceller = class _OrderCanceller {
|
|
17144
16923
|
/**
|
|
17145
16924
|
* @param ctx - Shared IntentsV2 context providing the source and destination
|
|
@@ -17178,8 +16957,9 @@ var OrderCanceller = class _OrderCanceller {
|
|
|
17178
16957
|
/**
|
|
17179
16958
|
* Quotes the native token cost of a source-initiated cross-chain cancellation.
|
|
17180
16959
|
*
|
|
17181
|
-
* Constructs
|
|
17182
|
-
* calls `quoteNative` on the source host to
|
|
16960
|
+
* Constructs the ISMP GET request the source gateway will dispatch, one
|
|
16961
|
+
* `_partialFills` key per leg, and calls `quoteNative` on the source host to
|
|
16962
|
+
* obtain the dispatch fee.
|
|
17183
16963
|
* Returns 0 for same-chain orders (no ISMP call needed).
|
|
17184
16964
|
*
|
|
17185
16965
|
* @param order - The order to quote.
|
|
@@ -17193,21 +16973,16 @@ var OrderCanceller = class _OrderCanceller {
|
|
|
17193
16973
|
}
|
|
17194
16974
|
const height = order.deadline + 1n;
|
|
17195
16975
|
const destIntentGateway = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachine);
|
|
17196
|
-
const
|
|
17197
|
-
|
|
17198
|
-
|
|
17199
|
-
functionName: "calculateCommitmentSlotHash",
|
|
17200
|
-
args: [order.id]
|
|
17201
|
-
});
|
|
17202
|
-
const key = concatHex([destIntentGateway, slotHash]);
|
|
17203
|
-
const context = encodeWithdrawalRequest(order, order.user);
|
|
16976
|
+
const commitment = this.orderId(order);
|
|
16977
|
+
const keys = order.inputs.map((_, index) => concatHex([destIntentGateway, partialFillSlot(commitment, index)]));
|
|
16978
|
+
const context = cancelFromSourceContext(order, commitment);
|
|
17204
16979
|
const getRequest = {
|
|
17205
16980
|
source: sourceStateMachine,
|
|
17206
16981
|
dest: destStateMachine,
|
|
17207
16982
|
from: this.ctx.source.configService.getIntentGatewayAddress(destStateMachine),
|
|
17208
16983
|
nonce: await this.ctx.source.getHostNonce(),
|
|
17209
16984
|
height,
|
|
17210
|
-
keys
|
|
16985
|
+
keys,
|
|
17211
16986
|
timeoutTimestamp: 0n,
|
|
17212
16987
|
context
|
|
17213
16988
|
};
|
|
@@ -17232,10 +17007,13 @@ var OrderCanceller = class _OrderCanceller {
|
|
|
17232
17007
|
*
|
|
17233
17008
|
* @param order - The order to cancel.
|
|
17234
17009
|
* @param indexerClient - Indexer client used to stream ISMP request status
|
|
17235
|
-
* updates and query state-machine heights.
|
|
17236
|
-
*
|
|
17010
|
+
* updates and query state-machine heights. The direct same-chain route does
|
|
17011
|
+
* not access it.
|
|
17012
|
+
* @param options - Choose the initiation side. Defaults to source-side
|
|
17013
|
+
* cancellation. Same-chain orders always use the direct local route.
|
|
17237
17014
|
* @yields {@link CancelEvent} objects describing each stage of the
|
|
17238
|
-
* cancellation lifecycle.
|
|
17015
|
+
* cancellation lifecycle. The caller signs or broadcasts the yielded
|
|
17016
|
+
* transaction, so its signer may differ from `order.user` after expiry.
|
|
17239
17017
|
*/
|
|
17240
17018
|
async *cancelOrder(order, indexerClient, options = {}) {
|
|
17241
17019
|
const sourceStateMachine = normalizeStateMachineId(order.source);
|
|
@@ -17269,9 +17047,12 @@ var OrderCanceller = class _OrderCanceller {
|
|
|
17269
17047
|
* Async generator that cancels an order by initiating the cancel from the
|
|
17270
17048
|
* source chain.
|
|
17271
17049
|
*
|
|
17272
|
-
* **Same-chain path:** encodes a direct `cancelOrder` call
|
|
17273
|
-
* `AWAITING_CANCEL_TRANSACTION`,
|
|
17274
|
-
*
|
|
17050
|
+
* **Same-chain path:** encodes a direct `cancelOrder` call with zero cancel
|
|
17051
|
+
* options, yields `AWAITING_CANCEL_TRANSACTION`, accepts either a signed raw
|
|
17052
|
+
* transaction or an already-broadcast hash, and yields `CANCELLATION_COMPLETE`
|
|
17053
|
+
* after confirming the `EscrowRefunded` event. The contract authorizes the
|
|
17054
|
+
* order user through the deadline and any caller strictly after it; the SDK
|
|
17055
|
+
* preserves `order.user` in calldata so a third party cannot redirect escrow.
|
|
17275
17056
|
*
|
|
17276
17057
|
* **Cross-chain path:**
|
|
17277
17058
|
* 1. Fetches (or resumes from cache) a destination finalization proof.
|
|
@@ -17581,9 +17362,9 @@ var OrderCanceller = class _OrderCanceller {
|
|
|
17581
17362
|
}
|
|
17582
17363
|
}
|
|
17583
17364
|
/**
|
|
17584
|
-
* Polls for a finalized destination-chain state proof
|
|
17585
|
-
*
|
|
17586
|
-
*
|
|
17365
|
+
* Polls for a finalized destination-chain state proof of every leg's
|
|
17366
|
+
* `_partialFills` slot, i.e. how much of the order was filled before the
|
|
17367
|
+
* deadline.
|
|
17587
17368
|
*
|
|
17588
17369
|
* Waits until the latest Hyperbridge-tracked state-machine height exceeds
|
|
17589
17370
|
* `order.deadline` (or the last failed probe height) before attempting to
|
|
@@ -17612,14 +17393,9 @@ var OrderCanceller = class _OrderCanceller {
|
|
|
17612
17393
|
const intentGatewayV2Address = this.ctx.dest.configService.getIntentGatewayAddress(
|
|
17613
17394
|
this.ctx.dest.config.stateMachineId
|
|
17614
17395
|
);
|
|
17615
|
-
const
|
|
17616
|
-
const
|
|
17617
|
-
|
|
17618
|
-
address: intentGatewayV2Address,
|
|
17619
|
-
functionName: "calculateCommitmentSlotHash",
|
|
17620
|
-
args: [orderId]
|
|
17621
|
-
});
|
|
17622
|
-
const proofHex = await this.ctx.dest.queryStateProof(latestHeight, [slotHash], intentGatewayV2Address);
|
|
17396
|
+
const commitment = this.orderId(order);
|
|
17397
|
+
const slots = order.inputs.map((_, index) => partialFillSlot(commitment, index));
|
|
17398
|
+
const proofHex = await this.ctx.dest.queryStateProof(latestHeight, slots, intentGatewayV2Address);
|
|
17623
17399
|
const proof = {
|
|
17624
17400
|
consensusStateId: this.ctx.dest.config.consensusStateId,
|
|
17625
17401
|
height: latestHeight,
|
|
@@ -17817,118 +17593,135 @@ var OrderCanceller = class _OrderCanceller {
|
|
|
17817
17593
|
return feeInDestFeeToken * 1005n / 1000n;
|
|
17818
17594
|
}
|
|
17819
17595
|
};
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
17824
|
-
|
|
17825
|
-
outputs: [],
|
|
17826
|
-
inputs: [
|
|
17827
|
-
ABI3.find((e) => e.type === "function" && e.name === "fillOrder").inputs[0],
|
|
17596
|
+
function cancelFromSourceContext(order, commitment) {
|
|
17597
|
+
return encodeAbiParameters(
|
|
17598
|
+
[
|
|
17599
|
+
{ type: "bytes32" },
|
|
17600
|
+
{ type: "bytes32" },
|
|
17828
17601
|
{
|
|
17829
|
-
|
|
17830
|
-
type: "tuple",
|
|
17831
|
-
internalType: "struct FillOptions",
|
|
17602
|
+
type: "tuple[]",
|
|
17832
17603
|
components: [
|
|
17833
|
-
{ name: "
|
|
17834
|
-
{ name: "
|
|
17835
|
-
{
|
|
17836
|
-
name: "outputs",
|
|
17837
|
-
type: "tuple[]",
|
|
17838
|
-
internalType: "struct TokenInfo[]",
|
|
17839
|
-
components: [
|
|
17840
|
-
{ name: "token", type: "bytes32", internalType: "bytes32" },
|
|
17841
|
-
{ name: "amount", type: "uint256", internalType: "uint256" }
|
|
17842
|
-
]
|
|
17843
|
-
}
|
|
17604
|
+
{ name: "token", type: "bytes32" },
|
|
17605
|
+
{ name: "amount", type: "uint256" }
|
|
17844
17606
|
]
|
|
17845
|
-
}
|
|
17607
|
+
},
|
|
17608
|
+
{ type: "uint256[]" }
|
|
17609
|
+
],
|
|
17610
|
+
[
|
|
17611
|
+
commitment,
|
|
17612
|
+
bytes20ToBytes32(order.user),
|
|
17613
|
+
order.inputs.map((input) => ({ token: normalizeAddressForEvmBytes32(input.token), amount: input.amount })),
|
|
17614
|
+
order.output.assets.map((asset) => asset.amount)
|
|
17846
17615
|
]
|
|
17616
|
+
);
|
|
17617
|
+
}
|
|
17618
|
+
var FILL_ORDER_SELECTOR = "0x68ddf058";
|
|
17619
|
+
var SUPPORTED_INTENTS_VERSION = 3n;
|
|
17620
|
+
var CONTRACT_VERSION_ABI = [
|
|
17621
|
+
{
|
|
17622
|
+
type: "function",
|
|
17623
|
+
name: "version",
|
|
17624
|
+
stateMutability: "view",
|
|
17625
|
+
inputs: [],
|
|
17626
|
+
outputs: [{ name: "", type: "uint64" }]
|
|
17847
17627
|
}
|
|
17848
17628
|
];
|
|
17849
|
-
|
|
17850
|
-
|
|
17851
|
-
|
|
17852
|
-
|
|
17853
|
-
|
|
17854
|
-
|
|
17855
|
-
|
|
17856
|
-
var CHAINS_WITHOUT_VALID_UNTIL = /* @__PURE__ */ new Set([
|
|
17857
|
-
97,
|
|
17858
|
-
// BNB testnet
|
|
17859
|
-
10200,
|
|
17860
|
-
// Gnosis Chiado
|
|
17861
|
-
80002,
|
|
17862
|
-
// Polygon Amoy
|
|
17863
|
-
84532,
|
|
17864
|
-
// Base Sepolia
|
|
17865
|
-
421614,
|
|
17866
|
-
// Arbitrum Sepolia
|
|
17867
|
-
688689,
|
|
17868
|
-
// Pharos testnet
|
|
17869
|
-
11155111,
|
|
17870
|
-
// Sepolia
|
|
17871
|
-
11155420,
|
|
17872
|
-
// Optimism Sepolia
|
|
17873
|
-
420420417
|
|
17874
|
-
// Polkadot Hub Paseo
|
|
17875
|
-
]);
|
|
17876
|
-
var knownV2Gateways = /* @__PURE__ */ new Set();
|
|
17877
|
-
function resetFillOptionsVersionCache() {
|
|
17878
|
-
knownV2Gateways.clear();
|
|
17629
|
+
async function readContractVersion(client, address) {
|
|
17630
|
+
try {
|
|
17631
|
+
return await client.readContract({ address, abi: CONTRACT_VERSION_ABI, functionName: "version" });
|
|
17632
|
+
} catch (error) {
|
|
17633
|
+
if (isRevert(error)) return void 0;
|
|
17634
|
+
throw error;
|
|
17635
|
+
}
|
|
17879
17636
|
}
|
|
17880
|
-
async function
|
|
17881
|
-
|
|
17882
|
-
if (!slot || slot.length < 66) return gateway;
|
|
17883
|
-
const addr = `0x${slot.slice(-40)}`;
|
|
17884
|
-
return /^0x0{40}$/.test(addr) ? gateway : addr;
|
|
17637
|
+
async function supportsRateFills(client, gateway) {
|
|
17638
|
+
return await readContractVersion(client, gateway) === SUPPORTED_INTENTS_VERSION;
|
|
17885
17639
|
}
|
|
17886
|
-
async function
|
|
17887
|
-
const
|
|
17888
|
-
if (
|
|
17889
|
-
|
|
17890
|
-
|
|
17891
|
-
|
|
17892
|
-
if (LEGACY_FILL_OPTIONS_IMPLEMENTATIONS.has(implementation.toLowerCase())) return 1;
|
|
17893
|
-
knownV2Gateways.add(key);
|
|
17894
|
-
return 2;
|
|
17640
|
+
async function assertGatewayRelease(client, gateway) {
|
|
17641
|
+
const version = await readContractVersion(client, gateway);
|
|
17642
|
+
if (version === SUPPORTED_INTENTS_VERSION) return;
|
|
17643
|
+
throw new Error(
|
|
17644
|
+
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}`
|
|
17645
|
+
);
|
|
17895
17646
|
}
|
|
17896
|
-
|
|
17897
|
-
|
|
17898
|
-
|
|
17899
|
-
|
|
17900
|
-
|
|
17901
|
-
|
|
17902
|
-
|
|
17647
|
+
var CANONICAL_EVM_TOKEN = /^0x0{24}[0-9a-fA-F]{40}$/;
|
|
17648
|
+
function isCanonicalEvmToken(token) {
|
|
17649
|
+
return CANONICAL_EVM_TOKEN.test(token);
|
|
17650
|
+
}
|
|
17651
|
+
function validateFillQuotes(order, options) {
|
|
17652
|
+
const count = order.output.assets.length;
|
|
17653
|
+
if (!count || !Array.isArray(options.inputs) || options.inputs.length !== count || options.outputs.length !== count || order.inputs.length !== count) {
|
|
17654
|
+
throw new Error("Fill inputs and outputs must quote every order leg");
|
|
17655
|
+
}
|
|
17656
|
+
for (let i = 0; i < count; i++) {
|
|
17657
|
+
const input = options.inputs[i];
|
|
17658
|
+
const output = options.outputs[i];
|
|
17659
|
+
if (input.token.toLowerCase() !== order.inputs[i].token.toLowerCase() || output.token.toLowerCase() !== order.output.assets[i].token.toLowerCase() || !isCanonicalEvmToken(input.token) || !isCanonicalEvmToken(output.token)) {
|
|
17660
|
+
throw new Error("Fill quote tokens must match their order legs");
|
|
17661
|
+
}
|
|
17662
|
+
if (input.amount === 0n !== (output.amount === 0n)) {
|
|
17663
|
+
throw new Error("Fill quote input and output must both be zero or positive");
|
|
17664
|
+
}
|
|
17903
17665
|
}
|
|
17904
|
-
|
|
17666
|
+
}
|
|
17667
|
+
function encodeFillOrder(order, options) {
|
|
17668
|
+
validateFillQuotes(order, options);
|
|
17905
17669
|
return encodeFunctionData({
|
|
17906
|
-
abi:
|
|
17670
|
+
abi: ABI3,
|
|
17907
17671
|
functionName: "fillOrder",
|
|
17908
|
-
args: [order,
|
|
17672
|
+
args: [order, options]
|
|
17909
17673
|
});
|
|
17910
17674
|
}
|
|
17911
17675
|
function decodeFillOrder(data) {
|
|
17912
17676
|
try {
|
|
17913
17677
|
const decoded = decodeFunctionData({ abi: ABI3, data });
|
|
17914
|
-
if (decoded.functionName
|
|
17915
|
-
|
|
17916
|
-
|
|
17917
|
-
|
|
17918
|
-
|
|
17919
|
-
try {
|
|
17920
|
-
const decoded = decodeFunctionData({ abi: FILL_ORDER_V1_ABI, data });
|
|
17921
|
-
if (decoded.functionName === "fillOrder" && decoded.args && decoded.args.length >= 2) {
|
|
17922
|
-
const legacy = decoded.args[1];
|
|
17923
|
-
return { order: decoded.args[0], options: { ...legacy, validUntil: 0n } };
|
|
17924
|
-
}
|
|
17678
|
+
if (decoded.functionName !== "fillOrder" || !decoded.args || decoded.args.length < 2) return null;
|
|
17679
|
+
const order = decoded.args[0];
|
|
17680
|
+
const options = decoded.args[1];
|
|
17681
|
+
validateFillQuotes(order, options);
|
|
17682
|
+
return { order, options };
|
|
17925
17683
|
} catch {
|
|
17684
|
+
return null;
|
|
17926
17685
|
}
|
|
17927
|
-
return null;
|
|
17928
17686
|
}
|
|
17929
|
-
var
|
|
17930
|
-
|
|
17687
|
+
var ENTRY_POINT_EVENT_ABI = [
|
|
17688
|
+
{ type: "event", name: "BeforeExecution", inputs: [] },
|
|
17689
|
+
{
|
|
17690
|
+
type: "event",
|
|
17691
|
+
name: "UserOperationEvent",
|
|
17692
|
+
inputs: [
|
|
17693
|
+
{ name: "userOpHash", type: "bytes32", indexed: true },
|
|
17694
|
+
{ name: "sender", type: "address", indexed: true },
|
|
17695
|
+
{ name: "paymaster", type: "address", indexed: true },
|
|
17696
|
+
{ name: "nonce", type: "uint256", indexed: false },
|
|
17697
|
+
{ name: "success", type: "bool", indexed: false },
|
|
17698
|
+
{ name: "actualGasCost", type: "uint256", indexed: false },
|
|
17699
|
+
{ name: "actualGasUsed", type: "uint256", indexed: false }
|
|
17700
|
+
]
|
|
17701
|
+
}
|
|
17702
|
+
];
|
|
17703
|
+
function userOperationLogs(logs, entryPoint, userOpHash) {
|
|
17704
|
+
const [beforeExecution, userOperationEvent] = ENTRY_POINT_EVENT_ABI.map((event) => toEventSelector(event));
|
|
17705
|
+
const isBoundary = (log) => log.address.toLowerCase() === entryPoint.toLowerCase() && (log.topics[0] === beforeExecution || log.topics[0] === userOperationEvent);
|
|
17706
|
+
const end = logs.findIndex(
|
|
17707
|
+
(log) => isBoundary(log) && log.topics[0] === userOperationEvent && log.topics[1]?.toLowerCase() === userOpHash.toLowerCase()
|
|
17708
|
+
);
|
|
17709
|
+
if (end === -1) throw new Error(`UserOperation ${userOpHash} is not in the transaction`);
|
|
17710
|
+
const { args } = decodeEventLog({
|
|
17711
|
+
abi: ENTRY_POINT_EVENT_ABI,
|
|
17712
|
+
eventName: "UserOperationEvent",
|
|
17713
|
+
data: logs[end].data,
|
|
17714
|
+
topics: logs[end].topics
|
|
17715
|
+
});
|
|
17716
|
+
if (!args.success) throw new Error(`UserOperation ${userOpHash} reverted`);
|
|
17717
|
+
let start = end - 1;
|
|
17718
|
+
while (start >= 0 && !isBoundary(logs[start])) start--;
|
|
17719
|
+
return logs.slice(start + 1, end);
|
|
17720
|
+
}
|
|
17721
|
+
var BidImpl = class {
|
|
17722
|
+
solverAddress;
|
|
17931
17723
|
outputs;
|
|
17724
|
+
inputs;
|
|
17932
17725
|
relayerFee;
|
|
17933
17726
|
nativeDispatchFee;
|
|
17934
17727
|
userOp;
|
|
@@ -17951,6 +17744,7 @@ var BidImpl = class {
|
|
|
17951
17744
|
this.sessionPrivateKey = params.sessionPrivateKey;
|
|
17952
17745
|
this.solverAddress = params.fillerBid.userOp.sender;
|
|
17953
17746
|
this.outputs = params.fillOptions.outputs;
|
|
17747
|
+
this.inputs = params.fillOptions.inputs;
|
|
17954
17748
|
this.relayerFee = params.fillOptions.relayerFee;
|
|
17955
17749
|
this.nativeDispatchFee = params.fillOptions.nativeDispatchFee;
|
|
17956
17750
|
this.userOp = params.fillerBid.userOp;
|
|
@@ -18083,31 +17877,28 @@ var BidImpl = class {
|
|
|
18083
17877
|
{ maxRetries: 5, backoffMs: 2e3, logMessage: "Fetching user operation receipt" }
|
|
18084
17878
|
);
|
|
18085
17879
|
txnHash = receipt.receipt.transactionHash;
|
|
18086
|
-
|
|
18087
|
-
|
|
18088
|
-
|
|
18089
|
-
|
|
18090
|
-
|
|
18091
|
-
|
|
18092
|
-
|
|
18093
|
-
|
|
18094
|
-
|
|
18095
|
-
|
|
18096
|
-
|
|
18097
|
-
|
|
18098
|
-
|
|
18099
|
-
|
|
18100
|
-
|
|
18101
|
-
|
|
18102
|
-
|
|
18103
|
-
|
|
18104
|
-
|
|
18105
|
-
|
|
18106
|
-
|
|
18107
|
-
|
|
18108
|
-
}
|
|
18109
|
-
} catch {
|
|
18110
|
-
throw new Error("Failed to determine fill status from logs");
|
|
17880
|
+
const chainReceipt = await this.ctx.dest.client.waitForTransactionReceipt({
|
|
17881
|
+
hash: txnHash,
|
|
17882
|
+
confirmations: 1
|
|
17883
|
+
});
|
|
17884
|
+
const gateway = this.ctx.dest.configService.getIntentGatewayAddress(
|
|
17885
|
+
normalizeStateMachineId(this.order.destination)
|
|
17886
|
+
);
|
|
17887
|
+
const fills = parseEventLogs({
|
|
17888
|
+
abi: ABI3,
|
|
17889
|
+
logs: userOperationLogs(chainReceipt.logs, entryPointAddress, userOpHash),
|
|
17890
|
+
eventName: ["OrderFilled", "PartialFill"]
|
|
17891
|
+
}).filter(
|
|
17892
|
+
(event) => event.address.toLowerCase() === gateway.toLowerCase() && event.args.commitment.toLowerCase() === commitment.toLowerCase()
|
|
17893
|
+
);
|
|
17894
|
+
if (fills.some((event) => event.eventName === "OrderFilled")) {
|
|
17895
|
+
fillStatus = "full";
|
|
17896
|
+
} else if (fills.length > 0) {
|
|
17897
|
+
fillStatus = "partial";
|
|
17898
|
+
filledAssets = fills[0].args.outputs.map(({ token }, index) => ({
|
|
17899
|
+
token,
|
|
17900
|
+
amount: fills.reduce((sum, event) => sum + (event.args.outputs[index]?.amount ?? 0n), 0n)
|
|
17901
|
+
}));
|
|
18111
17902
|
}
|
|
18112
17903
|
} catch (err) {
|
|
18113
17904
|
throw new Error(`Failed to execute bid: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -18174,6 +17965,20 @@ var BidManager = class {
|
|
|
18174
17965
|
callData,
|
|
18175
17966
|
paymasterAndData = "0x"
|
|
18176
17967
|
} = options;
|
|
17968
|
+
const fills = (this.crypto.decodeERC7821Execute(callData) ?? []).filter(
|
|
17969
|
+
(call) => call.data.slice(0, 10).toLowerCase() === FILL_ORDER_SELECTOR
|
|
17970
|
+
);
|
|
17971
|
+
if (fills.length === 0) throw new Error("Bid calldata carries no fillOrder call");
|
|
17972
|
+
for (const call of fills) {
|
|
17973
|
+
if (!decodeFillOrder(call.data)) {
|
|
17974
|
+
throw new Error("Malformed fill quote; every leg requires inputs and outputs");
|
|
17975
|
+
}
|
|
17976
|
+
}
|
|
17977
|
+
const chain = normalizeStateMachineId(order.destination);
|
|
17978
|
+
const gateway = this.ctx.dest.configService.getIntentGatewayAddress(chain);
|
|
17979
|
+
if (!await supportsRateFills(this.ctx.dest.client, gateway)) {
|
|
17980
|
+
throw new Error("Fills are not supported by the destination gateway");
|
|
17981
|
+
}
|
|
18177
17982
|
const chainId = BigInt(
|
|
18178
17983
|
this.ctx.dest.client.chain?.id ?? Number.parseInt(this.ctx.dest.config.stateMachineId.split("-")[1])
|
|
18179
17984
|
);
|
|
@@ -18191,10 +17996,10 @@ var BidManager = class {
|
|
|
18191
17996
|
signature: "0x"
|
|
18192
17997
|
};
|
|
18193
17998
|
const nonceKey = BigInt(nonce) >> 64n;
|
|
18194
|
-
const expectedKey = CryptoUtils.bidNonceKey(order.id, order.session);
|
|
17999
|
+
const expectedKey = CryptoUtils.bidNonceKey(order.id, order.session, callData);
|
|
18195
18000
|
if (nonceKey !== expectedKey) {
|
|
18196
18001
|
console.warn(
|
|
18197
|
-
`[BidManager] bid nonce key does not bind the order commitment
|
|
18002
|
+
`[BidManager] bid nonce key does not bind the order commitment, session key and calldata; on-chain validation will fail (order=${order.id})`
|
|
18198
18003
|
);
|
|
18199
18004
|
}
|
|
18200
18005
|
const solverSignature = await solverSigner.signTypedData(
|
|
@@ -18259,7 +18064,7 @@ var BidManager = class {
|
|
|
18259
18064
|
return this.selectAndExecuteBest(order, this.buildBids(order, bids, sessionPrivateKey));
|
|
18260
18065
|
}
|
|
18261
18066
|
/**
|
|
18262
|
-
* Autopilot bid selection: sorts the given bids by
|
|
18067
|
+
* Autopilot bid selection: sorts the given bids by rate, simulates
|
|
18263
18068
|
* each in order and attempts execution. Any bid that fails simulation or
|
|
18264
18069
|
* execution is skipped once so the next ranked bid can be attempted in the
|
|
18265
18070
|
* same round. For consumers that do not need custom selection logic.
|
|
@@ -18314,35 +18119,58 @@ var BidManager = class {
|
|
|
18314
18119
|
throw new Error("No bids passed simulation and execution");
|
|
18315
18120
|
}
|
|
18316
18121
|
/**
|
|
18317
|
-
* Sorts
|
|
18122
|
+
* Sorts bids by rate, best to worst, independently of how much each one fills.
|
|
18318
18123
|
*
|
|
18319
|
-
*
|
|
18320
|
-
*
|
|
18321
|
-
*
|
|
18322
|
-
*
|
|
18323
|
-
* - Mixed outputs: sort by DEX-quoted USD value descending, with a raw-amount
|
|
18324
|
-
* fallback if pricing fails.
|
|
18124
|
+
* A leg's rate is what the bid pays out for what it takes: `output / input` for
|
|
18125
|
+
* that leg. Every leg of an order trades the same pair, so a bid's rate is its
|
|
18126
|
+
* total output over its total input across the legs it quotes. Rates are
|
|
18127
|
+
* compared exactly, by cross-multiplication; equal rates keep arrival order.
|
|
18325
18128
|
*
|
|
18326
|
-
*
|
|
18129
|
+
* A bid is dropped when it does not quote the order's legs one for one, names
|
|
18130
|
+
* another token on a leg, quotes an input without an output (or the reverse),
|
|
18131
|
+
* quotes no leg at all, or quotes a leg below the rate the order requires.
|
|
18327
18132
|
*
|
|
18328
|
-
* @param order - The placed order
|
|
18133
|
+
* @param order - The placed order the bids compete to fill.
|
|
18329
18134
|
* @param bids - Executable bids to sort (from {@link buildBids}).
|
|
18330
|
-
* @returns
|
|
18135
|
+
* @returns The valid bids, best rate first, ready for simulation.
|
|
18331
18136
|
*/
|
|
18332
18137
|
async sortBids(order, bids) {
|
|
18333
|
-
const
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
|
|
18338
|
-
|
|
18339
|
-
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18138
|
+
const legs = order.inputs.length;
|
|
18139
|
+
const ranked = [];
|
|
18140
|
+
for (const [index, bid] of bids.entries()) {
|
|
18141
|
+
if (bid.inputs.length !== legs || bid.outputs.length !== legs || order.output.assets.length !== legs) {
|
|
18142
|
+
console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: does not quote the order's legs`);
|
|
18143
|
+
continue;
|
|
18144
|
+
}
|
|
18145
|
+
let input = 0n;
|
|
18146
|
+
let output = 0n;
|
|
18147
|
+
let reason = "";
|
|
18148
|
+
for (let leg = 0; leg < legs && !reason; leg++) {
|
|
18149
|
+
const take = bid.inputs[leg];
|
|
18150
|
+
const pay = bid.outputs[leg];
|
|
18151
|
+
const escrow = order.inputs[leg];
|
|
18152
|
+
const required = order.output.assets[leg];
|
|
18153
|
+
if (take.token.toLowerCase() !== escrow.token.toLowerCase()) reason = `input token mismatch on leg ${leg}`;
|
|
18154
|
+
else if (pay.token.toLowerCase() !== required.token.toLowerCase()) reason = `output token mismatch on leg ${leg}`;
|
|
18155
|
+
else if (take.amount < 0n || pay.amount < 0n || take.amount === 0n !== (pay.amount === 0n))
|
|
18156
|
+
reason = `no valid quote on leg ${leg}`;
|
|
18157
|
+
else if (pay.amount * escrow.amount < take.amount * required.amount) reason = `rate below the order on leg ${leg}`;
|
|
18158
|
+
input += take.amount;
|
|
18159
|
+
output += pay.amount;
|
|
18160
|
+
}
|
|
18161
|
+
if (!reason && input === 0n) reason = "quotes no leg";
|
|
18162
|
+
if (reason) {
|
|
18163
|
+
console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: ${reason}`);
|
|
18164
|
+
continue;
|
|
18165
|
+
}
|
|
18166
|
+
ranked.push({ bid, input, output, index });
|
|
18343
18167
|
}
|
|
18344
|
-
|
|
18345
|
-
|
|
18168
|
+
ranked.sort((a, b) => {
|
|
18169
|
+
const left = a.output * b.input;
|
|
18170
|
+
const right = b.output * a.input;
|
|
18171
|
+
return left === right ? a.index - b.index : left > right ? -1 : 1;
|
|
18172
|
+
});
|
|
18173
|
+
return ranked.map(({ bid }) => bid);
|
|
18346
18174
|
}
|
|
18347
18175
|
/**
|
|
18348
18176
|
* Extracts the `FillOptions` struct from a single bid's ERC-7821
|
|
@@ -18365,150 +18193,6 @@ var BidManager = class {
|
|
|
18365
18193
|
}
|
|
18366
18194
|
return null;
|
|
18367
18195
|
}
|
|
18368
|
-
/**
|
|
18369
|
-
* Case A: single output token.
|
|
18370
|
-
* Filter bids by token match only, sort descending by amount.
|
|
18371
|
-
* Partial fill bids are allowed — the contract determines fill status.
|
|
18372
|
-
*/
|
|
18373
|
-
sortSingleOutput(bids, requiredAsset) {
|
|
18374
|
-
const requiredAmount = new Decimal2(requiredAsset.amount.toString());
|
|
18375
|
-
console.log(
|
|
18376
|
-
`[BidManager] sortSingleOutput: required token=${requiredAsset.token}, amount=${requiredAmount.toString()}`
|
|
18377
|
-
);
|
|
18378
|
-
const validBids = [];
|
|
18379
|
-
for (const bid of bids) {
|
|
18380
|
-
const bidOutput = bid.outputs[0];
|
|
18381
|
-
const bidAmount = new Decimal2(bidOutput.amount.toString());
|
|
18382
|
-
if (bidOutput.token.toLowerCase() !== requiredAsset.token.toLowerCase()) {
|
|
18383
|
-
console.warn(
|
|
18384
|
-
`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: token mismatch (bid=${bidOutput.token}, required=${requiredAsset.token})`
|
|
18385
|
-
);
|
|
18386
|
-
continue;
|
|
18387
|
-
}
|
|
18388
|
-
if (bidAmount.lt(requiredAmount)) {
|
|
18389
|
-
console.log(
|
|
18390
|
-
`[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidAmount.toString()}, required=${requiredAmount.toString()}, covers=${bidAmount.div(requiredAmount).mul(100).toFixed(2)}%)`
|
|
18391
|
-
);
|
|
18392
|
-
} else {
|
|
18393
|
-
console.log(
|
|
18394
|
-
`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: amount=${bidAmount.toString()} (surplus=${bidAmount.minus(requiredAmount).toString()})`
|
|
18395
|
-
);
|
|
18396
|
-
}
|
|
18397
|
-
validBids.push({ bid, amount: bidOutput.amount });
|
|
18398
|
-
}
|
|
18399
|
-
validBids.sort((a, b) => {
|
|
18400
|
-
const aAmt = new Decimal2(a.amount.toString());
|
|
18401
|
-
const bAmt = new Decimal2(b.amount.toString());
|
|
18402
|
-
return bAmt.comparedTo(aAmt);
|
|
18403
|
-
});
|
|
18404
|
-
return validBids.map(({ bid }) => bid);
|
|
18405
|
-
}
|
|
18406
|
-
/**
|
|
18407
|
-
* Case B: all outputs are USDC/USDT.
|
|
18408
|
-
* Sum normalised USD values (treating each stable as $1) and sort descending.
|
|
18409
|
-
* Partial fill bids are allowed.
|
|
18410
|
-
*/
|
|
18411
|
-
sortAllStables(bids, orderOutputs, chainId) {
|
|
18412
|
-
const requiredUsd = this.computeStablesUsdValue(orderOutputs, chainId);
|
|
18413
|
-
console.log(`[BidManager] sortAllStables: required USD value=${requiredUsd.toString()}`);
|
|
18414
|
-
const validBids = [];
|
|
18415
|
-
for (const bid of bids) {
|
|
18416
|
-
const bidUsd = this.computeStablesUsdValue(bid.outputs, chainId);
|
|
18417
|
-
if (bidUsd === null) {
|
|
18418
|
-
console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: unable to compute USD value`);
|
|
18419
|
-
continue;
|
|
18420
|
-
}
|
|
18421
|
-
if (bidUsd.lt(requiredUsd)) {
|
|
18422
|
-
console.log(
|
|
18423
|
-
`[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidUsd.toString()}, required=${requiredUsd.toString()}, covers=${bidUsd.div(requiredUsd).mul(100).toFixed(2)}%)`
|
|
18424
|
-
);
|
|
18425
|
-
} else {
|
|
18426
|
-
console.log(`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: USD value=${bidUsd.toString()}`);
|
|
18427
|
-
}
|
|
18428
|
-
validBids.push({ bid, usdValue: bidUsd });
|
|
18429
|
-
}
|
|
18430
|
-
validBids.sort((a, b) => b.usdValue.comparedTo(a.usdValue));
|
|
18431
|
-
return validBids.map(({ bid }) => bid);
|
|
18432
|
-
}
|
|
18433
|
-
/**
|
|
18434
|
-
* Case C: mixed output tokens (at least one non-stable).
|
|
18435
|
-
* Price every token via on-chain DEX quotes, fall back to raw amounts
|
|
18436
|
-
* if pricing is unavailable. Partial fill bids are allowed.
|
|
18437
|
-
*/
|
|
18438
|
-
async sortMixedOutputs(bids, orderOutputs, chainId) {
|
|
18439
|
-
const requiredUsd = await this.computeOutputsUsdValue(orderOutputs, chainId);
|
|
18440
|
-
if (requiredUsd === null) {
|
|
18441
|
-
console.warn("[BidManager] sortMixedOutputs: output tokens unpriceable, falling back to raw-amount sort");
|
|
18442
|
-
return this.sortByRawAmountFallback(bids, orderOutputs);
|
|
18443
|
-
}
|
|
18444
|
-
console.log(`[BidManager] sortMixedOutputs: required USD value=${requiredUsd.toString()}`);
|
|
18445
|
-
const validBids = [];
|
|
18446
|
-
for (const bid of bids) {
|
|
18447
|
-
const bidUsd = await this.computeOutputsUsdValue(bid.outputs, chainId);
|
|
18448
|
-
if (bidUsd === null) {
|
|
18449
|
-
console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: unable to price mixed outputs`);
|
|
18450
|
-
continue;
|
|
18451
|
-
}
|
|
18452
|
-
if (bidUsd.lt(requiredUsd)) {
|
|
18453
|
-
console.log(
|
|
18454
|
-
`[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidUsd.toString()}, required=${requiredUsd.toString()}, covers=${bidUsd.div(requiredUsd).mul(100).toFixed(2)}%)`
|
|
18455
|
-
);
|
|
18456
|
-
} else {
|
|
18457
|
-
console.log(
|
|
18458
|
-
`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: mixed USD value=${bidUsd.toString()}`
|
|
18459
|
-
);
|
|
18460
|
-
}
|
|
18461
|
-
validBids.push({ bid, usdValue: bidUsd });
|
|
18462
|
-
}
|
|
18463
|
-
validBids.sort((a, b) => b.usdValue.comparedTo(a.usdValue));
|
|
18464
|
-
return validBids.map(({ bid }) => bid);
|
|
18465
|
-
}
|
|
18466
|
-
/**
|
|
18467
|
-
* Fallback when DEX pricing is unavailable.
|
|
18468
|
-
* Computes total spread per bid. Bids missing a required token are rejected.
|
|
18469
|
-
* Bids offering less than required for a token are allowed (partial fill).
|
|
18470
|
-
* Sorted by total offered amount descending.
|
|
18471
|
-
*/
|
|
18472
|
-
sortByRawAmountFallback(bids, orderOutputs) {
|
|
18473
|
-
console.log(
|
|
18474
|
-
`[BidManager] sortByRawAmountFallback: checking ${bids.length} bid(s) against ${orderOutputs.length} required output(s)`
|
|
18475
|
-
);
|
|
18476
|
-
const validBids = [];
|
|
18477
|
-
for (const bid of bids) {
|
|
18478
|
-
let valid = true;
|
|
18479
|
-
let totalOffered = new Decimal2(0);
|
|
18480
|
-
let rejectReason = "";
|
|
18481
|
-
for (const required of orderOutputs) {
|
|
18482
|
-
const matching = bid.outputs.find((o) => o.token.toLowerCase() === required.token.toLowerCase());
|
|
18483
|
-
if (!matching) {
|
|
18484
|
-
valid = false;
|
|
18485
|
-
rejectReason = `missing output token=${required.token}`;
|
|
18486
|
-
break;
|
|
18487
|
-
}
|
|
18488
|
-
totalOffered = totalOffered.plus(new Decimal2(matching.amount.toString()));
|
|
18489
|
-
}
|
|
18490
|
-
if (!valid) {
|
|
18491
|
-
console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED (fallback): ${rejectReason}`);
|
|
18492
|
-
continue;
|
|
18493
|
-
}
|
|
18494
|
-
const totalRequired = orderOutputs.reduce(
|
|
18495
|
-
(acc, o) => acc.plus(new Decimal2(o.amount.toString())),
|
|
18496
|
-
new Decimal2(0)
|
|
18497
|
-
);
|
|
18498
|
-
if (totalOffered.lt(totalRequired)) {
|
|
18499
|
-
console.log(
|
|
18500
|
-
`[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (fallback) (offered=${totalOffered.toString()}, required=${totalRequired.toString()}, covers=${totalOffered.div(totalRequired).mul(100).toFixed(2)}%)`
|
|
18501
|
-
);
|
|
18502
|
-
} else {
|
|
18503
|
-
console.log(
|
|
18504
|
-
`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED (fallback): totalOffered=${totalOffered.toString()}`
|
|
18505
|
-
);
|
|
18506
|
-
}
|
|
18507
|
-
validBids.push({ bid, totalOffered });
|
|
18508
|
-
}
|
|
18509
|
-
validBids.sort((a, b) => b.totalOffered.comparedTo(a.totalOffered));
|
|
18510
|
-
return validBids.map(({ bid }) => bid);
|
|
18511
|
-
}
|
|
18512
18196
|
// ── Token classification helpers ──────────────────────────────────
|
|
18513
18197
|
/**
|
|
18514
18198
|
* Returns `true` if `tokenAddr` is either USDC or USDT on the given chain.
|
|
@@ -18539,23 +18223,6 @@ var BidManager = class {
|
|
|
18539
18223
|
return configService.getUsdtDecimals(chainId);
|
|
18540
18224
|
}
|
|
18541
18225
|
// ── Basket valuation helpers ──────────────────────────────────────
|
|
18542
|
-
/**
|
|
18543
|
-
* Sums the USD value of a basket of stable tokens (USDC/USDT only),
|
|
18544
|
-
* normalising each amount by its decimal count and treating each token as $1.
|
|
18545
|
-
*
|
|
18546
|
-
* @param outputs - List of token/amount pairs where every token is a stable.
|
|
18547
|
-
* @param chainId - State-machine ID used to look up decimals.
|
|
18548
|
-
* @returns Total USD value as a `Decimal`.
|
|
18549
|
-
*/
|
|
18550
|
-
computeStablesUsdValue(outputs, chainId) {
|
|
18551
|
-
let total = new Decimal2(0);
|
|
18552
|
-
for (const output of outputs) {
|
|
18553
|
-
const tokenAddr = bytes32ToBytes20(output.token);
|
|
18554
|
-
const decimals = this.getStableDecimals(tokenAddr, chainId);
|
|
18555
|
-
total = total.plus(new Decimal2(output.amount.toString()).div(new Decimal2(10).pow(decimals)));
|
|
18556
|
-
}
|
|
18557
|
-
return total;
|
|
18558
|
-
}
|
|
18559
18226
|
/**
|
|
18560
18227
|
* Prices every token in the basket via on-chain DEX quotes (token → USDC).
|
|
18561
18228
|
* Stables are valued at $1. Non-stables are quoted through Uniswap
|
|
@@ -18570,6 +18237,7 @@ var BidManager = class {
|
|
|
18570
18237
|
const { asset: wethAddr } = configService.getWrappedNativeAssetWithDecimals(chainId);
|
|
18571
18238
|
let totalUsd = new Decimal2(0);
|
|
18572
18239
|
for (const output of outputs) {
|
|
18240
|
+
if (output.amount === 0n) continue;
|
|
18573
18241
|
const tokenAddr = bytes32ToBytes20(output.token);
|
|
18574
18242
|
if (this.isStableToken(tokenAddr, chainId)) {
|
|
18575
18243
|
const decimals = this.getStableDecimals(tokenAddr, chainId);
|
|
@@ -18693,6 +18361,14 @@ var GasEstimator = class {
|
|
|
18693
18361
|
*/
|
|
18694
18362
|
async estimateFillOrder(params, pricingOptions = {}) {
|
|
18695
18363
|
const { order } = params;
|
|
18364
|
+
if (params.outputs !== void 0 && params.inputs === void 0) {
|
|
18365
|
+
throw new Error("Custom output quotes require explicit inputs");
|
|
18366
|
+
}
|
|
18367
|
+
const inputs = (params.inputs ?? order.inputs).map((input) => ({
|
|
18368
|
+
...input,
|
|
18369
|
+
token: normalizeAddressForEvmBytes32(input.token)
|
|
18370
|
+
}));
|
|
18371
|
+
const quotedOutputs = params.outputs ?? order.output.assets;
|
|
18696
18372
|
const orderFeeGasPriceBumpPercent = pricingOptions.orderFeeGasPriceBumpPercent ?? 0n;
|
|
18697
18373
|
const solverPrivateKey = generatePrivateKey();
|
|
18698
18374
|
const solverAccountAddress = privateKeyToAddress(solverPrivateKey);
|
|
@@ -18701,7 +18377,7 @@ var GasEstimator = class {
|
|
|
18701
18377
|
const intentGatewayV2Address = this.ctx.dest.configService.getIntentGatewayAddress(destStateMachineId);
|
|
18702
18378
|
const entryPointAddress = this.ctx.dest.configService.getEntryPointV08Address(destStateMachineId);
|
|
18703
18379
|
const chainId = BigInt(Number.parseInt(destStateMachineId.split("-")[1]));
|
|
18704
|
-
const totalEthValue =
|
|
18380
|
+
const totalEthValue = quotedOutputs.filter((output) => bytes32ToBytes20(output.token) === ADDRESS_ZERO2).reduce((sum, output) => sum + output.amount, 0n);
|
|
18705
18381
|
const [sourceFeeToken, destFeeToken, gasPrice, latestBlock] = await Promise.all([
|
|
18706
18382
|
getFeeToken(this.ctx, this.ctx.source.config.stateMachineId, this.ctx.source),
|
|
18707
18383
|
getFeeToken(this.ctx, this.ctx.dest.config.stateMachineId, this.ctx.dest),
|
|
@@ -18710,7 +18386,7 @@ var GasEstimator = class {
|
|
|
18710
18386
|
]);
|
|
18711
18387
|
const baseFeePerGas = latestBlock.baseFeePerGas ?? gasPrice;
|
|
18712
18388
|
const feeTokenAsBytes32 = bytes20ToBytes32(destFeeToken.address);
|
|
18713
|
-
const assetsForOverrides = [...
|
|
18389
|
+
const assetsForOverrides = [...quotedOutputs];
|
|
18714
18390
|
if (!assetsForOverrides.some((asset) => asset.token.toLowerCase() === feeTokenAsBytes32.toLowerCase())) {
|
|
18715
18391
|
assetsForOverrides.push({ token: feeTokenAsBytes32, amount: 0n });
|
|
18716
18392
|
}
|
|
@@ -18733,6 +18409,7 @@ var GasEstimator = class {
|
|
|
18733
18409
|
]);
|
|
18734
18410
|
const { bundler: bundlerStateOverrides } = stateOverridesResult;
|
|
18735
18411
|
const fillOptions = {
|
|
18412
|
+
inputs,
|
|
18736
18413
|
relayerFee: crossChainFees.postRequestFee,
|
|
18737
18414
|
// Always dispatch with the fee token (see the method docs).
|
|
18738
18415
|
nativeDispatchFee: 0n,
|
|
@@ -18740,7 +18417,7 @@ var GasEstimator = class {
|
|
|
18740
18417
|
// bound here would only risk the estimate reverting on a slow bundler round trip.
|
|
18741
18418
|
// The caller sets the real one on the options it actually signs.
|
|
18742
18419
|
validUntil: 0n,
|
|
18743
|
-
outputs:
|
|
18420
|
+
outputs: quotedOutputs.map((asset) => ({
|
|
18744
18421
|
...asset,
|
|
18745
18422
|
token: normalizeAddressForEvmBytes32(asset.token)
|
|
18746
18423
|
}))
|
|
@@ -18752,12 +18429,8 @@ var GasEstimator = class {
|
|
|
18752
18429
|
let maxFeePerGas = gasPrice + gasPrice * BigInt(maxFeeBumpPercent) / 100n;
|
|
18753
18430
|
const orderForEstimation = { ...order, session: solverAccountAddress };
|
|
18754
18431
|
const commitment = orderCommitment(orderForEstimation);
|
|
18755
|
-
|
|
18756
|
-
const fillOrderCalldata = encodeFillOrder(
|
|
18757
|
-
transformOrderForContract(orderForEstimation),
|
|
18758
|
-
fillOptions,
|
|
18759
|
-
fillOptionsVersion
|
|
18760
|
-
);
|
|
18432
|
+
await assertGatewayRelease(this.ctx.dest.client, intentGatewayV2Address);
|
|
18433
|
+
const fillOrderCalldata = encodeFillOrder(transformOrderForContract(orderForEstimation), fillOptions);
|
|
18761
18434
|
let callGasLimit = 500000n;
|
|
18762
18435
|
let verificationGasLimit = 100000n;
|
|
18763
18436
|
let preVerificationGas = 100000n;
|
|
@@ -18904,7 +18577,8 @@ var GasEstimator = class {
|
|
|
18904
18577
|
totalGasCostWei,
|
|
18905
18578
|
totalGasInFeeToken: totalGasInSourceFeeToken,
|
|
18906
18579
|
relayerFeeInSourceFeeToken: crossChainFees.relayerFeeInSourceFeeToken,
|
|
18907
|
-
fillOptions
|
|
18580
|
+
fillOptions,
|
|
18581
|
+
inputs
|
|
18908
18582
|
};
|
|
18909
18583
|
}
|
|
18910
18584
|
/**
|
|
@@ -19067,35 +18741,47 @@ var OrderStatusChecker = class {
|
|
|
19067
18741
|
}
|
|
19068
18742
|
ctx;
|
|
19069
18743
|
/**
|
|
19070
|
-
*
|
|
19071
|
-
*
|
|
19072
|
-
*
|
|
19073
|
-
* A non-zero value at that slot means the solver has called `fillOrder` and the order is complete
|
|
19074
|
-
* from the user's perspective (the beneficiary has received their tokens).
|
|
18744
|
+
* Whether the order is finalized on the destination chain, read from the gateway's `_filled`
|
|
18745
|
+
* getter. It is set by the completing fill, and by a cancellation, which records the refund
|
|
18746
|
+
* beneficiary there. A partially filled order reads false; see {@link getFillProgress}.
|
|
19075
18747
|
*
|
|
19076
18748
|
* @param order - The V2 order to check. `order.id` is used as the commitment; if not set it is computed.
|
|
19077
|
-
* @returns True if the order has been filled on the destination chain, false otherwise.
|
|
19078
18749
|
*/
|
|
19079
18750
|
async isOrderFilled(order) {
|
|
19080
18751
|
const commitment = order.id ?? orderCommitment(order);
|
|
19081
|
-
const
|
|
19082
|
-
const
|
|
19083
|
-
const filledSlot = await this.ctx.dest.client.readContract({
|
|
18752
|
+
const gateway = this.ctx.dest.configService.getIntentGatewayAddress(destinationStateMachine(order));
|
|
18753
|
+
const finalizer = await this.ctx.dest.client.readContract({
|
|
19084
18754
|
abi: ABI3,
|
|
19085
|
-
address:
|
|
19086
|
-
functionName: "
|
|
18755
|
+
address: gateway,
|
|
18756
|
+
functionName: "_filled",
|
|
19087
18757
|
args: [commitment]
|
|
19088
18758
|
});
|
|
19089
|
-
|
|
19090
|
-
|
|
19091
|
-
|
|
19092
|
-
|
|
19093
|
-
|
|
18759
|
+
return !/^0x0{40}$/i.test(finalizer);
|
|
18760
|
+
}
|
|
18761
|
+
/**
|
|
18762
|
+
* Output credited to the order so far, one entry per leg, read from `_partialFills` on the
|
|
18763
|
+
* destination chain. Several solvers can each fill a slice of a same-chain or cross-chain order,
|
|
18764
|
+
* so this, not {@link isOrderFilled}, says how far along an open order is. Surplus paid above
|
|
18765
|
+
* the order's rate is not counted.
|
|
18766
|
+
*
|
|
18767
|
+
* @param order - The V2 order to check. `order.id` is used as the commitment; if not set it is computed.
|
|
18768
|
+
*/
|
|
18769
|
+
async getFillProgress(order) {
|
|
18770
|
+
const commitment = order.id ?? orderCommitment(order);
|
|
18771
|
+
const gateway = this.ctx.dest.configService.getIntentGatewayAddress(destinationStateMachine(order));
|
|
18772
|
+
return Promise.all(
|
|
18773
|
+
order.output.assets.map(async (asset, index) => ({
|
|
18774
|
+
token: asset.token,
|
|
18775
|
+
amount: await readLegPartialFill(this.ctx.dest.client, gateway, commitment, index, asset.token)
|
|
18776
|
+
}))
|
|
18777
|
+
);
|
|
19094
18778
|
}
|
|
19095
18779
|
/**
|
|
19096
18780
|
* Checks if a V2 order has been refunded by reading the `_orders` mapping on the source chain.
|
|
19097
18781
|
*
|
|
19098
|
-
* Calls `_orders(commitment,
|
|
18782
|
+
* Calls `_orders(commitment, index)` for each input: escrow is held per leg, keyed by the input's
|
|
18783
|
+
* index, so inputs that repeat a token are read separately. A gateway not yet upgraded to per-leg
|
|
18784
|
+
* escrow is read by token instead, see {@link readLegEscrow}. When the order is placed the
|
|
19099
18785
|
* escrowed amounts are stored there. After a successful refund the contract zeroes them out.
|
|
19100
18786
|
* An order is considered refunded when all escrowed input amounts have been returned (i.e. are 0).
|
|
19101
18787
|
*
|
|
@@ -19107,14 +18793,14 @@ var OrderStatusChecker = class {
|
|
|
19107
18793
|
const commitment = order.id ?? orderCommitment(order);
|
|
19108
18794
|
const sourceStateMachineId = isHex(order.source) ? hexToString$1(order.source) : order.source;
|
|
19109
18795
|
const intentGatewayV2Address = this.ctx.source.configService.getIntentGatewayAddress(sourceStateMachineId);
|
|
19110
|
-
for (
|
|
19111
|
-
const
|
|
19112
|
-
|
|
19113
|
-
|
|
19114
|
-
|
|
19115
|
-
|
|
19116
|
-
|
|
19117
|
-
|
|
18796
|
+
for (let index = 0; index < order.inputs.length; index++) {
|
|
18797
|
+
const escrowedAmount = await readLegEscrow(
|
|
18798
|
+
this.ctx.source.client,
|
|
18799
|
+
intentGatewayV2Address,
|
|
18800
|
+
commitment,
|
|
18801
|
+
index,
|
|
18802
|
+
order.inputs[index].token
|
|
18803
|
+
);
|
|
19118
18804
|
if (escrowedAmount !== 0n) {
|
|
19119
18805
|
return false;
|
|
19120
18806
|
}
|
|
@@ -19122,726 +18808,466 @@ var OrderStatusChecker = class {
|
|
|
19122
18808
|
return true;
|
|
19123
18809
|
}
|
|
19124
18810
|
};
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
function sortPoolSymbols(symbolA, symbolB) {
|
|
19128
|
-
return symbolA.toLowerCase() <= symbolB.toLowerCase() ? [symbolA, symbolB] : [symbolB, symbolA];
|
|
18811
|
+
function destinationStateMachine(order) {
|
|
18812
|
+
return isHex(order.destination) ? hexToString$1(order.destination) : order.destination;
|
|
19129
18813
|
}
|
|
19130
|
-
|
|
19131
|
-
|
|
19132
|
-
|
|
19133
|
-
|
|
19134
|
-
|
|
19135
|
-
return
|
|
19136
|
-
poolId: `${token0Symbol}-${token1Symbol}`,
|
|
19137
|
-
token0Symbol,
|
|
19138
|
-
token1Symbol
|
|
19139
|
-
};
|
|
18814
|
+
var ORDERBOOK_URLS = {
|
|
18815
|
+
mainnet: "https://orderbook.hyperfx.finance/mainnet/graphql",
|
|
18816
|
+
testnet: "https://orderbook.hyperfx.finance/testnet/graphql"
|
|
18817
|
+
};
|
|
18818
|
+
function orderbookUrlFor(stateMachineId) {
|
|
18819
|
+
return TESTNET_CHAINS.has(stateMachineId) ? ORDERBOOK_URLS.testnet : ORDERBOOK_URLS.mainnet;
|
|
19140
18820
|
}
|
|
19141
|
-
|
|
19142
|
-
|
|
19143
|
-
var
|
|
19144
|
-
|
|
19145
|
-
|
|
19146
|
-
|
|
19147
|
-
|
|
19148
|
-
|
|
19149
|
-
|
|
19150
|
-
|
|
19151
|
-
|
|
19152
|
-
|
|
18821
|
+
var ORDERBOOK_DECIMALS = 18;
|
|
18822
|
+
var ORDERBOOK_SCALE = 10n ** BigInt(ORDERBOOK_DECIMALS);
|
|
18823
|
+
var OrderbookRequestError = class extends Error {
|
|
18824
|
+
constructor(message) {
|
|
18825
|
+
super(`HyperFX orderbook request failed: ${message}`);
|
|
18826
|
+
this.name = "OrderbookRequestError";
|
|
18827
|
+
}
|
|
18828
|
+
};
|
|
18829
|
+
var RATE_FIELDS = "side fillChain rate depthIn depthOut backingLiquidity sourceChains orderCount solverCount";
|
|
18830
|
+
var TOP_OF_BOOK_QUERY = `
|
|
18831
|
+
query TopOfBook($tokenA: String!, $tokenB: String!, $fillChain: String!, $sourceChain: String!) {
|
|
18832
|
+
books { id base quote }
|
|
18833
|
+
aToB: bestRate(tokenIn: $tokenA, tokenOut: $tokenB, fillChain: $fillChain, sourceChain: $sourceChain) { ${RATE_FIELDS} }
|
|
18834
|
+
bToA: bestRate(tokenIn: $tokenB, tokenOut: $tokenA, fillChain: $fillChain, sourceChain: $sourceChain) { ${RATE_FIELDS} }
|
|
18835
|
+
}`;
|
|
18836
|
+
var ROUTE_LIQUIDITY_QUERY = `
|
|
18837
|
+
query RouteLiquidity($route: RouteInput!) {
|
|
18838
|
+
books { id base quote }
|
|
18839
|
+
routeLiquidity(route: $route) {
|
|
18840
|
+
route bestRate slippageBps depthIn depthOut availableLiquidity maxFillableIn orderCount solverCount
|
|
18841
|
+
}
|
|
18842
|
+
}`;
|
|
18843
|
+
var QUOTE_QUERY = `
|
|
18844
|
+
query Quote($route: RouteInput!, $amountIn: BigInt!) {
|
|
18845
|
+
quote(route: $route, amountIn: $amountIn) {
|
|
18846
|
+
route side amountIn slippageBps fillable maxFillableIn
|
|
18847
|
+
fills { advertisedSize orderRate amountIn amountOut }
|
|
18848
|
+
}
|
|
18849
|
+
}`;
|
|
18850
|
+
var QUOTE_PESSIMISTIC_QUERY = `
|
|
18851
|
+
query QuotePessimistic($route: RouteInput!, $amountIn: BigInt!) {
|
|
18852
|
+
quotePessimistic(route: $route, amountIn: $amountIn) {
|
|
18853
|
+
route side amountIn amountOut rate priceBucket slippageBps fillable maxFillableIn
|
|
18854
|
+
}
|
|
18855
|
+
}`;
|
|
18856
|
+
var ORDERBOOK_QUERIES = {
|
|
18857
|
+
topOfBook: TOP_OF_BOOK_QUERY,
|
|
18858
|
+
routeLiquidity: ROUTE_LIQUIDITY_QUERY,
|
|
18859
|
+
quote: QUOTE_QUERY,
|
|
18860
|
+
quotePessimistic: QUOTE_PESSIMISTIC_QUERY
|
|
18861
|
+
};
|
|
18862
|
+
var HyperFxOrderbook = class {
|
|
18863
|
+
client;
|
|
18864
|
+
/** @param urlOrClient - The orderbook's GraphQL URL, or a configured `graphql-request` client. */
|
|
18865
|
+
constructor(urlOrClient = ORDERBOOK_URLS.mainnet) {
|
|
18866
|
+
this.client = typeof urlOrClient === "string" ? new GraphQLClient(urlOrClient) : urlOrClient;
|
|
18867
|
+
}
|
|
19153
18868
|
/**
|
|
19154
|
-
*
|
|
19155
|
-
*
|
|
19156
|
-
|
|
19157
|
-
|
|
19158
|
-
|
|
19159
|
-
|
|
19160
|
-
|
|
19161
|
-
|
|
19162
|
-
|
|
19163
|
-
|
|
19164
|
-
|
|
19165
|
-
*/
|
|
19166
|
-
async getAvailableLiquidity(params) {
|
|
19167
|
-
const pool = resolveLiquidityPool(params.source.symbol, params.destination.symbol);
|
|
19168
|
-
const direction = params.source.symbol.toLowerCase() === pool.token0Symbol.toLowerCase() ? SELL : BUY;
|
|
19169
|
-
const variables = {
|
|
19170
|
-
poolId: pool.poolId,
|
|
19171
|
-
sourceChain: params.source.chain,
|
|
19172
|
-
destinationChain: params.destination.chain,
|
|
19173
|
-
direction
|
|
18869
|
+
* The best bid and ask on `fillChain` for the pair `tokenA`/`tokenB`, counting
|
|
18870
|
+
* only orders a swap from `sourceChain` can use.
|
|
18871
|
+
*/
|
|
18872
|
+
async topOfBook(params) {
|
|
18873
|
+
const data = await this.request(TOP_OF_BOOK_QUERY, params);
|
|
18874
|
+
const book = findBook(data.books, params.tokenA, params.tokenB);
|
|
18875
|
+
const rates = [data.aToB, data.bToA].filter((rate) => rate !== null).map(parseRate);
|
|
18876
|
+
return {
|
|
18877
|
+
book,
|
|
18878
|
+
bid: rates.find((rate) => rate.side === "BID") ?? null,
|
|
18879
|
+
ask: rates.find((rate) => rate.side === "ASK") ?? null
|
|
19174
18880
|
};
|
|
19175
|
-
|
|
19176
|
-
|
|
19177
|
-
|
|
19178
|
-
}
|
|
19179
|
-
const
|
|
19180
|
-
if (!chainLiquidity) return void 0;
|
|
19181
|
-
const route = response.poolRoutes.nodes[0];
|
|
18881
|
+
}
|
|
18882
|
+
/** Total liquidity on a route, no amount required. */
|
|
18883
|
+
async routeLiquidity(route) {
|
|
18884
|
+
const { books, routeLiquidity: raw } = await this.request(ROUTE_LIQUIDITY_QUERY, { route });
|
|
18885
|
+
const book = findBook(books, route.tokenIn, route.tokenOut);
|
|
19182
18886
|
return {
|
|
19183
|
-
|
|
19184
|
-
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
|
|
19190
|
-
|
|
19191
|
-
|
|
19192
|
-
|
|
19193
|
-
|
|
19194
|
-
...readLiquiditySlice(route.depth, route.bidCount, "explicit route"),
|
|
19195
|
-
updatedAt: readIndexerDate(route.lastUpdatedAt, "route lastUpdatedAt")
|
|
19196
|
-
} : null
|
|
18887
|
+
route: raw.route,
|
|
18888
|
+
book,
|
|
18889
|
+
side: sameSymbol(book.base, route.tokenIn) ? "BID" : "ASK",
|
|
18890
|
+
bestRate: optionalBigInt(raw.bestRate),
|
|
18891
|
+
slippageBps: raw.slippageBps,
|
|
18892
|
+
depthIn: BigInt(raw.depthIn),
|
|
18893
|
+
depthOut: BigInt(raw.depthOut),
|
|
18894
|
+
availableLiquidity: BigInt(raw.availableLiquidity),
|
|
18895
|
+
maxFillableIn: BigInt(raw.maxFillableIn),
|
|
18896
|
+
orderCount: raw.orderCount,
|
|
18897
|
+
solverCount: raw.solverCount
|
|
19197
18898
|
};
|
|
19198
18899
|
}
|
|
19199
18900
|
/**
|
|
19200
|
-
*
|
|
19201
|
-
*
|
|
19202
|
-
|
|
19203
|
-
|
|
19204
|
-
|
|
19205
|
-
|
|
19206
|
-
|
|
19207
|
-
async getBuyAndSellRates(params) {
|
|
19208
|
-
const pool = resolveLiquidityPool(params.tokenInSymbol, params.tokenOutSymbol);
|
|
19209
|
-
const response = await this.queryClient.request(BUY_AND_SELL_RATES, {
|
|
19210
|
-
poolId: pool.poolId
|
|
18901
|
+
* The optimistic quote for `amountIn` (1e18 tokenIn) on a route: the route's orders, best price
|
|
18902
|
+
* first, each taking what it can at its own price. The fills' `amountOut`s sum to the output.
|
|
18903
|
+
*/
|
|
18904
|
+
async quote(route, amountIn) {
|
|
18905
|
+
const { quote: raw } = await this.request(QUOTE_QUERY, {
|
|
18906
|
+
route,
|
|
18907
|
+
amountIn: amountIn.toString()
|
|
19211
18908
|
});
|
|
19212
|
-
|
|
19213
|
-
|
|
19214
|
-
|
|
19215
|
-
|
|
19216
|
-
|
|
19217
|
-
|
|
19218
|
-
|
|
19219
|
-
|
|
19220
|
-
|
|
19221
|
-
|
|
19222
|
-
|
|
19223
|
-
|
|
19224
|
-
|
|
19225
|
-
|
|
19226
|
-
|
|
19227
|
-
|
|
19228
|
-
|
|
18909
|
+
return {
|
|
18910
|
+
route: raw.route,
|
|
18911
|
+
side: raw.side,
|
|
18912
|
+
amountIn: BigInt(raw.amountIn),
|
|
18913
|
+
slippageBps: raw.slippageBps,
|
|
18914
|
+
fillable: raw.fillable,
|
|
18915
|
+
maxFillableIn: BigInt(raw.maxFillableIn),
|
|
18916
|
+
fills: raw.fills.map((fill) => ({
|
|
18917
|
+
advertisedSize: BigInt(fill.advertisedSize),
|
|
18918
|
+
orderRate: BigInt(fill.orderRate),
|
|
18919
|
+
amountIn: BigInt(fill.amountIn),
|
|
18920
|
+
amountOut: BigInt(fill.amountOut)
|
|
18921
|
+
}))
|
|
18922
|
+
};
|
|
18923
|
+
}
|
|
18924
|
+
/**
|
|
18925
|
+
* The pessimistic quote for `amountIn` (1e18 tokenIn) on a route: one price, from the first level
|
|
18926
|
+
* deep enough to fill the whole trade by itself, at which every order in it fills; the route's
|
|
18927
|
+
* worst price when no one level can but its levels together can.
|
|
18928
|
+
*/
|
|
18929
|
+
async quotePessimistic(route, amountIn) {
|
|
18930
|
+
const { quotePessimistic: raw } = await this.request(
|
|
18931
|
+
QUOTE_PESSIMISTIC_QUERY,
|
|
18932
|
+
{ route, amountIn: amountIn.toString() }
|
|
19229
18933
|
);
|
|
19230
|
-
const quoteIsTokenOut = quoteTokenSymbol === params.tokenOutSymbol;
|
|
19231
|
-
const orientedBuy = quoteIsTokenOut ? direct : reverse;
|
|
19232
|
-
const orientedSell = quoteIsTokenOut ? reverse : direct;
|
|
19233
18934
|
return {
|
|
19234
|
-
|
|
19235
|
-
|
|
19236
|
-
|
|
19237
|
-
|
|
19238
|
-
|
|
19239
|
-
|
|
19240
|
-
|
|
19241
|
-
|
|
18935
|
+
route: raw.route,
|
|
18936
|
+
side: raw.side,
|
|
18937
|
+
amountIn: BigInt(raw.amountIn),
|
|
18938
|
+
amountOut: BigInt(raw.amountOut),
|
|
18939
|
+
rate: optionalBigInt(raw.rate),
|
|
18940
|
+
priceBucket: optionalBigInt(raw.priceBucket),
|
|
18941
|
+
slippageBps: raw.slippageBps,
|
|
18942
|
+
fillable: raw.fillable,
|
|
18943
|
+
maxFillableIn: BigInt(raw.maxFillableIn)
|
|
19242
18944
|
};
|
|
19243
18945
|
}
|
|
19244
|
-
|
|
19245
|
-
|
|
19246
|
-
|
|
19247
|
-
|
|
19248
|
-
|
|
18946
|
+
async request(query, variables) {
|
|
18947
|
+
try {
|
|
18948
|
+
return await this.client.request(query, variables);
|
|
18949
|
+
} catch (error) {
|
|
18950
|
+
throw new OrderbookRequestError(describeError(error));
|
|
18951
|
+
}
|
|
19249
18952
|
}
|
|
19250
18953
|
};
|
|
18954
|
+
function describeError(error) {
|
|
18955
|
+
const graphqlErrors = error?.response?.errors;
|
|
18956
|
+
if (graphqlErrors?.length) return graphqlErrors.map((e) => e.message).join("; ");
|
|
18957
|
+
return error instanceof Error ? error.message : String(error);
|
|
18958
|
+
}
|
|
18959
|
+
function sameSymbol(a, b) {
|
|
18960
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
18961
|
+
}
|
|
18962
|
+
function findBook(books, tokenA, tokenB) {
|
|
18963
|
+
const book = books.find(
|
|
18964
|
+
(b) => sameSymbol(b.base, tokenA) && sameSymbol(b.quote, tokenB) || sameSymbol(b.base, tokenB) && sameSymbol(b.quote, tokenA)
|
|
18965
|
+
);
|
|
18966
|
+
if (!book) throw new OrderbookRequestError(`no book trades ${tokenA} for ${tokenB}`);
|
|
18967
|
+
return book;
|
|
18968
|
+
}
|
|
18969
|
+
function optionalBigInt(value) {
|
|
18970
|
+
return value === null ? null : BigInt(value);
|
|
18971
|
+
}
|
|
18972
|
+
function parseRate(raw) {
|
|
18973
|
+
return {
|
|
18974
|
+
side: raw.side,
|
|
18975
|
+
fillChain: raw.fillChain,
|
|
18976
|
+
rate: BigInt(raw.rate),
|
|
18977
|
+
depthIn: BigInt(raw.depthIn),
|
|
18978
|
+
depthOut: BigInt(raw.depthOut),
|
|
18979
|
+
backingLiquidity: BigInt(raw.backingLiquidity),
|
|
18980
|
+
sourceChains: raw.sourceChains,
|
|
18981
|
+
orderCount: raw.orderCount,
|
|
18982
|
+
solverCount: raw.solverCount
|
|
18983
|
+
};
|
|
18984
|
+
}
|
|
18985
|
+
|
|
18986
|
+
// src/protocols/intents/orderbook/types.ts
|
|
19251
18987
|
var UnsupportedLiquidityAssetError = class extends Error {
|
|
19252
18988
|
constructor(chain, asset) {
|
|
19253
|
-
super(`No configured
|
|
18989
|
+
super(`No configured asset found for ${asset} on ${chain}`);
|
|
19254
18990
|
this.name = "UnsupportedLiquidityAssetError";
|
|
19255
18991
|
}
|
|
19256
18992
|
};
|
|
19257
18993
|
var UnsupportedLiquidityChainError = class extends Error {
|
|
19258
18994
|
constructor(chainId) {
|
|
19259
|
-
super(`No configured
|
|
18995
|
+
super(`No configured chain found for chain ID ${chainId}`);
|
|
19260
18996
|
this.name = "UnsupportedLiquidityChainError";
|
|
19261
18997
|
}
|
|
19262
18998
|
};
|
|
19263
|
-
|
|
19264
|
-
|
|
19265
|
-
|
|
19266
|
-
|
|
19267
|
-
|
|
19268
|
-
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
|
|
18999
|
+
var OrderbookQuoteNotConvergedError = class extends Error {
|
|
19000
|
+
constructor(route, rounds, lastAmountIn) {
|
|
19001
|
+
super(
|
|
19002
|
+
`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`
|
|
19003
|
+
);
|
|
19004
|
+
this.route = route;
|
|
19005
|
+
this.rounds = rounds;
|
|
19006
|
+
this.lastAmountIn = lastAmountIn;
|
|
19007
|
+
this.name = "OrderbookQuoteNotConvergedError";
|
|
19008
|
+
}
|
|
19009
|
+
route;
|
|
19010
|
+
rounds;
|
|
19011
|
+
lastAmountIn;
|
|
19012
|
+
};
|
|
19013
|
+
var InsufficientOrderbookLiquidityError = class extends Error {
|
|
19014
|
+
constructor(route, maxFillableIn) {
|
|
19015
|
+
super(
|
|
19016
|
+
`The HyperFX orderbook cannot fill ${route.tokenIn} -> ${route.tokenOut} on ${route.sourceChain} -> ${route.destinationChain}; max fillable input is ${maxFillableIn} raw units`
|
|
19017
|
+
);
|
|
19018
|
+
this.route = route;
|
|
19019
|
+
this.maxFillableIn = maxFillableIn;
|
|
19020
|
+
this.name = "InsufficientOrderbookLiquidityError";
|
|
19276
19021
|
}
|
|
19277
|
-
|
|
19278
|
-
|
|
19279
|
-
|
|
19280
|
-
|
|
19281
|
-
|
|
19282
|
-
|
|
19283
|
-
|
|
19284
|
-
|
|
19285
|
-
|
|
19286
|
-
|
|
19287
|
-
|
|
19022
|
+
route;
|
|
19023
|
+
maxFillableIn;
|
|
19024
|
+
};
|
|
19025
|
+
|
|
19026
|
+
// src/protocols/intents/orderbook/market.ts
|
|
19027
|
+
var MAX_EXACT_OUTPUT_ROUNDS = 16;
|
|
19028
|
+
var OrderbookMarket = class {
|
|
19029
|
+
constructor(configService, orderbook) {
|
|
19030
|
+
this.configService = configService;
|
|
19031
|
+
this.orderbook = orderbook;
|
|
19032
|
+
}
|
|
19033
|
+
configService;
|
|
19034
|
+
orderbook;
|
|
19035
|
+
async quoteIntent(params, sourceChain, destinationChain) {
|
|
19036
|
+
const orderbook = this.orderbook();
|
|
19037
|
+
if (!params.optimistic) {
|
|
19038
|
+
const { quote: quote2, tokenIn: tokenIn2, tokenOut: tokenOut2 } = await this.priceIntent(
|
|
19039
|
+
params,
|
|
19040
|
+
sourceChain,
|
|
19041
|
+
destinationChain,
|
|
19042
|
+
(route, amountIn) => orderbook.quotePessimistic(route, amountIn),
|
|
19043
|
+
levelQuoteTotals
|
|
19044
|
+
);
|
|
19045
|
+
return {
|
|
19046
|
+
route: quote2.route,
|
|
19047
|
+
side: quote2.side,
|
|
19048
|
+
amountIn: fromOrderbookAmount(quote2.amountIn, tokenIn2.decimals),
|
|
19049
|
+
amountOut: fromOrderbookAmount(quote2.amountOut, tokenOut2.decimals),
|
|
19050
|
+
rate: quote2.rate,
|
|
19051
|
+
priceBucket: quote2.priceBucket,
|
|
19052
|
+
slippageBps: quote2.slippageBps,
|
|
19053
|
+
fillable: quote2.fillable,
|
|
19054
|
+
maxFillableIn: fromOrderbookAmount(quote2.maxFillableIn, tokenIn2.decimals)
|
|
19055
|
+
};
|
|
19056
|
+
}
|
|
19057
|
+
const { quote, tokenIn, tokenOut } = await this.priceIntent(
|
|
19058
|
+
params,
|
|
19059
|
+
sourceChain,
|
|
19060
|
+
destinationChain,
|
|
19061
|
+
(route, amountIn) => orderbook.quote(route, amountIn),
|
|
19062
|
+
swapQuoteTotals
|
|
19063
|
+
);
|
|
19288
19064
|
return {
|
|
19289
|
-
|
|
19290
|
-
|
|
19065
|
+
route: quote.route,
|
|
19066
|
+
side: quote.side,
|
|
19067
|
+
amountIn: fromOrderbookAmount(quote.amountIn, tokenIn.decimals),
|
|
19068
|
+
slippageBps: quote.slippageBps,
|
|
19069
|
+
fillable: quote.fillable,
|
|
19070
|
+
maxFillableIn: fromOrderbookAmount(quote.maxFillableIn, tokenIn.decimals),
|
|
19071
|
+
legs: quote.fills.map((fill) => ({
|
|
19072
|
+
advertisedSize: fromOrderbookAmount(fill.advertisedSize, tokenOut.decimals),
|
|
19073
|
+
orderRate: fill.orderRate,
|
|
19074
|
+
amountIn: fromOrderbookAmount(fill.amountIn, tokenIn.decimals),
|
|
19075
|
+
amountOut: fromOrderbookAmount(fill.amountOut, tokenOut.decimals)
|
|
19076
|
+
}))
|
|
19291
19077
|
};
|
|
19292
|
-
} catch (error) {
|
|
19293
|
-
if (error instanceof InvalidLiquidityIndexerResponseError) throw error;
|
|
19294
|
-
throw new InvalidLiquidityIndexerResponseError(`${label} is not a positive integer`);
|
|
19295
19078
|
}
|
|
19296
|
-
|
|
19297
|
-
|
|
19298
|
-
|
|
19299
|
-
|
|
19079
|
+
/**
|
|
19080
|
+
* The fillable quote for an exact input, or for the input found to deliver an
|
|
19081
|
+
* exact output, with the assets it trades.
|
|
19082
|
+
*/
|
|
19083
|
+
async priceIntent(params, sourceChain, destinationChain, price, totals) {
|
|
19084
|
+
validateQuoteParams(params);
|
|
19085
|
+
const tokenIn = this.assetByAddress(sourceChain, params.tokenIn);
|
|
19086
|
+
const tokenOut = this.assetByAddress(destinationChain, params.tokenOut);
|
|
19087
|
+
const route = toRoute(tokenIn, tokenOut);
|
|
19088
|
+
if (params.amountIn !== void 0) {
|
|
19089
|
+
const quote2 = await price(route, toOrderbookAmount(params.amountIn, tokenIn.decimals));
|
|
19090
|
+
const { fillable, amountOut, maxFillableIn } = totals(quote2);
|
|
19091
|
+
if (!fillable || fromOrderbookAmount(amountOut, tokenOut.decimals) === 0n) {
|
|
19092
|
+
throw this.insufficient(route, maxFillableIn, tokenIn);
|
|
19093
|
+
}
|
|
19094
|
+
return { quote: quote2, tokenIn, tokenOut };
|
|
19095
|
+
}
|
|
19096
|
+
const targetOut = toOrderbookAmount(params.amountOut, tokenOut.decimals);
|
|
19097
|
+
const quote = await this.quoteExactOutput(route, targetOut, tokenIn, price, totals);
|
|
19098
|
+
return { quote, tokenIn, tokenOut };
|
|
19099
|
+
}
|
|
19100
|
+
async availableLiquidity(params, sourceChain, destinationChain) {
|
|
19101
|
+
const tokenIn = this.assetByAddress(sourceChain, params.tokenIn);
|
|
19102
|
+
const tokenOut = this.assetByAddress(destinationChain, params.tokenOut);
|
|
19103
|
+
const liquidity = await this.orderbook().routeLiquidity(toRoute(tokenIn, tokenOut));
|
|
19104
|
+
const [base3, quote] = liquidity.side === "BID" ? [tokenIn, tokenOut] : [tokenOut, tokenIn];
|
|
19105
|
+
return {
|
|
19106
|
+
sourceChain: tokenIn.chain,
|
|
19107
|
+
destinationChain: tokenOut.chain,
|
|
19108
|
+
tokenInSymbol: tokenIn.symbol,
|
|
19109
|
+
tokenOutSymbol: tokenOut.symbol,
|
|
19110
|
+
tokenAddress: tokenOut.address,
|
|
19111
|
+
route: liquidity.route,
|
|
19112
|
+
side: liquidity.side,
|
|
19113
|
+
baseTokenSymbol: base3.symbol,
|
|
19114
|
+
quoteTokenSymbol: quote.symbol,
|
|
19115
|
+
bestRate: formatOptional(liquidity.bestRate),
|
|
19116
|
+
availableLiquidity: format(liquidity.availableLiquidity),
|
|
19117
|
+
depthIn: format(liquidity.depthIn),
|
|
19118
|
+
depthOut: format(liquidity.depthOut),
|
|
19119
|
+
maxFillableIn: format(liquidity.maxFillableIn),
|
|
19120
|
+
orderCount: liquidity.orderCount,
|
|
19121
|
+
solverCount: liquidity.solverCount
|
|
19122
|
+
};
|
|
19300
19123
|
}
|
|
19301
|
-
|
|
19302
|
-
|
|
19303
|
-
|
|
19304
|
-
|
|
19305
|
-
|
|
19306
|
-
|
|
19307
|
-
|
|
19308
|
-
|
|
19309
|
-
}
|
|
19310
|
-
|
|
19311
|
-
|
|
19312
|
-
|
|
19313
|
-
|
|
19314
|
-
|
|
19315
|
-
|
|
19316
|
-
|
|
19317
|
-
|
|
19318
|
-
|
|
19319
|
-
|
|
19320
|
-
|
|
19321
|
-
|
|
19124
|
+
async buyAndSellRates(params) {
|
|
19125
|
+
const tokenIn = this.assetBySymbol(params.sourceChain, params.tokenInSymbol);
|
|
19126
|
+
const tokenOut = this.assetBySymbol(params.destinationChain, params.tokenOutSymbol);
|
|
19127
|
+
const { book, bid, ask } = await this.orderbook().topOfBook({
|
|
19128
|
+
tokenA: tokenIn.symbol,
|
|
19129
|
+
tokenB: tokenOut.symbol,
|
|
19130
|
+
fillChain: tokenOut.chain,
|
|
19131
|
+
sourceChain: tokenIn.chain
|
|
19132
|
+
});
|
|
19133
|
+
const bidRate = bid?.rate ?? null;
|
|
19134
|
+
const askRate = ask?.rate ?? null;
|
|
19135
|
+
const mid = bidRate !== null && askRate !== null ? (bidRate + askRate) / 2n : null;
|
|
19136
|
+
const spread = bidRate !== null && askRate !== null && askRate >= bidRate ? askRate - bidRate : null;
|
|
19137
|
+
return {
|
|
19138
|
+
baseTokenSymbol: book.base,
|
|
19139
|
+
quoteTokenSymbol: book.quote,
|
|
19140
|
+
sourceChain: tokenIn.chain,
|
|
19141
|
+
destinationChain: tokenOut.chain,
|
|
19142
|
+
bid: formatOptional(bidRate),
|
|
19143
|
+
ask: formatOptional(askRate),
|
|
19144
|
+
mid: formatOptional(mid),
|
|
19145
|
+
spread: formatOptional(spread),
|
|
19146
|
+
spreadBps: spread !== null && mid ? Number(spread * 1000000n / mid) / 100 : null
|
|
19147
|
+
};
|
|
19322
19148
|
}
|
|
19323
|
-
|
|
19324
|
-
|
|
19325
|
-
|
|
19326
|
-
|
|
19327
|
-
|
|
19149
|
+
/**
|
|
19150
|
+
* The orderbook only quotes an input amount, so an output is priced by
|
|
19151
|
+
* guessing the input from the best rate, then keeping the part of each quote
|
|
19152
|
+
* filled better than its worst rate and re-pricing the rest of `targetOut` at
|
|
19153
|
+
* the input-to-output ratio that part filled at, until a quote delivers it.
|
|
19154
|
+
* That ratio is the orderbook's own, with the protocol fee already taken off.
|
|
19155
|
+
*/
|
|
19156
|
+
async quoteExactOutput(route, targetOut, tokenIn, price, totals) {
|
|
19157
|
+
const liquidity = await this.orderbook().routeLiquidity(route);
|
|
19158
|
+
if (liquidity.bestRate === null) throw this.insufficient(route, 0n, tokenIn);
|
|
19159
|
+
const inputUnit = toOrderbookAmount(1n, tokenIn.decimals);
|
|
19160
|
+
let amountIn = roundUpTo(requiredInput(liquidity.side, targetOut, liquidity.bestRate), inputUnit);
|
|
19161
|
+
let lastAmountIn = amountIn;
|
|
19162
|
+
for (let round = 0; round < MAX_EXACT_OUTPUT_ROUNDS; round++) {
|
|
19163
|
+
if (amountIn > liquidity.maxFillableIn) throw this.insufficient(route, liquidity.maxFillableIn, tokenIn);
|
|
19164
|
+
const served = await price(route, amountIn);
|
|
19165
|
+
const quote = totals(served);
|
|
19166
|
+
if (quote.fillable && quote.amountOut >= targetOut) return served;
|
|
19167
|
+
lastAmountIn = amountIn;
|
|
19168
|
+
const betterIn = quote.amountIn - quote.worstIn;
|
|
19169
|
+
const betterOut = quote.amountOut - quote.worstOut;
|
|
19170
|
+
const next = quote.fillable && quote.worstOut > 0n ? betterIn + roundUpTo(divCeil((targetOut - betterOut) * quote.worstIn, quote.worstOut), inputUnit) : amountIn;
|
|
19171
|
+
amountIn = next > amountIn ? next : amountIn + inputUnit;
|
|
19172
|
+
}
|
|
19173
|
+
throw new OrderbookQuoteNotConvergedError(
|
|
19174
|
+
route,
|
|
19175
|
+
MAX_EXACT_OUTPUT_ROUNDS,
|
|
19176
|
+
fromOrderbookAmount(lastAmountIn, tokenIn.decimals)
|
|
19328
19177
|
);
|
|
19329
|
-
this.name = "UnsupportedIntentQuotePairError";
|
|
19330
19178
|
}
|
|
19331
|
-
|
|
19332
|
-
|
|
19333
|
-
constructor(tokenA, tokenB) {
|
|
19334
|
-
super(`No Phantom order price snapshot found for ${tokenA} -> ${tokenB}`);
|
|
19335
|
-
this.name = "PhantomSnapshotUnavailableError";
|
|
19179
|
+
insufficient(route, maxFillableIn, tokenIn) {
|
|
19180
|
+
return new InsufficientOrderbookLiquidityError(route, fromOrderbookAmount(maxFillableIn, tokenIn.decimals));
|
|
19336
19181
|
}
|
|
19337
|
-
|
|
19338
|
-
|
|
19339
|
-
|
|
19340
|
-
|
|
19341
|
-
|
|
19182
|
+
assetByAddress(chain, address) {
|
|
19183
|
+
const resolvedChain = resolveChain(chain);
|
|
19184
|
+
return withDecimals(
|
|
19185
|
+
resolvedChain,
|
|
19186
|
+
address,
|
|
19187
|
+
this.configService.getAssetMetadataByAddress(resolvedChain, address)
|
|
19188
|
+
);
|
|
19342
19189
|
}
|
|
19343
|
-
|
|
19344
|
-
|
|
19345
|
-
|
|
19346
|
-
const route = params.source && params.destination && params.tokenIn && params.tokenOut ? ` for ${params.tokenIn} -> ${params.tokenOut} on ${params.source} -> ${params.destination}` : "";
|
|
19347
|
-
const side = params.side ? ` ${params.side}` : "";
|
|
19348
|
-
super(`No indexed${side} rate available${route}`);
|
|
19349
|
-
this.name = "IndexedRateUnavailableError";
|
|
19190
|
+
assetBySymbol(chain, symbol) {
|
|
19191
|
+
const resolvedChain = resolveChain(chain);
|
|
19192
|
+
return withDecimals(resolvedChain, symbol, this.configService.getAssetMetadataBySymbol(resolvedChain, symbol));
|
|
19350
19193
|
}
|
|
19351
19194
|
};
|
|
19352
|
-
|
|
19353
|
-
|
|
19354
|
-
|
|
19355
|
-
|
|
19195
|
+
function resolveChain(chain) {
|
|
19196
|
+
const stateMachineId = getConfigByStateMachineId(chain)?.stateMachineId;
|
|
19197
|
+
if (!stateMachineId) throw new UnsupportedLiquidityChainError(chain);
|
|
19198
|
+
return stateMachineId;
|
|
19199
|
+
}
|
|
19200
|
+
function withDecimals(chain, asset, metadata) {
|
|
19201
|
+
if (!metadata) throw new UnsupportedLiquidityAssetError(chain, asset);
|
|
19202
|
+
const { decimals } = metadata;
|
|
19203
|
+
if (decimals === void 0 || !Number.isSafeInteger(decimals) || decimals < 0 || decimals > ORDERBOOK_DECIMALS) {
|
|
19204
|
+
throw new UnsupportedLiquidityAssetError(chain, `${metadata.symbol} (decimals are not configured)`);
|
|
19356
19205
|
}
|
|
19357
|
-
};
|
|
19358
|
-
|
|
19206
|
+
return { chain, symbol: metadata.symbol, address: metadata.address, decimals };
|
|
19207
|
+
}
|
|
19359
19208
|
function validateQuoteParams(params) {
|
|
19360
19209
|
const hasAmountIn = params.amountIn !== void 0;
|
|
19361
19210
|
const hasAmountOut = params.amountOut !== void 0;
|
|
19362
19211
|
if (hasAmountIn === hasAmountOut) throw new Error("Provide exactly one of amountIn or amountOut");
|
|
19363
19212
|
if (params.amountIn !== void 0 && params.amountIn <= 0n) throw new Error("amountIn must be greater than zero");
|
|
19364
19213
|
if (params.amountOut !== void 0 && params.amountOut <= 0n) throw new Error("amountOut must be greater than zero");
|
|
19365
|
-
if (params.tokenIn.toLowerCase() === params.tokenOut.toLowerCase()) {
|
|
19366
|
-
throw new Error("tokenIn and tokenOut cannot be the same");
|
|
19367
|
-
}
|
|
19368
19214
|
}
|
|
19369
|
-
|
|
19370
|
-
|
|
19371
|
-
|
|
19372
|
-
|
|
19373
|
-
|
|
19374
|
-
|
|
19375
|
-
|
|
19376
|
-
abi: IntentGatewayV2_default.ABI,
|
|
19377
|
-
functionName: "params"
|
|
19378
|
-
});
|
|
19379
|
-
const protocolFeeBps = Array.isArray(gatewayParams) ? BigInt(gatewayParams[4]) : BigInt(gatewayParams.protocolFeeBps ?? 0);
|
|
19380
|
-
if (protocolFeeBps < 0n || protocolFeeBps >= BPS_DENOMINATOR) {
|
|
19381
|
-
throw new Error(`Invalid IntentGateway protocol fee: ${protocolFeeBps} bps`);
|
|
19382
|
-
}
|
|
19383
|
-
return protocolFeeBps;
|
|
19384
|
-
}
|
|
19385
|
-
function deductProtocolFee(amount, protocolFeeBps) {
|
|
19386
|
-
if (protocolFeeBps <= 0n) return amount;
|
|
19387
|
-
const fee = amount * protocolFeeBps / BPS_DENOMINATOR;
|
|
19388
|
-
return amount - fee;
|
|
19215
|
+
function toRoute(tokenIn, tokenOut) {
|
|
19216
|
+
return {
|
|
19217
|
+
tokenIn: tokenIn.symbol,
|
|
19218
|
+
tokenOut: tokenOut.symbol,
|
|
19219
|
+
sourceChain: tokenIn.chain,
|
|
19220
|
+
destinationChain: tokenOut.chain
|
|
19221
|
+
};
|
|
19389
19222
|
}
|
|
19390
|
-
function
|
|
19391
|
-
|
|
19392
|
-
if (protocolFeeBps >= BPS_DENOMINATOR) throw new Error("protocolFeeBps must be less than 10,000");
|
|
19393
|
-
return divCeil(netAmount * BPS_DENOMINATOR, BPS_DENOMINATOR - protocolFeeBps);
|
|
19223
|
+
function requiredInput(side, amountOut, rate) {
|
|
19224
|
+
return side === "BID" ? divCeil(amountOut * ORDERBOOK_SCALE, rate) : divCeil(amountOut * rate, ORDERBOOK_SCALE);
|
|
19394
19225
|
}
|
|
19395
|
-
function
|
|
19396
|
-
|
|
19397
|
-
|
|
19226
|
+
function swapQuoteTotals(quote) {
|
|
19227
|
+
const worstFill = quote.fills.at(-1);
|
|
19228
|
+
if (quote.fillable && !worstFill)
|
|
19229
|
+
throw new OrderbookRequestError("the orderbook served a fillable quote with no fills");
|
|
19230
|
+
return {
|
|
19231
|
+
side: quote.side,
|
|
19232
|
+
amountIn: quote.amountIn,
|
|
19233
|
+
amountOut: quote.fills.reduce((total, fill) => total + fill.amountOut, 0n),
|
|
19234
|
+
fillable: quote.fillable,
|
|
19235
|
+
maxFillableIn: quote.maxFillableIn,
|
|
19236
|
+
worstIn: worstFill?.amountIn ?? 0n,
|
|
19237
|
+
worstOut: worstFill?.amountOut ?? 0n
|
|
19238
|
+
};
|
|
19398
19239
|
}
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
var UniswapV4IntentQuoteStrategy = class {
|
|
19403
|
-
constructor(chainConfigService) {
|
|
19404
|
-
this.chainConfigService = chainConfigService;
|
|
19405
|
-
}
|
|
19406
|
-
chainConfigService;
|
|
19407
|
-
baseQuoteClient;
|
|
19408
|
-
async quote(params, source, destination) {
|
|
19409
|
-
validateQuoteParams(params);
|
|
19410
|
-
const protocolFeeBps = await readProtocolFeeBps(this.chainConfigService, source);
|
|
19411
|
-
const quoteClient = this.resolveQuoteClient(source, destination);
|
|
19412
|
-
const poolConfig = this.resolvePoolConfig(params, source.stateMachineId, destination.stateMachineId);
|
|
19413
|
-
return params.amountIn !== void 0 ? this.quoteExactInput({ params, client: quoteClient, protocolFeeBps, poolConfig }) : this.quoteExactOutput({ params, client: quoteClient, protocolFeeBps, poolConfig });
|
|
19414
|
-
}
|
|
19415
|
-
resolveQuoteClient(source, destination) {
|
|
19416
|
-
if (source.stateMachineId === UNISWAP_INTENT_QUOTE_CHAIN) return source.client;
|
|
19417
|
-
if (destination.stateMachineId === UNISWAP_INTENT_QUOTE_CHAIN) return destination.client;
|
|
19418
|
-
if (this.baseQuoteClient) return this.baseQuoteClient;
|
|
19419
|
-
const rpcUrl = this.chainConfigService.getRpcUrl(UNISWAP_INTENT_QUOTE_CHAIN);
|
|
19420
|
-
if (!rpcUrl) throw new Error(`RPC URL is not configured for ${UNISWAP_INTENT_QUOTE_CHAIN}`);
|
|
19421
|
-
const baseQuoteClient = createPublicClient({
|
|
19422
|
-
chain: base,
|
|
19423
|
-
transport: http(rpcUrl)
|
|
19424
|
-
});
|
|
19425
|
-
this.baseQuoteClient = baseQuoteClient;
|
|
19426
|
-
return baseQuoteClient;
|
|
19427
|
-
}
|
|
19428
|
-
resolvePoolConfig(params, source, destination) {
|
|
19429
|
-
const override = params.uniswapV4?.poolKey;
|
|
19430
|
-
if (override) {
|
|
19431
|
-
const poolKey = normalizePoolKey(override);
|
|
19432
|
-
const tokenInForQuote = getAddress(override.currencyIn ?? params.tokenIn);
|
|
19433
|
-
if (tokenInForQuote !== poolKey.currency0 && tokenInForQuote !== poolKey.currency1) {
|
|
19434
|
-
throw new Error(
|
|
19435
|
-
`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.`
|
|
19436
|
-
);
|
|
19437
|
-
}
|
|
19438
|
-
return {
|
|
19439
|
-
poolKey,
|
|
19440
|
-
quoterAddress: this.resolveQuoterAddress(UNISWAP_INTENT_QUOTE_CHAIN, override.quoterAddress),
|
|
19441
|
-
tokenInForQuote
|
|
19442
|
-
};
|
|
19443
|
-
}
|
|
19444
|
-
const poolConfig = this.resolveConfiguredPool(params, source, destination, UNISWAP_INTENT_QUOTE_CHAIN);
|
|
19445
|
-
if (poolConfig) return poolConfig;
|
|
19446
|
-
throw new UnsupportedIntentQuotePairError({
|
|
19447
|
-
source,
|
|
19448
|
-
destination,
|
|
19449
|
-
tokenIn: params.tokenIn,
|
|
19450
|
-
tokenOut: params.tokenOut
|
|
19451
|
-
});
|
|
19240
|
+
function levelQuoteTotals(quote) {
|
|
19241
|
+
if (quote.fillable && quote.rate === null) {
|
|
19242
|
+
throw new OrderbookRequestError("the orderbook served a fillable quote with no rate");
|
|
19452
19243
|
}
|
|
19453
|
-
resolveConfiguredPool(params, sourceChain, destinationChain, quoteChain) {
|
|
19454
|
-
for (const pool of this.chainConfigService.getUniswapV4PoolConfigs(quoteChain)) {
|
|
19455
|
-
const resolvedPool = this.resolveConfiguredPoolTokens(quoteChain, pool);
|
|
19456
|
-
if (!resolvedPool) continue;
|
|
19457
|
-
const tokenInForQuote = this.matchPoolToken(params.tokenIn, sourceChain, resolvedPool);
|
|
19458
|
-
const tokenOutForQuote = this.matchPoolToken(params.tokenOut, destinationChain, resolvedPool);
|
|
19459
|
-
if (!tokenInForQuote || !tokenOutForQuote || tokenInForQuote.symbol === tokenOutForQuote.symbol) continue;
|
|
19460
|
-
const { currency0, currency1 } = sortCurrencies(resolvedPool[0].address, resolvedPool[1].address);
|
|
19461
|
-
return {
|
|
19462
|
-
poolKey: {
|
|
19463
|
-
currency0,
|
|
19464
|
-
currency1,
|
|
19465
|
-
fee: pool.fee,
|
|
19466
|
-
tickSpacing: pool.tickSpacing,
|
|
19467
|
-
hooks: getAddress(pool.hooks ?? zeroAddress)
|
|
19468
|
-
},
|
|
19469
|
-
quoterAddress: this.resolveQuoterAddress(quoteChain),
|
|
19470
|
-
tokenInForQuote: tokenInForQuote.address
|
|
19471
|
-
};
|
|
19472
|
-
}
|
|
19473
|
-
return null;
|
|
19474
|
-
}
|
|
19475
|
-
resolveQuoterAddress(chain, override) {
|
|
19476
|
-
const address = override ?? this.chainConfigService.getUniswapV4QuoterAddress(chain);
|
|
19477
|
-
if (!address || address === "0x" || address === zeroAddress) {
|
|
19478
|
-
throw new Error(`Uniswap V4 quoter is not configured for chain ${chain}`);
|
|
19479
|
-
}
|
|
19480
|
-
return getAddress(address);
|
|
19481
|
-
}
|
|
19482
|
-
resolveConfiguredPoolTokens(chain, pool) {
|
|
19483
|
-
const first = this.resolveConfiguredPoolToken(chain, pool.tokens[0]);
|
|
19484
|
-
const second = this.resolveConfiguredPoolToken(chain, pool.tokens[1]);
|
|
19485
|
-
return first && second ? [first, second] : null;
|
|
19486
|
-
}
|
|
19487
|
-
resolveConfiguredPoolToken(chain, symbol) {
|
|
19488
|
-
const address = this.chainConfigService.getAssetAddress(chain, symbol);
|
|
19489
|
-
if (!address || address === "0x") return null;
|
|
19490
|
-
return { symbol, address: getAddress(address) };
|
|
19491
|
-
}
|
|
19492
|
-
matchPoolToken(tokenAddress, orderChain, poolTokens) {
|
|
19493
|
-
const addressMatch = poolTokens.find(
|
|
19494
|
-
(poolToken) => poolToken.address.toLowerCase() === tokenAddress.toLowerCase()
|
|
19495
|
-
);
|
|
19496
|
-
if (addressMatch) return addressMatch;
|
|
19497
|
-
const metadata = this.chainConfigService.getAssetMetadataByAddress(orderChain, tokenAddress);
|
|
19498
|
-
if (!metadata) return null;
|
|
19499
|
-
return poolTokens.find((poolToken) => poolToken.symbol === metadata.symbol) ?? null;
|
|
19500
|
-
}
|
|
19501
|
-
async quoteExactInput(args) {
|
|
19502
|
-
const amountIn = args.params.amountIn;
|
|
19503
|
-
if (amountIn === void 0) throw new Error("amountIn is required for an exact-input quote");
|
|
19504
|
-
const swapAmountIn = deductProtocolFee(amountIn, args.protocolFeeBps);
|
|
19505
|
-
const amountOut = await this.readV4QuoteExactInput(args.client, args.poolConfig, swapAmountIn);
|
|
19506
|
-
return {
|
|
19507
|
-
strategy: "uniswap_v4",
|
|
19508
|
-
tradeType: "EXACT_INPUT",
|
|
19509
|
-
amountIn,
|
|
19510
|
-
amountOut,
|
|
19511
|
-
quoteMetadata: {
|
|
19512
|
-
quoteChain: UNISWAP_INTENT_QUOTE_CHAIN,
|
|
19513
|
-
poolKey: args.poolConfig.poolKey,
|
|
19514
|
-
quoterAddress: args.poolConfig.quoterAddress,
|
|
19515
|
-
protocolFeeBps: args.protocolFeeBps
|
|
19516
|
-
}
|
|
19517
|
-
};
|
|
19518
|
-
}
|
|
19519
|
-
async quoteExactOutput(args) {
|
|
19520
|
-
const amountOut = args.params.amountOut;
|
|
19521
|
-
if (amountOut === void 0) throw new Error("amountOut is required for an exact-output quote");
|
|
19522
|
-
const swapAmountIn = await this.readV4QuoteExactOutput(args.client, args.poolConfig, amountOut);
|
|
19523
|
-
const amountIn = grossUpForProtocolFee(swapAmountIn, args.protocolFeeBps);
|
|
19524
|
-
return {
|
|
19525
|
-
strategy: "uniswap_v4",
|
|
19526
|
-
tradeType: "EXACT_OUTPUT",
|
|
19527
|
-
amountIn,
|
|
19528
|
-
amountOut,
|
|
19529
|
-
quoteMetadata: {
|
|
19530
|
-
quoteChain: UNISWAP_INTENT_QUOTE_CHAIN,
|
|
19531
|
-
poolKey: args.poolConfig.poolKey,
|
|
19532
|
-
quoterAddress: args.poolConfig.quoterAddress,
|
|
19533
|
-
protocolFeeBps: args.protocolFeeBps
|
|
19534
|
-
}
|
|
19535
|
-
};
|
|
19536
|
-
}
|
|
19537
|
-
async readV4QuoteExactInput(client, poolConfig, amountIn) {
|
|
19538
|
-
const data = encodeFunctionData({
|
|
19539
|
-
abi: UNISWAP_V4_QUOTER_ABI,
|
|
19540
|
-
functionName: "quoteExactInputSingle",
|
|
19541
|
-
args: [
|
|
19542
|
-
{
|
|
19543
|
-
poolKey: poolConfig.poolKey,
|
|
19544
|
-
zeroForOne: getZeroForOne(poolConfig.tokenInForQuote, poolConfig.poolKey),
|
|
19545
|
-
exactAmount: amountIn,
|
|
19546
|
-
hookData: "0x"
|
|
19547
|
-
}
|
|
19548
|
-
]
|
|
19549
|
-
});
|
|
19550
|
-
const response = await client.call({ to: poolConfig.quoterAddress, data });
|
|
19551
|
-
if (!response.data || response.data === "0x") {
|
|
19552
|
-
throw new Error(`Uniswap V4 quoter at ${poolConfig.quoterAddress} returned no data`);
|
|
19553
|
-
}
|
|
19554
|
-
const [amountOut] = decodeFunctionResult({
|
|
19555
|
-
abi: UNISWAP_V4_QUOTER_ABI,
|
|
19556
|
-
functionName: "quoteExactInputSingle",
|
|
19557
|
-
data: response.data
|
|
19558
|
-
});
|
|
19559
|
-
return amountOut;
|
|
19560
|
-
}
|
|
19561
|
-
async readV4QuoteExactOutput(client, poolConfig, amountOut) {
|
|
19562
|
-
const data = encodeFunctionData({
|
|
19563
|
-
abi: UNISWAP_V4_QUOTER_ABI,
|
|
19564
|
-
functionName: "quoteExactOutputSingle",
|
|
19565
|
-
args: [
|
|
19566
|
-
{
|
|
19567
|
-
poolKey: poolConfig.poolKey,
|
|
19568
|
-
zeroForOne: getZeroForOne(poolConfig.tokenInForQuote, poolConfig.poolKey),
|
|
19569
|
-
exactAmount: amountOut,
|
|
19570
|
-
hookData: "0x"
|
|
19571
|
-
}
|
|
19572
|
-
]
|
|
19573
|
-
});
|
|
19574
|
-
const response = await client.call({ to: poolConfig.quoterAddress, data });
|
|
19575
|
-
if (!response.data || response.data === "0x") {
|
|
19576
|
-
throw new Error(`Uniswap V4 quoter at ${poolConfig.quoterAddress} returned no data`);
|
|
19577
|
-
}
|
|
19578
|
-
const [amountIn] = decodeFunctionResult({
|
|
19579
|
-
abi: UNISWAP_V4_QUOTER_ABI,
|
|
19580
|
-
functionName: "quoteExactOutputSingle",
|
|
19581
|
-
data: response.data
|
|
19582
|
-
});
|
|
19583
|
-
return amountIn;
|
|
19584
|
-
}
|
|
19585
|
-
};
|
|
19586
|
-
function normalizePoolKey(poolKey) {
|
|
19587
19244
|
return {
|
|
19588
|
-
|
|
19589
|
-
|
|
19590
|
-
|
|
19591
|
-
|
|
19592
|
-
|
|
19245
|
+
side: quote.side,
|
|
19246
|
+
amountIn: quote.amountIn,
|
|
19247
|
+
amountOut: quote.amountOut,
|
|
19248
|
+
fillable: quote.fillable,
|
|
19249
|
+
maxFillableIn: quote.maxFillableIn,
|
|
19250
|
+
worstIn: quote.amountIn,
|
|
19251
|
+
worstOut: quote.amountOut
|
|
19593
19252
|
};
|
|
19594
19253
|
}
|
|
19595
|
-
function
|
|
19596
|
-
|
|
19597
|
-
const output = getAddress(tokenOut);
|
|
19598
|
-
return BigInt(input) < BigInt(output) ? { currency0: input, currency1: output } : { currency0: output, currency1: input };
|
|
19599
|
-
}
|
|
19600
|
-
function getZeroForOne(tokenIn, poolKey) {
|
|
19601
|
-
return getAddress(tokenIn).toLowerCase() === getAddress(poolKey.currency0).toLowerCase();
|
|
19602
|
-
}
|
|
19603
|
-
|
|
19604
|
-
// src/protocols/intents/quote/phantomSnapshot.ts
|
|
19605
|
-
var PHANTOM_INTENT_QUOTE_CHAIN = "EVM-8453" /* BASE_MAINNET */;
|
|
19606
|
-
var BASE_SNAPSHOT_ASSET_BY_ORDER_ASSET = {
|
|
19607
|
-
USDC: "USDC",
|
|
19608
|
-
USDT: "USDC",
|
|
19609
|
-
cNGN: "cNGN"
|
|
19610
|
-
};
|
|
19611
|
-
var SUPPORTED_BASE_SNAPSHOT_PAIRS = /* @__PURE__ */ new Set(["USDC:cNGN", "cNGN:USDC"]);
|
|
19612
|
-
var PhantomSnapshotIntentQuoteStrategy = class {
|
|
19613
|
-
constructor(chainConfigService, getQueryClient) {
|
|
19614
|
-
this.chainConfigService = chainConfigService;
|
|
19615
|
-
this.getQueryClient = getQueryClient;
|
|
19616
|
-
this.pairResolver = new PhantomSnapshotPairResolver(chainConfigService);
|
|
19617
|
-
}
|
|
19618
|
-
chainConfigService;
|
|
19619
|
-
getQueryClient;
|
|
19620
|
-
pairResolver;
|
|
19621
|
-
async quote(params, source, destination) {
|
|
19622
|
-
validateQuoteParams(params);
|
|
19623
|
-
const pair = this.pairResolver.resolve(params, source.stateMachineId, destination.stateMachineId);
|
|
19624
|
-
if (!pair) {
|
|
19625
|
-
throw new UnsupportedIntentQuotePairError({
|
|
19626
|
-
source: source.stateMachineId,
|
|
19627
|
-
destination: destination.stateMachineId,
|
|
19628
|
-
tokenIn: params.tokenIn,
|
|
19629
|
-
tokenOut: params.tokenOut,
|
|
19630
|
-
quoteSource: "Phantom snapshot pair"
|
|
19631
|
-
});
|
|
19632
|
-
}
|
|
19633
|
-
const queryClient = this.getQueryClient();
|
|
19634
|
-
const [protocolFeeBps, snapshot] = await Promise.all([
|
|
19635
|
-
readProtocolFeeBps(this.chainConfigService, source),
|
|
19636
|
-
_queryLatestPhantomOrderPriceSnapshot({
|
|
19637
|
-
tokenA: pair.tokenA,
|
|
19638
|
-
tokenB: pair.tokenB,
|
|
19639
|
-
queryClient
|
|
19640
|
-
})
|
|
19641
|
-
]);
|
|
19642
|
-
if (!snapshot) throw new PhantomSnapshotUnavailableError(pair.tokenA, pair.tokenB);
|
|
19643
|
-
this.validateSnapshot(snapshot);
|
|
19644
|
-
return this.quoteSnapshot(params, protocolFeeBps, snapshot);
|
|
19645
|
-
}
|
|
19646
|
-
quoteSnapshot(params, protocolFeeBps, snapshot) {
|
|
19647
|
-
if (params.amountIn !== void 0) {
|
|
19648
|
-
const netAmountIn2 = deductProtocolFee(params.amountIn, protocolFeeBps);
|
|
19649
|
-
const amountOut = netAmountIn2 * snapshot.medianPrice / snapshot.standardAmount;
|
|
19650
|
-
if (amountOut <= 0n) {
|
|
19651
|
-
throw new InvalidPhantomSnapshotError(snapshot.commitment, "quote rounds down to zero output");
|
|
19652
|
-
}
|
|
19653
|
-
return this.buildResult("EXACT_INPUT", params.amountIn, amountOut, protocolFeeBps, snapshot);
|
|
19654
|
-
}
|
|
19655
|
-
if (params.amountOut === void 0) throw new Error("Quote amount is missing after validation");
|
|
19656
|
-
const netAmountIn = divCeil(params.amountOut * snapshot.standardAmount, snapshot.medianPrice);
|
|
19657
|
-
const amountIn = grossUpForProtocolFee(netAmountIn, protocolFeeBps);
|
|
19658
|
-
return this.buildResult("EXACT_OUTPUT", amountIn, params.amountOut, protocolFeeBps, snapshot);
|
|
19659
|
-
}
|
|
19660
|
-
buildResult(tradeType, amountIn, amountOut, protocolFeeBps, snapshot) {
|
|
19661
|
-
return {
|
|
19662
|
-
strategy: "phantom_snapshot",
|
|
19663
|
-
tradeType,
|
|
19664
|
-
amountIn,
|
|
19665
|
-
amountOut,
|
|
19666
|
-
quoteMetadata: {
|
|
19667
|
-
quoteChain: PHANTOM_INTENT_QUOTE_CHAIN,
|
|
19668
|
-
commitment: snapshot.commitment,
|
|
19669
|
-
tokenA: snapshot.tokenA,
|
|
19670
|
-
tokenB: snapshot.tokenB,
|
|
19671
|
-
standardAmount: snapshot.standardAmount,
|
|
19672
|
-
medianPrice: snapshot.medianPrice,
|
|
19673
|
-
lowestPrice: snapshot.lowestPrice,
|
|
19674
|
-
highestPrice: snapshot.highestPrice,
|
|
19675
|
-
blockNumber: snapshot.blockNumber,
|
|
19676
|
-
snapshotTime: snapshot.snapshotTime,
|
|
19677
|
-
bidCount: snapshot.bidCount,
|
|
19678
|
-
protocolFeeBps
|
|
19679
|
-
}
|
|
19680
|
-
};
|
|
19681
|
-
}
|
|
19682
|
-
validateSnapshot(snapshot) {
|
|
19683
|
-
if (snapshot.standardAmount <= 0n) {
|
|
19684
|
-
throw new InvalidPhantomSnapshotError(snapshot.commitment, "standardAmount must be greater than zero");
|
|
19685
|
-
}
|
|
19686
|
-
if (snapshot.medianPrice <= 0n) {
|
|
19687
|
-
throw new InvalidPhantomSnapshotError(snapshot.commitment, "medianPrice must be greater than zero");
|
|
19688
|
-
}
|
|
19689
|
-
if (snapshot.bidCount <= 0) {
|
|
19690
|
-
throw new InvalidPhantomSnapshotError(snapshot.commitment, "bidCount must be greater than zero");
|
|
19691
|
-
}
|
|
19692
|
-
if (Number.isNaN(snapshot.snapshotTime.getTime())) {
|
|
19693
|
-
throw new InvalidPhantomSnapshotError(snapshot.commitment, "snapshotTime is invalid");
|
|
19694
|
-
}
|
|
19695
|
-
}
|
|
19696
|
-
};
|
|
19697
|
-
var PhantomSnapshotPairResolver = class {
|
|
19698
|
-
constructor(chainConfigService) {
|
|
19699
|
-
this.chainConfigService = chainConfigService;
|
|
19700
|
-
}
|
|
19701
|
-
chainConfigService;
|
|
19702
|
-
resolve(params, sourceStateMachineId, destinationStateMachineId) {
|
|
19703
|
-
const tokenIn = this.resolveBaseSnapshotToken(sourceStateMachineId, params.tokenIn);
|
|
19704
|
-
const tokenOut = this.resolveBaseSnapshotToken(destinationStateMachineId, params.tokenOut);
|
|
19705
|
-
if (!tokenIn || !tokenOut || !isSupportedSnapshotPair(tokenIn.symbol, tokenOut.symbol)) return;
|
|
19706
|
-
return { tokenA: tokenIn.address, tokenB: tokenOut.address };
|
|
19707
|
-
}
|
|
19708
|
-
resolveBaseSnapshotToken(chain, tokenAddress) {
|
|
19709
|
-
const orderAsset = this.chainConfigService.getAssetMetadataByAddress(chain, tokenAddress)?.symbol;
|
|
19710
|
-
if (!orderAsset) return;
|
|
19711
|
-
const snapshotAsset = BASE_SNAPSHOT_ASSET_BY_ORDER_ASSET[orderAsset];
|
|
19712
|
-
if (!snapshotAsset) return;
|
|
19713
|
-
const address = this.chainConfigService.getAssetAddress(PHANTOM_INTENT_QUOTE_CHAIN, snapshotAsset);
|
|
19714
|
-
if (!isConfiguredAddress(address)) return;
|
|
19715
|
-
return { symbol: snapshotAsset, address };
|
|
19716
|
-
}
|
|
19717
|
-
};
|
|
19718
|
-
function isSupportedSnapshotPair(tokenA, tokenB) {
|
|
19719
|
-
return SUPPORTED_BASE_SNAPSHOT_PAIRS.has(`${tokenA}:${tokenB}`);
|
|
19254
|
+
function toOrderbookAmount(amount, decimals) {
|
|
19255
|
+
return amount * 10n ** BigInt(ORDERBOOK_DECIMALS - decimals);
|
|
19720
19256
|
}
|
|
19721
|
-
function
|
|
19722
|
-
return
|
|
19257
|
+
function fromOrderbookAmount(amount, decimals) {
|
|
19258
|
+
return amount / 10n ** BigInt(ORDERBOOK_DECIMALS - decimals);
|
|
19723
19259
|
}
|
|
19724
|
-
|
|
19725
|
-
|
|
19726
|
-
var IndexedRateIntentQuoteStrategy = class {
|
|
19727
|
-
constructor(chainConfigService, getQueryClient) {
|
|
19728
|
-
this.chainConfigService = chainConfigService;
|
|
19729
|
-
this.getQueryClient = getQueryClient;
|
|
19730
|
-
}
|
|
19731
|
-
chainConfigService;
|
|
19732
|
-
getQueryClient;
|
|
19733
|
-
async quote(params, source, destination) {
|
|
19734
|
-
validateQuoteParams(params);
|
|
19735
|
-
const sourceConfig = getConfigByStateMachineId(source.stateMachineId);
|
|
19736
|
-
const destinationConfig = getConfigByStateMachineId(destination.stateMachineId);
|
|
19737
|
-
if (!sourceConfig) throw new UnsupportedLiquidityChainError(source.stateMachineId);
|
|
19738
|
-
if (!destinationConfig) throw new UnsupportedLiquidityChainError(destination.stateMachineId);
|
|
19739
|
-
const tokenIn = this.resolveAsset(sourceConfig.stateMachineId, params.tokenIn);
|
|
19740
|
-
const tokenOut = this.resolveAsset(destinationConfig.stateMachineId, params.tokenOut);
|
|
19741
|
-
const [protocolFeeBps, rates] = await Promise.all([
|
|
19742
|
-
readProtocolFeeBps(this.chainConfigService, source),
|
|
19743
|
-
new LiquidityEngine(this.getQueryClient()).getBuyAndSellRates({
|
|
19744
|
-
sourceChain: sourceConfig.stateMachineId,
|
|
19745
|
-
destinationChain: destinationConfig.stateMachineId,
|
|
19746
|
-
tokenInSymbol: tokenIn.symbol,
|
|
19747
|
-
tokenOutSymbol: tokenOut.symbol
|
|
19748
|
-
})
|
|
19749
|
-
]);
|
|
19750
|
-
if (!rates) {
|
|
19751
|
-
throw new IndexedRateUnavailableError({
|
|
19752
|
-
source: sourceConfig.stateMachineId,
|
|
19753
|
-
destination: destinationConfig.stateMachineId,
|
|
19754
|
-
tokenIn: tokenIn.symbol,
|
|
19755
|
-
tokenOut: tokenOut.symbol
|
|
19756
|
-
});
|
|
19757
|
-
}
|
|
19758
|
-
const selectedRate = selectIndexedRate(rates, tokenIn.symbol, tokenOut.symbol);
|
|
19759
|
-
return quoteWithIndexedRate(params, tokenIn, tokenOut, selectedRate, rates, protocolFeeBps);
|
|
19760
|
-
}
|
|
19761
|
-
resolveAsset(chain, address) {
|
|
19762
|
-
const asset = this.chainConfigService.getAssetMetadataByAddress(chain, address);
|
|
19763
|
-
if (!asset) throw new UnsupportedLiquidityAssetError(chain, address);
|
|
19764
|
-
const { decimals } = asset;
|
|
19765
|
-
if (decimals === void 0 || !Number.isSafeInteger(decimals) || decimals < 0) {
|
|
19766
|
-
throw new InvalidIndexedRateError(`decimals are not configured for ${asset.symbol} on ${chain}`);
|
|
19767
|
-
}
|
|
19768
|
-
return { ...asset, decimals };
|
|
19769
|
-
}
|
|
19770
|
-
};
|
|
19771
|
-
function selectIndexedRate(rates, tokenInSymbol, tokenOutSymbol) {
|
|
19772
|
-
if (tokenInSymbol === rates.baseTokenSymbol && tokenOutSymbol === rates.quoteTokenSymbol) {
|
|
19773
|
-
return readIndexedRate2(
|
|
19774
|
-
"buy",
|
|
19775
|
-
rates.buyRate,
|
|
19776
|
-
rates.buyRateUpdatedAt,
|
|
19777
|
-
rates,
|
|
19778
|
-
tokenInSymbol,
|
|
19779
|
-
tokenOutSymbol
|
|
19780
|
-
);
|
|
19781
|
-
}
|
|
19782
|
-
if (tokenInSymbol === rates.quoteTokenSymbol && tokenOutSymbol === rates.baseTokenSymbol) {
|
|
19783
|
-
return readIndexedRate2(
|
|
19784
|
-
"sell",
|
|
19785
|
-
rates.sellRate,
|
|
19786
|
-
rates.sellRateUpdatedAt,
|
|
19787
|
-
rates,
|
|
19788
|
-
tokenInSymbol,
|
|
19789
|
-
tokenOutSymbol
|
|
19790
|
-
);
|
|
19791
|
-
}
|
|
19792
|
-
throw new InvalidIndexedRateError(
|
|
19793
|
-
`indexed pair ${rates.baseTokenSymbol}/${rates.quoteTokenSymbol} does not match ${tokenInSymbol}/${tokenOutSymbol}`
|
|
19794
|
-
);
|
|
19260
|
+
function roundUpTo(amount, unit) {
|
|
19261
|
+
return divCeil(amount, unit) * unit;
|
|
19795
19262
|
}
|
|
19796
|
-
function
|
|
19797
|
-
|
|
19798
|
-
throw new IndexedRateUnavailableError({
|
|
19799
|
-
source: rates.sourceChain,
|
|
19800
|
-
destination: rates.destinationChain,
|
|
19801
|
-
tokenIn: tokenInSymbol,
|
|
19802
|
-
tokenOut: tokenOutSymbol,
|
|
19803
|
-
side
|
|
19804
|
-
});
|
|
19805
|
-
}
|
|
19806
|
-
try {
|
|
19807
|
-
const scaledRate = parseUnits(rate, INDEXED_RATE_DECIMALS);
|
|
19808
|
-
if (scaledRate <= 0n || Number.isNaN(updatedAt.getTime())) throw new Error();
|
|
19809
|
-
return { side, rate, scaledRate, updatedAt };
|
|
19810
|
-
} catch {
|
|
19811
|
-
throw new InvalidIndexedRateError(`${side} rate or timestamp is invalid`);
|
|
19812
|
-
}
|
|
19263
|
+
function divCeil(numerator, denominator) {
|
|
19264
|
+
return (numerator + denominator - 1n) / denominator;
|
|
19813
19265
|
}
|
|
19814
|
-
function
|
|
19815
|
-
|
|
19816
|
-
const outputUnit = 10n ** BigInt(tokenOut.decimals);
|
|
19817
|
-
if (params.amountIn !== void 0) {
|
|
19818
|
-
const netAmountIn2 = deductProtocolFee(params.amountIn, protocolFeeBps);
|
|
19819
|
-
const amountOut = selectedRate.side === "buy" ? netAmountIn2 * selectedRate.scaledRate * outputUnit / (inputUnit * INDEXED_RATE_SCALE) : netAmountIn2 * outputUnit * INDEXED_RATE_SCALE / (inputUnit * selectedRate.scaledRate);
|
|
19820
|
-
if (amountOut <= 0n) throw new InvalidIndexedRateError("quote rounds down to zero output");
|
|
19821
|
-
return buildResult("EXACT_INPUT", params.amountIn, amountOut, selectedRate, rates, protocolFeeBps);
|
|
19822
|
-
}
|
|
19823
|
-
if (params.amountOut === void 0) throw new Error("Quote amount is missing after validation");
|
|
19824
|
-
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);
|
|
19825
|
-
const amountIn = grossUpForProtocolFee(netAmountIn, protocolFeeBps);
|
|
19826
|
-
return buildResult("EXACT_OUTPUT", amountIn, params.amountOut, selectedRate, rates, protocolFeeBps);
|
|
19266
|
+
function format(amount) {
|
|
19267
|
+
return formatUnits(amount, ORDERBOOK_DECIMALS);
|
|
19827
19268
|
}
|
|
19828
|
-
function
|
|
19829
|
-
return
|
|
19830
|
-
strategy: "indexed_rates",
|
|
19831
|
-
tradeType,
|
|
19832
|
-
amountIn,
|
|
19833
|
-
amountOut,
|
|
19834
|
-
quoteMetadata: {
|
|
19835
|
-
sourceChain: rates.sourceChain,
|
|
19836
|
-
destinationChain: rates.destinationChain,
|
|
19837
|
-
baseTokenSymbol: rates.baseTokenSymbol,
|
|
19838
|
-
quoteTokenSymbol: rates.quoteTokenSymbol,
|
|
19839
|
-
rateSide: selectedRate.side,
|
|
19840
|
-
rate: selectedRate.rate,
|
|
19841
|
-
rateUpdatedAt: selectedRate.updatedAt,
|
|
19842
|
-
protocolFeeBps
|
|
19843
|
-
}
|
|
19844
|
-
};
|
|
19269
|
+
function formatOptional(amount) {
|
|
19270
|
+
return amount === null ? null : format(amount);
|
|
19845
19271
|
}
|
|
19846
19272
|
|
|
19847
19273
|
// src/protocols/intents/IntentGateway.ts
|
|
@@ -19882,8 +19308,10 @@ var IntentGateway = class _IntentGateway {
|
|
|
19882
19308
|
bidManager;
|
|
19883
19309
|
/** Estimates gas costs for filling an order and converts them to fee-token amounts. */
|
|
19884
19310
|
gasEstimator;
|
|
19885
|
-
/**
|
|
19886
|
-
|
|
19311
|
+
/** The HyperFX orderbook quotes, liquidity and rates are read from. */
|
|
19312
|
+
orderbook;
|
|
19313
|
+
/** Prices intents, liquidity and rates from {@link orderbook}. */
|
|
19314
|
+
market;
|
|
19887
19315
|
/**
|
|
19888
19316
|
* Private constructor — use {@link IntentGateway.create} instead.
|
|
19889
19317
|
*
|
|
@@ -19927,17 +19355,8 @@ var IntentGateway = class _IntentGateway {
|
|
|
19927
19355
|
this.bidManager = bidManager;
|
|
19928
19356
|
this.gasEstimator = gasEstimator;
|
|
19929
19357
|
this._crypto = crypto;
|
|
19930
|
-
this.
|
|
19931
|
-
|
|
19932
|
-
dest.configService,
|
|
19933
|
-
() => this.requireIndexer().queryClient
|
|
19934
|
-
),
|
|
19935
|
-
phantom_snapshot: new PhantomSnapshotIntentQuoteStrategy(
|
|
19936
|
-
dest.configService,
|
|
19937
|
-
() => this.requireIndexer().queryClient
|
|
19938
|
-
),
|
|
19939
|
-
uniswap_v4: new UniswapV4IntentQuoteStrategy(dest.configService)
|
|
19940
|
-
};
|
|
19358
|
+
this.orderbook = new HyperFxOrderbook(orderbookUrlFor(dest.config.stateMachineId));
|
|
19359
|
+
this.market = new OrderbookMarket(dest.configService, () => this.orderbook);
|
|
19941
19360
|
}
|
|
19942
19361
|
/**
|
|
19943
19362
|
* Creates an initialized IntentGateway instance.
|
|
@@ -19983,89 +19402,50 @@ var IntentGateway = class _IntentGateway {
|
|
|
19983
19402
|
}
|
|
19984
19403
|
}
|
|
19985
19404
|
/**
|
|
19986
|
-
*
|
|
19987
|
-
*
|
|
19988
|
-
*
|
|
19989
|
-
* `strategy: "phantom_snapshot"` or `strategy: "uniswap_v4"` only when
|
|
19990
|
-
* explicitly requesting a legacy quote source. Provide exactly one of
|
|
19991
|
-
* `amountIn` or `amountOut`.
|
|
19992
|
-
*
|
|
19993
|
-
* The gateway's source and destination chains resolve the configured order
|
|
19994
|
-
* tokens; the indexer supplies the depth-weighted pool rate. Returned
|
|
19995
|
-
* `amountIn`/`amountOut` already account for the gateway's protocol fee
|
|
19996
|
-
* (`quoteMetadata.protocolFeeBps`), which the gateway deducts from order inputs.
|
|
19405
|
+
* Points quotes, liquidity and rate reads at a HyperFX orderbook other than
|
|
19406
|
+
* the destination chain's network deployment ({@link ORDERBOOK_URLS}).
|
|
19407
|
+
* Returns `this` for chaining.
|
|
19997
19408
|
*
|
|
19998
|
-
* @param
|
|
19999
|
-
* @returns The quoted amounts plus strategy-specific metadata.
|
|
20000
|
-
* @throws {UnsupportedIntentQuoteStrategyError} For unknown strategies.
|
|
20001
|
-
* @throws {UnsupportedIntentQuotePairError} When the selected strategy does not support the pair.
|
|
20002
|
-
* @throws {IndexedRateUnavailableError} When the requested direction has no indexed rate.
|
|
19409
|
+
* @param orderbook - The orderbook's GraphQL URL, or a configured client.
|
|
20003
19410
|
*/
|
|
19411
|
+
withOrderbook(orderbook) {
|
|
19412
|
+
this.orderbook = typeof orderbook === "string" ? new HyperFxOrderbook(orderbook) : orderbook;
|
|
19413
|
+
return this;
|
|
19414
|
+
}
|
|
20004
19415
|
async quoteIntent(params) {
|
|
20005
|
-
|
|
20006
|
-
const destination = { stateMachineId: this.dest.config.stateMachineId, client: this.dest.client };
|
|
20007
|
-
const strategy = params.strategy ?? "indexed_rates";
|
|
20008
|
-
const handler = this.quoteStrategies[strategy];
|
|
20009
|
-
if (!handler) throw new UnsupportedIntentQuoteStrategyError(strategy);
|
|
20010
|
-
return handler.quote({ ...params, strategy }, source, destination);
|
|
19416
|
+
return this.market.quoteIntent(params, this.source.config.stateMachineId, this.dest.config.stateMachineId);
|
|
20011
19417
|
}
|
|
20012
19418
|
/**
|
|
20013
|
-
* Returns
|
|
19419
|
+
* Returns the orderbook liquidity serving a swap of `tokenIn` on the source
|
|
19420
|
+
* chain for `tokenOut` on the destination chain: the best rate, true and
|
|
19421
|
+
* virtual depth, and the largest input the route can fill.
|
|
20014
19422
|
*
|
|
20015
|
-
*
|
|
20016
|
-
* from the indexer's pair-centric liquidity entities. The SDK does not decide
|
|
20017
|
-
* whether unrestricted bidders cover the source chain. Amounts reflect the
|
|
20018
|
-
* latest rolling sample; they are not reservations or fill guarantees.
|
|
20019
|
-
*
|
|
20020
|
-
* Requires a prior call to {@link withQueryClient}.
|
|
19423
|
+
* Amounts reflect the live book; they are not reservations or fill guarantees.
|
|
20021
19424
|
*/
|
|
20022
19425
|
async queryAvailableLiquidity(params) {
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
|
|
20026
|
-
|
|
20027
|
-
if (!destinationStateMachineId) throw new UnsupportedLiquidityChainError(this.dest.config.stateMachineId);
|
|
20028
|
-
const sourceToken = this.source.configService.getAssetMetadataByAddress(sourceStateMachineId, params.tokenIn);
|
|
20029
|
-
const destinationToken = this.dest.configService.getAssetMetadataByAddress(
|
|
20030
|
-
destinationStateMachineId,
|
|
20031
|
-
params.tokenOut
|
|
19426
|
+
return this.market.availableLiquidity(
|
|
19427
|
+
params,
|
|
19428
|
+
this.source.config.stateMachineId,
|
|
19429
|
+
this.dest.config.stateMachineId
|
|
20032
19430
|
);
|
|
20033
|
-
if (!sourceToken) throw new UnsupportedLiquidityAssetError(sourceStateMachineId, params.tokenIn);
|
|
20034
|
-
if (!destinationToken) throw new UnsupportedLiquidityAssetError(destinationStateMachineId, params.tokenOut);
|
|
20035
|
-
return new LiquidityEngine(queryClient).getAvailableLiquidity({
|
|
20036
|
-
source: {
|
|
20037
|
-
chain: sourceStateMachineId,
|
|
20038
|
-
...sourceToken
|
|
20039
|
-
},
|
|
20040
|
-
destination: {
|
|
20041
|
-
chain: destinationStateMachineId,
|
|
20042
|
-
...destinationToken
|
|
20043
|
-
}
|
|
20044
|
-
});
|
|
20045
19431
|
}
|
|
20046
19432
|
/**
|
|
20047
|
-
* Returns
|
|
20048
|
-
* units
|
|
20049
|
-
*
|
|
19433
|
+
* Returns the orderbook's best bid and ask for a pair, with the mid and
|
|
19434
|
+
* spread, in quote-token units per one base token. Only orders filled on the
|
|
19435
|
+
* destination chain that accept swaps from the source chain are counted.
|
|
19436
|
+
* Symbols are matched case-insensitively; chain IDs resolve configured token
|
|
19437
|
+
* deployments.
|
|
20050
19438
|
*/
|
|
20051
19439
|
async queryBuyAndSellRates(params) {
|
|
20052
|
-
const { queryClient } = this.requireIndexer();
|
|
20053
19440
|
const sourceChain = chainConfigs[params.sourceChainId]?.stateMachineId;
|
|
20054
19441
|
const destinationChain = chainConfigs[params.destinationChainId]?.stateMachineId;
|
|
20055
19442
|
if (!sourceChain) throw new UnsupportedLiquidityChainError(params.sourceChainId);
|
|
20056
19443
|
if (!destinationChain) throw new UnsupportedLiquidityChainError(params.destinationChainId);
|
|
20057
|
-
|
|
20058
|
-
const destinationToken = this.dest.configService.getAssetMetadataBySymbol(
|
|
20059
|
-
destinationChain,
|
|
20060
|
-
params.tokenOutSymbol
|
|
20061
|
-
);
|
|
20062
|
-
if (!sourceToken) throw new UnsupportedLiquidityAssetError(sourceChain, params.tokenInSymbol);
|
|
20063
|
-
if (!destinationToken) throw new UnsupportedLiquidityAssetError(destinationChain, params.tokenOutSymbol);
|
|
20064
|
-
return new LiquidityEngine(queryClient).getBuyAndSellRates({
|
|
19444
|
+
return this.market.buyAndSellRates({
|
|
20065
19445
|
sourceChain,
|
|
20066
19446
|
destinationChain,
|
|
20067
|
-
tokenInSymbol:
|
|
20068
|
-
tokenOutSymbol:
|
|
19447
|
+
tokenInSymbol: params.tokenInSymbol,
|
|
19448
|
+
tokenOutSymbol: params.tokenOutSymbol
|
|
20069
19449
|
});
|
|
20070
19450
|
}
|
|
20071
19451
|
/**
|
|
@@ -20316,6 +19696,8 @@ var IntentGateway = class _IntentGateway {
|
|
|
20316
19696
|
/**
|
|
20317
19697
|
* Returns both the native token cost and the relayer fee for cancelling an
|
|
20318
19698
|
* order. Use `relayerFee` to approve the ERC-20 spend before submitting.
|
|
19699
|
+
* Same-chain orders use the direct local route for either `from` option and
|
|
19700
|
+
* return `{ nativeValue: 0n, relayerFee: 0n }`.
|
|
20319
19701
|
*
|
|
20320
19702
|
* Delegates to {@link OrderCanceller.quoteCancelOrder}.
|
|
20321
19703
|
*
|
|
@@ -20339,9 +19721,17 @@ var IntentGateway = class _IntentGateway {
|
|
|
20339
19721
|
*
|
|
20340
19722
|
* Delegates to {@link OrderCanceller.cancelOrder}.
|
|
20341
19723
|
*
|
|
19724
|
+
* Same-chain orders always use the direct source-gateway route. The order
|
|
19725
|
+
* user may call through the deadline; any account may call strictly after
|
|
19726
|
+
* it, pays the transaction gas, and cannot change the refund beneficiary.
|
|
19727
|
+
* The SDK yields unsigned transaction fields and accepts either the caller's
|
|
19728
|
+
* signed raw transaction or its already-broadcast transaction hash.
|
|
19729
|
+
*
|
|
20342
19730
|
* @param order - The order to cancel.
|
|
20343
|
-
* @param indexerClient - Indexer client used for ISMP request
|
|
20344
|
-
*
|
|
19731
|
+
* @param indexerClient - Indexer client used for cross-chain ISMP request
|
|
19732
|
+
* status streaming; the same-chain route does not access it.
|
|
19733
|
+
* @param options - Choose the cross-chain initiation side. Defaults to source;
|
|
19734
|
+
* ignored for routing when source and destination are the same chain.
|
|
20345
19735
|
* @yields {@link CancelEvent} objects describing each cancellation stage.
|
|
20346
19736
|
*/
|
|
20347
19737
|
async *cancelOrder(order, indexerClient, options = {}) {
|
|
@@ -20471,7 +19861,7 @@ var IntentGateway = class _IntentGateway {
|
|
|
20471
19861
|
if (estimate.totalGasCostWei === 0n || estimate.totalGasInFeeToken === 0n) {
|
|
20472
19862
|
throw new Error("Gas estimation failed");
|
|
20473
19863
|
}
|
|
20474
|
-
const fees = isSameChain ? estimate.totalGasInFeeToken * 2n : (estimate.totalGasInFeeToken + estimate.relayerFeeInSourceFeeToken) * 105n / 100n;
|
|
19864
|
+
const fees = isSameChain ? estimate.totalGasInFeeToken * 2n : ((estimate.totalGasInFeeToken + estimate.relayerFeeInSourceFeeToken) * 105n + 99n) / 100n;
|
|
20475
19865
|
const { address: feeToken } = await this.source.getFeeTokenWithDecimals();
|
|
20476
19866
|
return {
|
|
20477
19867
|
fees,
|
|
@@ -20504,17 +19894,27 @@ var IntentGateway = class _IntentGateway {
|
|
|
20504
19894
|
return this._crypto.decodeERC7821Execute(callData);
|
|
20505
19895
|
}
|
|
20506
19896
|
/**
|
|
20507
|
-
* Checks whether an order
|
|
19897
|
+
* Checks whether an order is finalized on the destination chain.
|
|
20508
19898
|
*
|
|
20509
19899
|
* Delegates to {@link OrderStatusChecker.isOrderFilled}.
|
|
20510
19900
|
*
|
|
20511
19901
|
* @param order - The order to check.
|
|
20512
|
-
* @returns `true`
|
|
20513
|
-
*
|
|
19902
|
+
* @returns `true` once a completing fill or a destination-side cancellation
|
|
19903
|
+
* has finalized the order; `false` while it is open or only partly filled.
|
|
20514
19904
|
*/
|
|
20515
19905
|
async isOrderFilled(order) {
|
|
20516
19906
|
return this.orderStatusChecker.isOrderFilled(order);
|
|
20517
19907
|
}
|
|
19908
|
+
/**
|
|
19909
|
+
* Output credited to the order so far, one entry per leg.
|
|
19910
|
+
*
|
|
19911
|
+
* Delegates to {@link OrderStatusChecker.getFillProgress}.
|
|
19912
|
+
*
|
|
19913
|
+
* @param order - The order to check.
|
|
19914
|
+
*/
|
|
19915
|
+
async getFillProgress(order) {
|
|
19916
|
+
return this.orderStatusChecker.getFillProgress(order);
|
|
19917
|
+
}
|
|
20518
19918
|
/**
|
|
20519
19919
|
* Checks whether all escrowed inputs for an order have been refunded on the
|
|
20520
19920
|
* source chain.
|
|
@@ -20522,8 +19922,8 @@ var IntentGateway = class _IntentGateway {
|
|
|
20522
19922
|
* Delegates to {@link OrderStatusChecker.isOrderRefunded}.
|
|
20523
19923
|
*
|
|
20524
19924
|
* @param order - The order to check.
|
|
20525
|
-
* @returns `true` if every input
|
|
20526
|
-
* in the `_orders` mapping on the source chain.
|
|
19925
|
+
* @returns `true` if every input's escrowed amount has been zeroed out
|
|
19926
|
+
* in the per-leg `_orders` mapping on the source chain.
|
|
20527
19927
|
*/
|
|
20528
19928
|
async isOrderRefunded(order) {
|
|
20529
19929
|
return this.orderStatusChecker.isOrderRefunded(order);
|
|
@@ -20560,9 +19960,7 @@ var IntentGateway = class _IntentGateway {
|
|
|
20560
19960
|
}
|
|
20561
19961
|
requireIndexer() {
|
|
20562
19962
|
if (!this.indexer) {
|
|
20563
|
-
throw new Error(
|
|
20564
|
-
"IntentGateway: call withQueryClient(queryClient) before using indexer-backed methods or Phantom quotes"
|
|
20565
|
-
);
|
|
19963
|
+
throw new Error("IntentGateway: call withQueryClient(queryClient) before using indexer-backed methods");
|
|
20566
19964
|
}
|
|
20567
19965
|
return this.indexer;
|
|
20568
19966
|
}
|
|
@@ -20585,74 +19983,64 @@ var IntentGateway = class _IntentGateway {
|
|
|
20585
19983
|
const { queryClient, pollInterval, logger } = this.requireIndexer();
|
|
20586
19984
|
const streamLogger = logger.withTag("[orderStatusStream]");
|
|
20587
19985
|
const TERMINAL = ["FILLED", "REDEEMED", "REFUNDED"];
|
|
19986
|
+
const latestOrderStatus = (order2) => {
|
|
19987
|
+
const latest = order2.statuses[order2.statuses.length - 1];
|
|
19988
|
+
if (latest.status === "CANCELLED") {
|
|
19989
|
+
for (let i = order2.statuses.length - 1; i >= 0; i--) {
|
|
19990
|
+
if (TERMINAL.includes(order2.statuses[i].status)) return order2.statuses[i];
|
|
19991
|
+
}
|
|
19992
|
+
}
|
|
19993
|
+
return latest;
|
|
19994
|
+
};
|
|
20588
19995
|
let order;
|
|
20589
19996
|
while (!order) {
|
|
20590
19997
|
await sleep(pollInterval);
|
|
20591
19998
|
order = await _queryOrderInternal({ commitmentHash: commitment, queryClient, logger });
|
|
20592
19999
|
}
|
|
20593
20000
|
streamLogger.trace("`Order` found");
|
|
20594
|
-
|
|
20001
|
+
let latestStatus = latestOrderStatus(order);
|
|
20595
20002
|
yield { status: latestStatus.status, metadata: latestStatus.metadata };
|
|
20596
20003
|
if (TERMINAL.includes(latestStatus.status)) return;
|
|
20597
20004
|
while (true) {
|
|
20598
20005
|
await sleep(pollInterval);
|
|
20599
20006
|
const updatedOrder = await _queryOrderInternal({ commitmentHash: commitment, queryClient, logger });
|
|
20600
20007
|
if (!updatedOrder) continue;
|
|
20601
|
-
const newLatestStatus = updatedOrder
|
|
20008
|
+
const newLatestStatus = latestOrderStatus(updatedOrder);
|
|
20602
20009
|
if (newLatestStatus.status !== latestStatus.status) {
|
|
20010
|
+
latestStatus = newLatestStatus;
|
|
20603
20011
|
yield { status: newLatestStatus.status, metadata: newLatestStatus.metadata };
|
|
20604
20012
|
if (TERMINAL.includes(newLatestStatus.status)) return;
|
|
20605
20013
|
}
|
|
20606
20014
|
}
|
|
20607
20015
|
}
|
|
20608
20016
|
};
|
|
20609
|
-
|
|
20610
|
-
|
|
20611
|
-
|
|
20612
|
-
|
|
20613
|
-
}
|
|
20614
|
-
return f;
|
|
20017
|
+
|
|
20018
|
+
// src/protocols/intents/liquidity-pool.ts
|
|
20019
|
+
function sortPoolSymbols(symbolA, symbolB) {
|
|
20020
|
+
return symbolA.toLowerCase() <= symbolB.toLowerCase() ? [symbolA, symbolB] : [symbolB, symbolA];
|
|
20615
20021
|
}
|
|
20616
|
-
|
|
20617
|
-
|
|
20618
|
-
|
|
20619
|
-
|
|
20620
|
-
|
|
20621
|
-
|
|
20622
|
-
|
|
20623
|
-
}
|
|
20624
|
-
|
|
20625
|
-
|
|
20626
|
-
|
|
20627
|
-
|
|
20628
|
-
|
|
20629
|
-
|
|
20630
|
-
|
|
20631
|
-
|
|
20632
|
-
|
|
20633
|
-
|
|
20634
|
-
|
|
20635
|
-
|
|
20636
|
-
|
|
20637
|
-
body: JSON.stringify(payload)
|
|
20638
|
-
}),
|
|
20639
|
-
timeout
|
|
20640
|
-
]);
|
|
20641
|
-
const body = await response.json();
|
|
20642
|
-
if (body?.error) {
|
|
20643
|
-
lastErr = new Error(`rpc error: ${JSON.stringify(body.error).slice(0, 200)}`);
|
|
20644
|
-
continue;
|
|
20645
|
-
}
|
|
20646
|
-
return body;
|
|
20647
|
-
} catch (err) {
|
|
20648
|
-
lastErr = err;
|
|
20649
|
-
} finally {
|
|
20650
|
-
if (timer) clearTimeout(timer);
|
|
20651
|
-
}
|
|
20652
|
-
}
|
|
20653
|
-
throw new PhantomRpcError(`RPC call failed after 4 attempts: ${url}`, lastErr);
|
|
20022
|
+
function poolSlug(symbolA, symbolB) {
|
|
20023
|
+
return sortPoolSymbols(symbolA, symbolB).join("-");
|
|
20024
|
+
}
|
|
20025
|
+
|
|
20026
|
+
// src/protocols/intents/rateFill.ts
|
|
20027
|
+
function ceilDiv(numerator, denominator) {
|
|
20028
|
+
return numerator === 0n ? 0n : (numerator - 1n) / denominator + 1n;
|
|
20029
|
+
}
|
|
20030
|
+
function previewRateFill(escrow, required, filled, take, offered) {
|
|
20031
|
+
if (escrow <= 0n || required <= 0n || take <= 0n || offered <= 0n) {
|
|
20032
|
+
throw new Error("RateFillInvalidAmount");
|
|
20033
|
+
}
|
|
20034
|
+
if (filled < 0n || filled >= required) throw new Error("RateFillNoProgress");
|
|
20035
|
+
if (ceilDiv(take * required, escrow) > offered) throw new Error("RateBelowOrder");
|
|
20036
|
+
const uncappedCredit = take * required / escrow;
|
|
20037
|
+
const credit = uncappedCredit < required - filled ? uncappedCredit : required - filled;
|
|
20038
|
+
const release = escrow * (filled + credit) / required - escrow * filled / required;
|
|
20039
|
+
if (credit === 0n || release === 0n) throw new Error("RateFillNoProgress");
|
|
20040
|
+
const ratePayment = ceilDiv(offered * release, take);
|
|
20041
|
+
const delivered = ratePayment > credit ? ratePayment : credit;
|
|
20042
|
+
return { credit, release, delivered, surplus: delivered - credit };
|
|
20654
20043
|
}
|
|
20655
|
-
var FILL_ORDER_ABI = IntentGatewayV2_default.ABI;
|
|
20656
20044
|
var DECLARATION_V1 = 1;
|
|
20657
20045
|
var DECLARATION_V2 = 2;
|
|
20658
20046
|
var PAYMASTER_ADDRESS_BYTES = 20;
|
|
@@ -20850,40 +20238,6 @@ function encodeAcceptedSourceChains(chains2) {
|
|
|
20850
20238
|
function decodeAcceptedSourceChains(paymasterAndData) {
|
|
20851
20239
|
return decodePhantomBidDeclaration(paymasterAndData).acceptedSources;
|
|
20852
20240
|
}
|
|
20853
|
-
var UNISWAP_QUOTE_HAIRCUT_BPS = 10n;
|
|
20854
|
-
function haircut(amount, bps) {
|
|
20855
|
-
return amount * (10000n - bps) / 10000n;
|
|
20856
|
-
}
|
|
20857
|
-
function applyUniswapQuoteHaircut(amount) {
|
|
20858
|
-
return haircut(amount, UNISWAP_QUOTE_HAIRCUT_BPS);
|
|
20859
|
-
}
|
|
20860
|
-
var SELECTOR_GATEWAY_PARAMS = "0xcff0ab96";
|
|
20861
|
-
var GATEWAY_PARAMS_WORDS = 6;
|
|
20862
|
-
var GATEWAY_PARAMS_FEE_WORD = 4;
|
|
20863
|
-
async function readProtocolFeeHaircutBps(evmRpcUrl, gatewayAddress) {
|
|
20864
|
-
const result = await rpcCall(evmRpcUrl, {
|
|
20865
|
-
id: 1,
|
|
20866
|
-
jsonrpc: "2.0",
|
|
20867
|
-
method: "eth_call",
|
|
20868
|
-
params: [{ to: gatewayAddress, data: SELECTOR_GATEWAY_PARAMS }, "latest"]
|
|
20869
|
-
});
|
|
20870
|
-
const hex = result.result;
|
|
20871
|
-
if (typeof hex !== "string" || hex.length < 2 + 64 * GATEWAY_PARAMS_WORDS) {
|
|
20872
|
-
throw new PhantomRpcError(`IntentGateway.params() returned no usable result from ${gatewayAddress} on ${evmRpcUrl}`);
|
|
20873
|
-
}
|
|
20874
|
-
const start = 2 + 64 * GATEWAY_PARAMS_FEE_WORD;
|
|
20875
|
-
const protocolFeeBps = BigInt(`0x${hex.slice(start, start + 64)}`);
|
|
20876
|
-
if (protocolFeeBps >= 10000n) {
|
|
20877
|
-
throw new PhantomRpcError(`IntentGateway ${gatewayAddress} reports an implausible protocol fee: ${protocolFeeBps} bps`);
|
|
20878
|
-
}
|
|
20879
|
-
return protocolFeeBps;
|
|
20880
|
-
}
|
|
20881
|
-
function applyProtocolFeeHaircut(amount, protocolFeeBps) {
|
|
20882
|
-
return haircut(amount, protocolFeeBps);
|
|
20883
|
-
}
|
|
20884
|
-
FILL_ORDER_ABI.find(
|
|
20885
|
-
(item) => item?.type === "function" && item?.name === "fillOrder"
|
|
20886
|
-
)?.inputs?.[0];
|
|
20887
20241
|
var TokenGateway = class {
|
|
20888
20242
|
source;
|
|
20889
20243
|
dest;
|
|
@@ -25099,9 +24453,9 @@ async function teleport(teleport_param) {
|
|
|
25099
24453
|
if (scaleEncodedAssetId === null) {
|
|
25100
24454
|
throw new Error("Unknown asset id provided");
|
|
25101
24455
|
}
|
|
25102
|
-
const
|
|
25103
|
-
const destination = convertStateMachineIdToEnum(
|
|
25104
|
-
const recipient = hexToBytes(normalizeAddressForStateMachine(params.recipient,
|
|
24456
|
+
const destinationStateMachine2 = normalizeStateMachineId(params.destination);
|
|
24457
|
+
const destination = convertStateMachineIdToEnum(destinationStateMachine2);
|
|
24458
|
+
const recipient = hexToBytes(normalizeAddressForStateMachine(params.recipient, destinationStateMachine2));
|
|
25105
24459
|
const teleportParams = {
|
|
25106
24460
|
destination,
|
|
25107
24461
|
recepient: Array.from(recipient),
|
|
@@ -25354,6 +24708,6 @@ async function teleportDot(param_) {
|
|
|
25354
24708
|
return stream;
|
|
25355
24709
|
}
|
|
25356
24710
|
|
|
25357
|
-
export { ADDRESS_ZERO2 as ADDRESS_ZERO, BundlerMethod,
|
|
24711
|
+
export { ADDRESS_ZERO2 as ADDRESS_ZERO, BundlerMethod, CONTRACT_VERSION_ABI, ChainConfigService, Chains, CryptoUtils, DEFAULT_ADDRESS, DEFAULT_GRAFFITI, DOMAIN_TYPEHASH, DUMMY_PRIVATE_KEY, ERC20Method, ERC7821_BATCH_MODE, EvmChain, ABI as EvmHostABI, EvmLanguage, FILL_ORDER_SELECTOR, HyperClientStatus, HyperFungibleToken, HyperFungibleTokenABI, HyperFxOrderbook, INCLUSION_TIMEOUT_MS, InsufficientOrderbookLiquidityError, IntentGateway, ABI3 as IntentGatewayABI, IntentOrderStatus, IntentsCoprocessor, IsmpClient, MAX_DECLARED_ENTRIES, MOCK_ADDRESS, ORDERBOOK_DECIMALS, ORDERBOOK_QUERIES, ORDERBOOK_URLS, ORDER_V2_PARAM_TYPE, OrderStatus, OrderStatusChecker, OrderbookQuoteNotConvergedError, OrderbookRequestError, PACKED_USEROP_TYPEHASH, PERMIT2_SPONSORSHIP_BYTES, PLACE_ORDER_SELECTOR, PharosChain, PolkadotHubChain, REQUEST_COMMITMENTS_SLOT, REQUEST_RECEIPTS_SLOT, RESPONSE_COMMITMENTS_SLOT, RESPONSE_RECEIPTS_SLOT, RequestKind, RequestStatus, SELECT_SOLVER_TYPEHASH, STATE_COMMITMENTS_SLOT, SUPPORTED_INTENTS_VERSION, SubstrateChain, Swap, TESTNET_CHAINS, TeleportStatus, TimeoutStatus, TokenGateway, TronChain, USE_ETHERSCAN_CHAINS, UnsupportedLiquidityAssetError, UnsupportedLiquidityChainError, WrappedHyperFungibleTokenABI, __test, adjustDecimals, assertGatewayRelease, bytes20ToBytes32, bytes32ToBytes20, calculateAllowanceMappingLocation, calculateBalanceMappingLocation, chainConfigs, constructRedeemEscrowRequestBody, constructRefundEscrowRequestBody, convertCodecToIGetRequest, convertCodecToIProof, convertIGetRequestToCodec, convertIProofToCodec, convertStateIdToStateMachineId, convertStateMachineEnumToString, convertStateMachineIdToEnum, createEvmChain, createQueryClient, decodeAcceptedSourceChains, decodeERC7821ExecuteBatch, decodeFillOrder, decodePhantomBidDeclaration, decodePhantomBidPaymasterAndData, decodeUserOpScale, deriveHttpUrl, encodeAcceptedSourceChains, encodeERC7821ExecuteBatch, encodeFillOrder, encodeISMPMessage, encodePhantomBidDeclaration, encodePhantomBidPaymasterAndData, encodeStateMachineId, encodeUserOpScale, encodeWithdrawalRequest, estimateGasForPost, fetchPrice, fetchSourceProof, generateRootWithProof, getChainId, getConfigByStateMachineId, getContractCallInput, getContractCallInputs, getGasPriceFromEtherscan, getOrFetchStorageSlot, getOrderPlacedFromTx, getPostRequestEventFromTx, getPostResponseEventFromTx, getRequestCommitment, getStateCommitmentFieldSlot, getStateCommitmentSlot, getStorageSlot, getViemChain, hexToString, hyperbridgeAddress, isCanonicalEvmToken, maxBigInt, normalizeAddressForEvmBytes32, normalizeAddressForStateMachine, normalizeEvmAddress, normalizeEvmChainId, normalizeStateMachineId, orderCommitment, orderbookUrlFor, parseStateMachineId, pharosAtlantic, pharosMainnet, polkadotAssetHubPaseo, polkadotHubMainnet, poolSlug, postRequestCommitment, previewRateFill, queryAssetTeleported, queryGetRequest, queryPostRequest, quoteUniswap, readLegEscrow, readLegPartialFill, requestCommitmentKey, responseCommitmentKey, retryPromise, sortPoolSymbols, supportsRateFills, teleport, teleportDot, transformOrderForContract, tronChainIds, tronNile };
|
|
25358
24712
|
//# sourceMappingURL=index.js.map
|
|
25359
24713
|
//# sourceMappingURL=index.js.map
|