@hyperbridge/sdk 2.2.0 → 2.2.3
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 +4383 -4121
- package/dist/browser/index.js +1074 -533
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +1074 -531
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +4383 -4121
- package/dist/node/index.d.ts +4383 -4121
- package/dist/node/index.js +1074 -533
- package/dist/node/index.js.map +1 -1
- package/package.json +1 -1
package/dist/node/index.cjs
CHANGED
|
@@ -2579,7 +2579,7 @@ var chainConfigs = {
|
|
|
2579
2579
|
USDT: { balanceSlot: 151, allowanceSlot: 152 }
|
|
2580
2580
|
},
|
|
2581
2581
|
addresses: {
|
|
2582
|
-
IntentGateway: "
|
|
2582
|
+
IntentGateway: "0xE13fB34CAe12505ae51BaC8C405AF8EB27AC8058",
|
|
2583
2583
|
TokenGateway: "0xFcDa26cA021d5535C3059547390E6cCd8De7acA6",
|
|
2584
2584
|
Host: "0xEB944071A9Bf22810757C5BcFf7a2aE9663a311D",
|
|
2585
2585
|
UniswapRouter02: "0x9639379819420704457B07A0C33B678D9E0F8Df0",
|
|
@@ -2589,7 +2589,7 @@ var chainConfigs = {
|
|
|
2589
2589
|
UniswapV3Quoter: "0x0000000000000000000000000000000000000000",
|
|
2590
2590
|
UniswapV4Quoter: "0x0000000000000000000000000000000000000000",
|
|
2591
2591
|
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
2592
|
-
SolverAccount: "
|
|
2592
|
+
SolverAccount: "0x0b5cfBc16ef60AD6930ba5A90Bb09475B7BF3815"
|
|
2593
2593
|
},
|
|
2594
2594
|
rpcEnvKey: "BSC_CHAPEL",
|
|
2595
2595
|
defaultRpcUrl: "https://bnb-testnet.api.onfinality.io/public",
|
|
@@ -2696,8 +2696,8 @@ var chainConfigs = {
|
|
|
2696
2696
|
DAI: { balanceSlot: 0, allowanceSlot: 0 }
|
|
2697
2697
|
},
|
|
2698
2698
|
addresses: {
|
|
2699
|
-
IntentGateway: "
|
|
2700
|
-
SolverAccount: "
|
|
2699
|
+
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2700
|
+
SolverAccount: "0x975e80B476cB1d4Cd06c292ce36898f2bE4159ea",
|
|
2701
2701
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
2702
2702
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2703
2703
|
UniswapRouter02: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D",
|
|
@@ -2751,8 +2751,8 @@ var chainConfigs = {
|
|
|
2751
2751
|
DAI: { balanceSlot: 0, allowanceSlot: 0 }
|
|
2752
2752
|
},
|
|
2753
2753
|
addresses: {
|
|
2754
|
-
IntentGateway: "
|
|
2755
|
-
SolverAccount: "
|
|
2754
|
+
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2755
|
+
SolverAccount: "0x975e80B476cB1d4Cd06c292ce36898f2bE4159ea",
|
|
2756
2756
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
2757
2757
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2758
2758
|
UniswapRouter02: "0x10ED43C718714eb63d5aA57B78B54704E256024E",
|
|
@@ -2807,8 +2807,8 @@ var chainConfigs = {
|
|
|
2807
2807
|
DAI: { balanceSlot: 0, allowanceSlot: 0 }
|
|
2808
2808
|
},
|
|
2809
2809
|
addresses: {
|
|
2810
|
-
IntentGateway: "
|
|
2811
|
-
SolverAccount: "
|
|
2810
|
+
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2811
|
+
SolverAccount: "0x975e80B476cB1d4Cd06c292ce36898f2bE4159ea",
|
|
2812
2812
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
2813
2813
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2814
2814
|
UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
|
|
@@ -2864,8 +2864,8 @@ var chainConfigs = {
|
|
|
2864
2864
|
DAI: { balanceSlot: 0, allowanceSlot: 0 }
|
|
2865
2865
|
},
|
|
2866
2866
|
addresses: {
|
|
2867
|
-
IntentGateway: "
|
|
2868
|
-
SolverAccount: "
|
|
2867
|
+
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2868
|
+
SolverAccount: "0x975e80B476cB1d4Cd06c292ce36898f2bE4159ea",
|
|
2869
2869
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
2870
2870
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2871
2871
|
UniswapRouter02: "0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24",
|
|
@@ -2889,6 +2889,7 @@ var chainConfigs = {
|
|
|
2889
2889
|
consensusStateId: "ETH0",
|
|
2890
2890
|
coingeckoId: "base",
|
|
2891
2891
|
layerZeroEid: 30184,
|
|
2892
|
+
uniswapV4Pools: [{ tokens: ["USDC", "cNGN"], fee: 1500, tickSpacing: 30 }],
|
|
2892
2893
|
popularTokens: [
|
|
2893
2894
|
"0x4200000000000000000000000000000000000006",
|
|
2894
2895
|
"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
@@ -2922,8 +2923,8 @@ var chainConfigs = {
|
|
|
2922
2923
|
DAI: { balanceSlot: 0, allowanceSlot: 0 }
|
|
2923
2924
|
},
|
|
2924
2925
|
addresses: {
|
|
2925
|
-
IntentGateway: "
|
|
2926
|
-
SolverAccount: "
|
|
2926
|
+
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
2927
|
+
SolverAccount: "0x975e80B476cB1d4Cd06c292ce36898f2bE4159ea",
|
|
2927
2928
|
TokenGateway: "0x8b536105b6Fae2aE9199f5146D3C57Dfe53b614E",
|
|
2928
2929
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
2929
2930
|
UniswapRouter02: "0xd2f9496824951D5237cC71245D659E48d0d5f9E8",
|
|
@@ -2993,7 +2994,7 @@ var chainConfigs = {
|
|
|
2993
2994
|
//wmatic, change it to wpol
|
|
2994
2995
|
DAI: "0x0000000000000000000000000000000000000000",
|
|
2995
2996
|
USDC: "0x693b854d6965ffeaae21c74049dea644b56fcacb",
|
|
2996
|
-
USDT: "
|
|
2997
|
+
USDT: "0x0000000000000000000000000000000000000000"
|
|
2997
2998
|
},
|
|
2998
2999
|
tokenDecimals: {
|
|
2999
3000
|
USDC: 18,
|
|
@@ -3004,13 +3005,13 @@ var chainConfigs = {
|
|
|
3004
3005
|
USDC: { balanceSlot: 1, allowanceSlot: 2 }
|
|
3005
3006
|
},
|
|
3006
3007
|
addresses: {
|
|
3007
|
-
IntentGateway: "
|
|
3008
|
+
IntentGateway: "0xE13fB34CAe12505ae51BaC8C405AF8EB27AC8058",
|
|
3008
3009
|
TokenGateway: "0x8b536105b6Fae2aE9199f5146D3C57Dfe53b614E",
|
|
3009
3010
|
Host: "0xEB944071A9Bf22810757C5BcFf7a2aE9663a311D",
|
|
3010
3011
|
Calldispatcher: "0x876F1891982E260026630c233A4897160A281Fb8",
|
|
3011
3012
|
Permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
3012
3013
|
EntryPointV08: "0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108",
|
|
3013
|
-
SolverAccount: "
|
|
3014
|
+
SolverAccount: "0x0b5cfBc16ef60AD6930ba5A90Bb09475B7BF3815"
|
|
3014
3015
|
},
|
|
3015
3016
|
rpcEnvKey: "POLYGON_AMOY",
|
|
3016
3017
|
defaultRpcUrl: "https://rpc-amoy.polygon.technology",
|
|
@@ -3033,8 +3034,8 @@ var chainConfigs = {
|
|
|
3033
3034
|
USDT: 6
|
|
3034
3035
|
},
|
|
3035
3036
|
addresses: {
|
|
3036
|
-
IntentGateway: "
|
|
3037
|
-
SolverAccount: "
|
|
3037
|
+
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
3038
|
+
SolverAccount: "0x975e80B476cB1d4Cd06c292ce36898f2bE4159ea",
|
|
3038
3039
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
3039
3040
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
3040
3041
|
UniswapRouter02: "0x4A7b5Da61326A6379179b40d00F57E5bbDC962c2",
|
|
@@ -3068,8 +3069,8 @@ var chainConfigs = {
|
|
|
3068
3069
|
USDT: 6
|
|
3069
3070
|
},
|
|
3070
3071
|
addresses: {
|
|
3071
|
-
IntentGateway: "
|
|
3072
|
-
SolverAccount: "
|
|
3072
|
+
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
3073
|
+
SolverAccount: "0x975e80B476cB1d4Cd06c292ce36898f2bE4159ea",
|
|
3073
3074
|
TokenGateway: "0xFd413e3AFe560182C4471F4d143A96d3e259B6dE",
|
|
3074
3075
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
3075
3076
|
UniswapRouter02: "0xB2e26652e4BAd1e56055A051f922E06760cA0BFE",
|
|
@@ -3223,8 +3224,8 @@ var chainConfigs = {
|
|
|
3223
3224
|
USDT: 6
|
|
3224
3225
|
},
|
|
3225
3226
|
addresses: {
|
|
3226
|
-
IntentGateway: "
|
|
3227
|
-
SolverAccount: "
|
|
3227
|
+
IntentGateway: "0xAe041F7B0CB581876832830baeB6a2Aa2a3C9716",
|
|
3228
|
+
SolverAccount: "0x975e80B476cB1d4Cd06c292ce36898f2bE4159ea",
|
|
3228
3229
|
Host: "0x620128E2B19193d6Bd244a3AC8D3bBa0541B19c3",
|
|
3229
3230
|
Calldispatcher: "0xE2C7e576E26E0bE7aC97c6fE925bcDAbD87c4bEd"
|
|
3230
3231
|
},
|
|
@@ -5102,6 +5103,9 @@ var ChainConfigService = class {
|
|
|
5102
5103
|
getDaiAsset(chain) {
|
|
5103
5104
|
return this.getConfig(chain)?.assets?.DAI ?? "0x";
|
|
5104
5105
|
}
|
|
5106
|
+
getAssetAddress(chain, symbol) {
|
|
5107
|
+
return this.getConfig(chain)?.assets?.[symbol];
|
|
5108
|
+
}
|
|
5105
5109
|
getUsdtAsset(chain) {
|
|
5106
5110
|
return this.getConfig(chain)?.assets?.USDT ?? "0x";
|
|
5107
5111
|
}
|
|
@@ -5170,6 +5174,9 @@ var ChainConfigService = class {
|
|
|
5170
5174
|
getUniswapV4StateViewAddress(chain) {
|
|
5171
5175
|
return this.getConfig(chain)?.addresses.UniswapV4StateView ?? "0x";
|
|
5172
5176
|
}
|
|
5177
|
+
getUniswapV4PoolConfigs(chain) {
|
|
5178
|
+
return this.getConfig(chain)?.uniswapV4Pools ?? [];
|
|
5179
|
+
}
|
|
5173
5180
|
getPermit2Address(chain) {
|
|
5174
5181
|
return this.getConfig(chain)?.addresses.Permit2 ?? "0x";
|
|
5175
5182
|
}
|
|
@@ -5180,7 +5187,7 @@ var ChainConfigService = class {
|
|
|
5180
5187
|
return this.getConfig(chain)?.coingeckoId;
|
|
5181
5188
|
}
|
|
5182
5189
|
getEtherscanApiKey() {
|
|
5183
|
-
return typeof process !== "undefined" ? process
|
|
5190
|
+
return typeof process !== "undefined" ? process.env?.ETHERSCAN_API_KEY : void 0;
|
|
5184
5191
|
}
|
|
5185
5192
|
getCalldispatcherAddress(chain) {
|
|
5186
5193
|
return this.getConfig(chain)?.addresses.Calldispatcher ?? "0x";
|
|
@@ -9549,7 +9556,28 @@ function adjustDecimals(feeInFeeToken, fromDecimals, toDecimals) {
|
|
|
9549
9556
|
}
|
|
9550
9557
|
}
|
|
9551
9558
|
var USE_ETHERSCAN_CHAINS = /* @__PURE__ */ new Set(["EVM-137", "EVM-56", "EVM-1"]);
|
|
9552
|
-
var TESTNET_CHAINS = /* @__PURE__ */ new Set([
|
|
9559
|
+
var TESTNET_CHAINS = /* @__PURE__ */ new Set([
|
|
9560
|
+
"EVM-97",
|
|
9561
|
+
// BSC Chapel
|
|
9562
|
+
"EVM-10200",
|
|
9563
|
+
// Gnosis Chiado
|
|
9564
|
+
"EVM-11155111",
|
|
9565
|
+
// Sepolia
|
|
9566
|
+
"EVM-421614",
|
|
9567
|
+
// Arbitrum Sepolia
|
|
9568
|
+
"EVM-84532",
|
|
9569
|
+
// Base Sepolia
|
|
9570
|
+
"EVM-11155420",
|
|
9571
|
+
// Optimism Sepolia
|
|
9572
|
+
"EVM-80002",
|
|
9573
|
+
// Polygon Amoy
|
|
9574
|
+
"EVM-420420417",
|
|
9575
|
+
// Polkadot Asset Hub Paseo
|
|
9576
|
+
"EVM-3448148188",
|
|
9577
|
+
// Tron Nile
|
|
9578
|
+
"EVM-688689"
|
|
9579
|
+
// Pharos Atlantic
|
|
9580
|
+
]);
|
|
9553
9581
|
function collectCallInputsByAddress(call, targetContractAddress, acc) {
|
|
9554
9582
|
const normalizedTarget = targetContractAddress.toLowerCase();
|
|
9555
9583
|
if (call.calls && Array.isArray(call.calls)) {
|
|
@@ -9864,6 +9892,48 @@ var GetRequestClient = class {
|
|
|
9864
9892
|
async buildFinalized(request, hyperbridgeDelivered, response) {
|
|
9865
9893
|
const sourceChain = this.ctx.config.source;
|
|
9866
9894
|
const hyperbridge = this.ctx.config.hyperbridge;
|
|
9895
|
+
const { config } = hyperbridge;
|
|
9896
|
+
const finality = await this.queries.queryStateMachineUpdateByHeight({
|
|
9897
|
+
statemachineId: config.stateMachineId,
|
|
9898
|
+
height: hyperbridgeDelivered.metadata.blockNumber,
|
|
9899
|
+
chain: config.stateMachineId
|
|
9900
|
+
});
|
|
9901
|
+
if (finality) {
|
|
9902
|
+
const proof = await hyperbridge.queryProof(
|
|
9903
|
+
{ Responses: [response.commitment] },
|
|
9904
|
+
request.source,
|
|
9905
|
+
BigInt(finality.height)
|
|
9906
|
+
);
|
|
9907
|
+
const calldata = sourceChain.encode({
|
|
9908
|
+
kind: "GetResponse",
|
|
9909
|
+
proof: {
|
|
9910
|
+
stateMachine: config.stateMachineId,
|
|
9911
|
+
consensusStateId: config.consensusStateId,
|
|
9912
|
+
proof,
|
|
9913
|
+
height: BigInt(finality.height)
|
|
9914
|
+
},
|
|
9915
|
+
responses: [
|
|
9916
|
+
{
|
|
9917
|
+
get: request,
|
|
9918
|
+
values: request.keys.map((key, index) => ({
|
|
9919
|
+
key,
|
|
9920
|
+
value: response.values[index] || "0x"
|
|
9921
|
+
}))
|
|
9922
|
+
}
|
|
9923
|
+
],
|
|
9924
|
+
signer: viem.pad("0x")
|
|
9925
|
+
});
|
|
9926
|
+
return {
|
|
9927
|
+
status: RequestStatus.HYPERBRIDGE_FINALIZED,
|
|
9928
|
+
metadata: {
|
|
9929
|
+
blockHash: finality.blockHash,
|
|
9930
|
+
blockNumber: finality.height,
|
|
9931
|
+
transactionHash: finality.transactionHash,
|
|
9932
|
+
timestamp: finality.timestamp,
|
|
9933
|
+
calldata
|
|
9934
|
+
}
|
|
9935
|
+
};
|
|
9936
|
+
}
|
|
9867
9937
|
if (sourceChain instanceof EvmChain) {
|
|
9868
9938
|
const hyperbridgeSubstrate = hyperbridge;
|
|
9869
9939
|
const currentEpoch = await sourceChain.currentEpoch();
|
|
@@ -9872,18 +9942,18 @@ var GetRequestClient = class {
|
|
|
9872
9942
|
currentEpoch
|
|
9873
9943
|
);
|
|
9874
9944
|
if (!consensusResult) return void 0;
|
|
9875
|
-
const
|
|
9945
|
+
const proof = await hyperbridge.queryProof(
|
|
9876
9946
|
{ Responses: [response.commitment] },
|
|
9877
9947
|
request.source,
|
|
9878
9948
|
consensusResult.provenHeight
|
|
9879
9949
|
);
|
|
9880
|
-
const
|
|
9950
|
+
const calldata = sourceChain.encode({
|
|
9881
9951
|
kind: "BatchConsensusAndGetResponse",
|
|
9882
9952
|
consensusProofs: consensusResult.proofs,
|
|
9883
9953
|
proof: {
|
|
9884
|
-
stateMachine:
|
|
9885
|
-
consensusStateId:
|
|
9886
|
-
proof
|
|
9954
|
+
stateMachine: config.stateMachineId,
|
|
9955
|
+
consensusStateId: config.consensusStateId,
|
|
9956
|
+
proof,
|
|
9887
9957
|
height: consensusResult.provenHeight
|
|
9888
9958
|
},
|
|
9889
9959
|
responses: [
|
|
@@ -9905,50 +9975,11 @@ var GetRequestClient = class {
|
|
|
9905
9975
|
transactionHash: hyperbridgeDelivered.metadata.transactionHash,
|
|
9906
9976
|
// @ts-ignore
|
|
9907
9977
|
timestamp: hyperbridgeDelivered.metadata.timestamp,
|
|
9908
|
-
calldata
|
|
9978
|
+
calldata
|
|
9909
9979
|
}
|
|
9910
9980
|
};
|
|
9911
9981
|
}
|
|
9912
|
-
|
|
9913
|
-
statemachineId: this.ctx.config.hyperbridge.config.stateMachineId,
|
|
9914
|
-
height: hyperbridgeDelivered.metadata.blockNumber,
|
|
9915
|
-
chain: request.source
|
|
9916
|
-
});
|
|
9917
|
-
if (!hyperbridgeFinality) return void 0;
|
|
9918
|
-
const proof = await hyperbridge.queryProof(
|
|
9919
|
-
{ Responses: [response.commitment] },
|
|
9920
|
-
request.source,
|
|
9921
|
-
BigInt(hyperbridgeFinality.height)
|
|
9922
|
-
);
|
|
9923
|
-
const calldata = sourceChain.encode({
|
|
9924
|
-
kind: "GetResponse",
|
|
9925
|
-
proof: {
|
|
9926
|
-
stateMachine: this.ctx.config.hyperbridge.config.stateMachineId,
|
|
9927
|
-
consensusStateId: this.ctx.config.hyperbridge.config.consensusStateId,
|
|
9928
|
-
proof,
|
|
9929
|
-
height: BigInt(hyperbridgeFinality.height)
|
|
9930
|
-
},
|
|
9931
|
-
responses: [
|
|
9932
|
-
{
|
|
9933
|
-
get: request,
|
|
9934
|
-
values: request.keys.map((key, index) => ({
|
|
9935
|
-
key,
|
|
9936
|
-
value: response.values[index] || "0x"
|
|
9937
|
-
}))
|
|
9938
|
-
}
|
|
9939
|
-
],
|
|
9940
|
-
signer: viem.pad("0x")
|
|
9941
|
-
});
|
|
9942
|
-
return {
|
|
9943
|
-
status: RequestStatus.HYPERBRIDGE_FINALIZED,
|
|
9944
|
-
metadata: {
|
|
9945
|
-
blockHash: hyperbridgeFinality.blockHash,
|
|
9946
|
-
blockNumber: hyperbridgeFinality.height,
|
|
9947
|
-
transactionHash: hyperbridgeFinality.transactionHash,
|
|
9948
|
-
timestamp: hyperbridgeFinality.timestamp,
|
|
9949
|
-
calldata
|
|
9950
|
-
}
|
|
9951
|
-
};
|
|
9982
|
+
return void 0;
|
|
9952
9983
|
}
|
|
9953
9984
|
/**
|
|
9954
9985
|
* Streaming helper: waits (via `waitOrAbort`) for the consensus proof or
|
|
@@ -9963,7 +9994,12 @@ var GetRequestClient = class {
|
|
|
9963
9994
|
const hyperbridge = this.ctx.config.hyperbridge;
|
|
9964
9995
|
const stateMachineId = this.ctx.config.hyperbridge.config.stateMachineId;
|
|
9965
9996
|
const neededHeight = BigInt(request.statuses[hyperbridgeDeliveredIndex].metadata.blockNumber);
|
|
9966
|
-
|
|
9997
|
+
let finality = await this.queries.queryStateMachineUpdateByHeight({
|
|
9998
|
+
statemachineId: stateMachineId,
|
|
9999
|
+
height: Number(neededHeight),
|
|
10000
|
+
chain: stateMachineId
|
|
10001
|
+
});
|
|
10002
|
+
if (!finality && sourceChain instanceof EvmChain) {
|
|
9967
10003
|
const hyperbridgeSubstrate = hyperbridge;
|
|
9968
10004
|
const currentEpoch = await sourceChain.currentEpoch();
|
|
9969
10005
|
const consensusResult = await waitOrAbort(this.ctx, {
|
|
@@ -10007,18 +10043,20 @@ var GetRequestClient = class {
|
|
|
10007
10043
|
}
|
|
10008
10044
|
};
|
|
10009
10045
|
}
|
|
10010
|
-
|
|
10011
|
-
|
|
10012
|
-
|
|
10013
|
-
|
|
10014
|
-
|
|
10015
|
-
|
|
10016
|
-
|
|
10017
|
-
|
|
10046
|
+
if (!finality) {
|
|
10047
|
+
finality = await waitOrAbort(this.ctx, {
|
|
10048
|
+
signal,
|
|
10049
|
+
promise: () => this.queries.queryStateMachineUpdateByHeight({
|
|
10050
|
+
statemachineId: stateMachineId,
|
|
10051
|
+
height: Number(neededHeight),
|
|
10052
|
+
chain: stateMachineId
|
|
10053
|
+
})
|
|
10054
|
+
});
|
|
10055
|
+
}
|
|
10018
10056
|
const proof = await hyperbridge.queryProof(
|
|
10019
10057
|
{ Responses: [response?.commitment] },
|
|
10020
10058
|
request.source,
|
|
10021
|
-
BigInt(
|
|
10059
|
+
BigInt(finality.height)
|
|
10022
10060
|
);
|
|
10023
10061
|
const calldata = sourceChain.encode({
|
|
10024
10062
|
kind: "GetResponse",
|
|
@@ -10026,7 +10064,7 @@ var GetRequestClient = class {
|
|
|
10026
10064
|
stateMachine: stateMachineId,
|
|
10027
10065
|
consensusStateId: this.ctx.config.hyperbridge.config.consensusStateId,
|
|
10028
10066
|
proof,
|
|
10029
|
-
height: BigInt(
|
|
10067
|
+
height: BigInt(finality.height)
|
|
10030
10068
|
},
|
|
10031
10069
|
responses: [
|
|
10032
10070
|
{
|
|
@@ -10042,10 +10080,10 @@ var GetRequestClient = class {
|
|
|
10042
10080
|
return {
|
|
10043
10081
|
status: RequestStatus.HYPERBRIDGE_FINALIZED,
|
|
10044
10082
|
metadata: {
|
|
10045
|
-
blockHash:
|
|
10046
|
-
blockNumber:
|
|
10047
|
-
transactionHash:
|
|
10048
|
-
timestamp:
|
|
10083
|
+
blockHash: finality.blockHash,
|
|
10084
|
+
blockNumber: finality.height,
|
|
10085
|
+
transactionHash: finality.transactionHash,
|
|
10086
|
+
timestamp: finality.timestamp,
|
|
10049
10087
|
calldata
|
|
10050
10088
|
}
|
|
10051
10089
|
};
|
|
@@ -10234,26 +10272,27 @@ var PostRequestClient = class {
|
|
|
10234
10272
|
* accompanying timeout-proof calldata.
|
|
10235
10273
|
*/
|
|
10236
10274
|
async addTimeoutFinalityEvents(request) {
|
|
10237
|
-
const destChain = this.ctx.config.dest;
|
|
10238
|
-
const hyperbridge = this.ctx.config.hyperbridge;
|
|
10239
10275
|
const events = [];
|
|
10240
|
-
const commitment = postRequestCommitment(request).commitment;
|
|
10241
|
-
const receipt = await destChain.queryRequestReceipt(commitment);
|
|
10242
|
-
const destTimestamp = await destChain.timestamp();
|
|
10243
10276
|
const commit = (req) => {
|
|
10244
10277
|
this.logger.trace(`Added ${events.length} timeout events`, events);
|
|
10245
10278
|
request.statuses = [...req.statuses, ...events];
|
|
10246
10279
|
return request;
|
|
10247
10280
|
};
|
|
10248
10281
|
if (request.timeoutTimestamp === 0n) return commit(request);
|
|
10282
|
+
if (request.statuses.some(
|
|
10283
|
+
(item) => item.status === RequestStatus.DESTINATION || item.status === TimeoutStatus.TIMED_OUT
|
|
10284
|
+
))
|
|
10285
|
+
return commit(request);
|
|
10286
|
+
const destChain = this.ctx.config.dest;
|
|
10287
|
+
const hyperbridge = this.ctx.config.hyperbridge;
|
|
10288
|
+
const commitment = postRequestCommitment(request).commitment;
|
|
10289
|
+
const receipt = await destChain.queryRequestReceipt(commitment);
|
|
10290
|
+
const destTimestamp = await destChain.timestamp();
|
|
10249
10291
|
if (receipt || request.timeoutTimestamp > destTimestamp) return commit(request);
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
metadata: { blockHash: "0x", blockNumber: 0, transactionHash: "0x" }
|
|
10255
|
-
});
|
|
10256
|
-
}
|
|
10292
|
+
events.push({
|
|
10293
|
+
status: TimeoutStatus.PENDING_TIMEOUT,
|
|
10294
|
+
metadata: { blockHash: "0x", blockNumber: 0, transactionHash: "0x" }
|
|
10295
|
+
});
|
|
10257
10296
|
const delivered = request.statuses.find((item) => item.status === RequestStatus.HYPERBRIDGE_DELIVERED);
|
|
10258
10297
|
let hyperbridgeFinalized;
|
|
10259
10298
|
if (!delivered) {
|
|
@@ -10544,6 +10583,40 @@ var PostRequestClient = class {
|
|
|
10544
10583
|
async buildFinalized(request, hyperbridgeDelivered) {
|
|
10545
10584
|
const destChain = this.ctx.config.dest;
|
|
10546
10585
|
const hyperbridge = this.ctx.config.hyperbridge;
|
|
10586
|
+
const { config } = hyperbridge;
|
|
10587
|
+
const finality = await this.queries.queryStateMachineUpdateByHeight({
|
|
10588
|
+
statemachineId: config.stateMachineId,
|
|
10589
|
+
height: hyperbridgeDelivered.metadata.blockNumber,
|
|
10590
|
+
chain: config.stateMachineId
|
|
10591
|
+
});
|
|
10592
|
+
if (finality) {
|
|
10593
|
+
const proof = await hyperbridge.queryProof(
|
|
10594
|
+
{ Requests: [postRequestCommitment(request).commitment] },
|
|
10595
|
+
request.dest,
|
|
10596
|
+
BigInt(finality.height)
|
|
10597
|
+
);
|
|
10598
|
+
const calldata = destChain.encode({
|
|
10599
|
+
kind: "PostRequest",
|
|
10600
|
+
proof: {
|
|
10601
|
+
stateMachine: config.stateMachineId,
|
|
10602
|
+
consensusStateId: config.consensusStateId,
|
|
10603
|
+
proof,
|
|
10604
|
+
height: BigInt(finality.height)
|
|
10605
|
+
},
|
|
10606
|
+
requests: [request],
|
|
10607
|
+
signer: viem.pad("0x")
|
|
10608
|
+
});
|
|
10609
|
+
return {
|
|
10610
|
+
status: RequestStatus.HYPERBRIDGE_FINALIZED,
|
|
10611
|
+
metadata: {
|
|
10612
|
+
blockHash: finality.blockHash,
|
|
10613
|
+
blockNumber: finality.height,
|
|
10614
|
+
transactionHash: finality.transactionHash,
|
|
10615
|
+
timestamp: finality.timestamp,
|
|
10616
|
+
calldata
|
|
10617
|
+
}
|
|
10618
|
+
};
|
|
10619
|
+
}
|
|
10547
10620
|
if (destChain instanceof EvmChain) {
|
|
10548
10621
|
const hyperbridgeSubstrate = hyperbridge;
|
|
10549
10622
|
const currentEpoch = await destChain.currentEpoch();
|
|
@@ -10552,18 +10625,18 @@ var PostRequestClient = class {
|
|
|
10552
10625
|
currentEpoch
|
|
10553
10626
|
);
|
|
10554
10627
|
if (!consensusResult) return void 0;
|
|
10555
|
-
const
|
|
10628
|
+
const proof = await hyperbridge.queryProof(
|
|
10556
10629
|
{ Requests: [postRequestCommitment(request).commitment] },
|
|
10557
10630
|
request.dest,
|
|
10558
10631
|
consensusResult.provenHeight
|
|
10559
10632
|
);
|
|
10560
|
-
const
|
|
10633
|
+
const calldata = destChain.encode({
|
|
10561
10634
|
kind: "BatchConsensusAndPostRequest",
|
|
10562
10635
|
consensusProofs: consensusResult.proofs,
|
|
10563
10636
|
proof: {
|
|
10564
|
-
stateMachine:
|
|
10565
|
-
consensusStateId:
|
|
10566
|
-
proof
|
|
10637
|
+
stateMachine: config.stateMachineId,
|
|
10638
|
+
consensusStateId: config.consensusStateId,
|
|
10639
|
+
proof,
|
|
10567
10640
|
height: consensusResult.provenHeight
|
|
10568
10641
|
},
|
|
10569
10642
|
requests: [request],
|
|
@@ -10577,42 +10650,11 @@ var PostRequestClient = class {
|
|
|
10577
10650
|
transactionHash: hyperbridgeDelivered.metadata.transactionHash,
|
|
10578
10651
|
// @ts-ignore
|
|
10579
10652
|
timestamp: hyperbridgeDelivered.metadata.timestamp,
|
|
10580
|
-
calldata
|
|
10653
|
+
calldata
|
|
10581
10654
|
}
|
|
10582
10655
|
};
|
|
10583
10656
|
}
|
|
10584
|
-
|
|
10585
|
-
statemachineId: this.ctx.config.hyperbridge.config.stateMachineId,
|
|
10586
|
-
height: hyperbridgeDelivered.metadata.blockNumber,
|
|
10587
|
-
chain: request.dest
|
|
10588
|
-
});
|
|
10589
|
-
if (!hyperbridgeFinality) return void 0;
|
|
10590
|
-
const proof = await hyperbridge.queryProof(
|
|
10591
|
-
{ Requests: [postRequestCommitment(request).commitment] },
|
|
10592
|
-
request.dest,
|
|
10593
|
-
BigInt(hyperbridgeFinality.height)
|
|
10594
|
-
);
|
|
10595
|
-
const calldata = destChain.encode({
|
|
10596
|
-
kind: "PostRequest",
|
|
10597
|
-
proof: {
|
|
10598
|
-
stateMachine: this.ctx.config.hyperbridge.config.stateMachineId,
|
|
10599
|
-
consensusStateId: this.ctx.config.hyperbridge.config.consensusStateId,
|
|
10600
|
-
proof,
|
|
10601
|
-
height: BigInt(hyperbridgeFinality.height)
|
|
10602
|
-
},
|
|
10603
|
-
requests: [request],
|
|
10604
|
-
signer: viem.pad("0x")
|
|
10605
|
-
});
|
|
10606
|
-
return {
|
|
10607
|
-
status: RequestStatus.HYPERBRIDGE_FINALIZED,
|
|
10608
|
-
metadata: {
|
|
10609
|
-
blockHash: hyperbridgeFinality.blockHash,
|
|
10610
|
-
blockNumber: hyperbridgeFinality.height,
|
|
10611
|
-
transactionHash: hyperbridgeFinality.transactionHash,
|
|
10612
|
-
timestamp: hyperbridgeFinality.timestamp,
|
|
10613
|
-
calldata
|
|
10614
|
-
}
|
|
10615
|
-
};
|
|
10657
|
+
return void 0;
|
|
10616
10658
|
}
|
|
10617
10659
|
/**
|
|
10618
10660
|
* Streaming helper: waits for the consensus proof, fetches the messaging
|
|
@@ -10626,14 +10668,19 @@ var PostRequestClient = class {
|
|
|
10626
10668
|
const stateMachineId = this.ctx.config.hyperbridge.config.stateMachineId;
|
|
10627
10669
|
const neededHeight = BigInt(request.statuses[hyperbridgeDeliveredIndex].metadata.blockNumber);
|
|
10628
10670
|
this.logger.trace(`[streamFinalized] neededHeight=${neededHeight}`);
|
|
10629
|
-
|
|
10671
|
+
const commitment = postRequestCommitment(request).commitment;
|
|
10672
|
+
let finality = await this.queries.queryStateMachineUpdateByHeight({
|
|
10673
|
+
statemachineId: stateMachineId,
|
|
10674
|
+
height: Number(neededHeight),
|
|
10675
|
+
chain: stateMachineId
|
|
10676
|
+
});
|
|
10677
|
+
if (!finality && destChain instanceof EvmChain) {
|
|
10630
10678
|
const hyperbridgeSubstrate = hyperbridge;
|
|
10631
10679
|
const currentEpoch = await destChain.currentEpoch();
|
|
10632
10680
|
const consensusResult = await waitOrAbort(this.ctx, {
|
|
10633
10681
|
signal,
|
|
10634
10682
|
promise: () => hyperbridgeSubstrate.queryConsensusProofs(neededHeight, currentEpoch)
|
|
10635
10683
|
});
|
|
10636
|
-
const commitment = postRequestCommitment(request).commitment;
|
|
10637
10684
|
this.logger.trace(
|
|
10638
10685
|
`[streamFinalized] consensusProofs found (${consensusResult.proofs.length} proofs), provenHeight=${consensusResult.provenHeight}, commitment=${commitment}, dest=${request.dest}`
|
|
10639
10686
|
);
|
|
@@ -10665,45 +10712,45 @@ var PostRequestClient = class {
|
|
|
10665
10712
|
}
|
|
10666
10713
|
};
|
|
10667
10714
|
}
|
|
10668
|
-
|
|
10669
|
-
|
|
10670
|
-
|
|
10671
|
-
|
|
10672
|
-
|
|
10673
|
-
|
|
10674
|
-
|
|
10675
|
-
|
|
10715
|
+
if (!finality) {
|
|
10716
|
+
finality = await waitOrAbort(this.ctx, {
|
|
10717
|
+
signal,
|
|
10718
|
+
promise: () => this.queries.queryStateMachineUpdateByHeight({
|
|
10719
|
+
statemachineId: stateMachineId,
|
|
10720
|
+
height: Number(neededHeight),
|
|
10721
|
+
chain: stateMachineId
|
|
10722
|
+
})
|
|
10723
|
+
});
|
|
10724
|
+
}
|
|
10676
10725
|
const proof = await this.fetchProofWithRetry(
|
|
10677
10726
|
signal,
|
|
10678
|
-
() => hyperbridge.queryProof(
|
|
10679
|
-
{ Requests: [postRequestCommitment(request).commitment] },
|
|
10680
|
-
request.dest,
|
|
10681
|
-
BigInt(hyperbridgeFinalized.height)
|
|
10682
|
-
)
|
|
10727
|
+
() => hyperbridge.queryProof({ Requests: [commitment] }, request.dest, BigInt(finality.height))
|
|
10683
10728
|
);
|
|
10729
|
+
if (!(destChain instanceof EvmChain)) {
|
|
10730
|
+
const { stateId } = parseStateMachineId(stateMachineId);
|
|
10731
|
+
await waitForChallengePeriod(destChain, {
|
|
10732
|
+
height: BigInt(finality.height),
|
|
10733
|
+
id: { stateId, consensusStateId: this.ctx.config.hyperbridge.config.consensusStateId }
|
|
10734
|
+
});
|
|
10735
|
+
}
|
|
10684
10736
|
const calldata = destChain.encode({
|
|
10685
10737
|
kind: "PostRequest",
|
|
10686
10738
|
proof: {
|
|
10687
10739
|
stateMachine: stateMachineId,
|
|
10688
10740
|
consensusStateId: this.ctx.config.hyperbridge.config.consensusStateId,
|
|
10689
10741
|
proof,
|
|
10690
|
-
height: BigInt(
|
|
10742
|
+
height: BigInt(finality.height)
|
|
10691
10743
|
},
|
|
10692
10744
|
requests: [request],
|
|
10693
10745
|
signer: viem.pad("0x")
|
|
10694
10746
|
});
|
|
10695
|
-
const { stateId } = parseStateMachineId(stateMachineId);
|
|
10696
|
-
await waitForChallengePeriod(destChain, {
|
|
10697
|
-
height: BigInt(hyperbridgeFinalized.height),
|
|
10698
|
-
id: { stateId, consensusStateId: this.ctx.config.hyperbridge.config.consensusStateId }
|
|
10699
|
-
});
|
|
10700
10747
|
return {
|
|
10701
10748
|
status: RequestStatus.HYPERBRIDGE_FINALIZED,
|
|
10702
10749
|
metadata: {
|
|
10703
|
-
blockHash:
|
|
10704
|
-
blockNumber:
|
|
10705
|
-
transactionHash:
|
|
10706
|
-
timestamp:
|
|
10750
|
+
blockHash: finality.blockHash,
|
|
10751
|
+
blockNumber: finality.height,
|
|
10752
|
+
transactionHash: finality.transactionHash,
|
|
10753
|
+
timestamp: finality.timestamp,
|
|
10707
10754
|
calldata
|
|
10708
10755
|
}
|
|
10709
10756
|
};
|
|
@@ -14626,6 +14673,7 @@ function orderCommitment(order) {
|
|
|
14626
14673
|
return viem.keccak256(encoded);
|
|
14627
14674
|
}
|
|
14628
14675
|
async function convertGasToFeeToken(ctx, gasEstimate, gasEstimateIn, evmChainID, gasPriceOverride) {
|
|
14676
|
+
if (TESTNET_CHAINS.has(evmChainID)) return 1n;
|
|
14629
14677
|
const chain = ctx[gasEstimateIn];
|
|
14630
14678
|
const client = chain.client;
|
|
14631
14679
|
const gasPrice = gasPriceOverride ?? await retryPromise(() => client.getGasPrice(), {
|
|
@@ -14660,6 +14708,7 @@ async function convertGasToFeeToken(ctx, gasEstimate, gasEstimateIn, evmChainID,
|
|
|
14660
14708
|
}
|
|
14661
14709
|
}
|
|
14662
14710
|
async function convertFeeTokenToWei(ctx, feeTokenAmount, feeTokenIn, evmChainID) {
|
|
14711
|
+
if (TESTNET_CHAINS.has(evmChainID)) return 1n;
|
|
14663
14712
|
const chain = ctx[feeTokenIn];
|
|
14664
14713
|
const client = chain.client;
|
|
14665
14714
|
const wethAddr = chain.configService.getWrappedNativeAssetWithDecimals(evmChainID).asset;
|
|
@@ -14775,16 +14824,16 @@ var OrderPlacer = class {
|
|
|
14775
14824
|
return { order, receipt };
|
|
14776
14825
|
}
|
|
14777
14826
|
};
|
|
14827
|
+
|
|
14828
|
+
// src/protocols/intents/OrderExecutor.ts
|
|
14778
14829
|
var USED_USEROPS_STORAGE_KEY = (commitment) => `used-userops:${commitment.toLowerCase()}`;
|
|
14779
14830
|
var OrderExecutor = class {
|
|
14780
|
-
constructor(ctx, bidManager
|
|
14831
|
+
constructor(ctx, bidManager) {
|
|
14781
14832
|
this.ctx = ctx;
|
|
14782
14833
|
this.bidManager = bidManager;
|
|
14783
|
-
this.crypto = crypto;
|
|
14784
14834
|
}
|
|
14785
14835
|
ctx;
|
|
14786
14836
|
bidManager;
|
|
14787
|
-
crypto;
|
|
14788
14837
|
/**
|
|
14789
14838
|
* Sleeps until the order's block deadline is reached, then yields EXPIRED.
|
|
14790
14839
|
* Uses the chain's block time to calculate the sleep duration.
|
|
@@ -14855,17 +14904,6 @@ var OrderExecutor = class {
|
|
|
14855
14904
|
}
|
|
14856
14905
|
return fetchedBids;
|
|
14857
14906
|
}
|
|
14858
|
-
/**
|
|
14859
|
-
* Selects the best bid from the provided candidates, submits the
|
|
14860
|
-
* UserOperation, and persists the dedup entry to prevent resubmission.
|
|
14861
|
-
*/
|
|
14862
|
-
async submitBid(params) {
|
|
14863
|
-
const { order, freshBids, sessionPrivateKey, commitment, usedUserOps, userOpHashKey } = params;
|
|
14864
|
-
const result = await this.bidManager.selectBid(order, freshBids, sessionPrivateKey);
|
|
14865
|
-
usedUserOps.add(userOpHashKey(result.userOp));
|
|
14866
|
-
await this.persistUsedUserOps(commitment, usedUserOps);
|
|
14867
|
-
return result;
|
|
14868
|
-
}
|
|
14869
14907
|
/**
|
|
14870
14908
|
* Processes a fill result and returns updated fill accumulators,
|
|
14871
14909
|
* the status update to yield (if any), and whether the order is
|
|
@@ -14933,7 +14971,15 @@ var OrderExecutor = class {
|
|
|
14933
14971
|
}
|
|
14934
14972
|
/**
|
|
14935
14973
|
* Executes an intent order by racing bid polling against the order's
|
|
14936
|
-
* block deadline. Yields status updates at each lifecycle stage
|
|
14974
|
+
* block deadline. Yields status updates at each lifecycle stage and hands
|
|
14975
|
+
* bid selection to the consumer.
|
|
14976
|
+
*
|
|
14977
|
+
* This is a **bidirectional** generator: when it yields `BIDS_RECEIVED`, the
|
|
14978
|
+
* consumer picks a bid, calls `bid.execute()`, and feeds the resulting
|
|
14979
|
+
* {@link SelectBidResult} back via `gen.next(result)`. The generator then
|
|
14980
|
+
* records the dedup entry, emits `BID_SELECTED`, tracks the fill, and either
|
|
14981
|
+
* terminates or continues polling for the remaining amount. Feeding back
|
|
14982
|
+
* `undefined` (no bid executed this round) causes it to keep polling.
|
|
14937
14983
|
*
|
|
14938
14984
|
* **Same-chain:** `AWAITING_BIDS` → `BIDS_RECEIVED` → `BID_SELECTED`
|
|
14939
14985
|
* → (`FILLED` | `PARTIAL_FILL`)* → (`FILLED` | `EXPIRED`)
|
|
@@ -14944,7 +14990,6 @@ var OrderExecutor = class {
|
|
|
14944
14990
|
async *executeOrder(options) {
|
|
14945
14991
|
const { order, sessionPrivateKey, auctionTimeMs, pollIntervalMs = DEFAULT_POLL_INTERVAL, solver } = options;
|
|
14946
14992
|
const commitment = order.id;
|
|
14947
|
-
order.source === order.destination;
|
|
14948
14993
|
if (!this.ctx.intentsCoprocessor) {
|
|
14949
14994
|
yield { status: "FAILED", error: "IntentsCoprocessor required for order execution" };
|
|
14950
14995
|
return;
|
|
@@ -14972,11 +15017,24 @@ var OrderExecutor = class {
|
|
|
14972
15017
|
remainingAssets
|
|
14973
15018
|
});
|
|
14974
15019
|
const deadlineTimeout = this.deadlineStream(order.deadline, commitment);
|
|
14975
|
-
const
|
|
15020
|
+
const deadlinePromise = deadlineTimeout.next();
|
|
14976
15021
|
try {
|
|
14977
|
-
|
|
14978
|
-
|
|
14979
|
-
|
|
15022
|
+
let input;
|
|
15023
|
+
while (true) {
|
|
15024
|
+
const winner = input !== void 0 ? { from: "exec", r: await executionStream.next(input) } : await Promise.race([
|
|
15025
|
+
executionStream.next(void 0).then((r) => ({ from: "exec", r })),
|
|
15026
|
+
deadlinePromise.then((r) => ({ from: "deadline", r }))
|
|
15027
|
+
]);
|
|
15028
|
+
input = void 0;
|
|
15029
|
+
if (winner.from === "deadline") {
|
|
15030
|
+
if (!winner.r.done && winner.r.value) yield winner.r.value;
|
|
15031
|
+
return;
|
|
15032
|
+
}
|
|
15033
|
+
const { value, done } = winner.r;
|
|
15034
|
+
if (done) return;
|
|
15035
|
+
const fed = yield value;
|
|
15036
|
+
if (value.status === "BIDS_RECEIVED") input = fed;
|
|
15037
|
+
if (value.status === "EXPIRED" || value.status === "FILLED") return;
|
|
14980
15038
|
}
|
|
14981
15039
|
} finally {
|
|
14982
15040
|
console.log(`[OrderExecutor] Tearing down streams for commitment=${commitment}`);
|
|
@@ -14985,9 +15043,16 @@ var OrderExecutor = class {
|
|
|
14985
15043
|
}
|
|
14986
15044
|
}
|
|
14987
15045
|
/**
|
|
14988
|
-
* Core execution loop that polls for bids
|
|
14989
|
-
*
|
|
14990
|
-
*
|
|
15046
|
+
* Core execution loop that polls for bids and tracks fill progress. Builds
|
|
15047
|
+
* first-class {@link Bid} objects from the raw filler bids and yields them to
|
|
15048
|
+
* the consumer, which picks one, calls `bid.execute()`, and feeds the result
|
|
15049
|
+
* back via `gen.next(result)`. The loop then records the dedup entry, emits
|
|
15050
|
+
* `BID_SELECTED`, processes the fill, and continues polling for the remaining
|
|
15051
|
+
* amount on partial fills.
|
|
15052
|
+
*
|
|
15053
|
+
* Bidirectional: the value passed to `.next()` after a `BIDS_RECEIVED` yield is
|
|
15054
|
+
* the {@link SelectBidResult} from the executed bid (or `undefined` to skip the
|
|
15055
|
+
* round and keep polling).
|
|
14991
15056
|
*/
|
|
14992
15057
|
async *executionStream(params) {
|
|
14993
15058
|
const {
|
|
@@ -15002,12 +15067,7 @@ var OrderExecutor = class {
|
|
|
15002
15067
|
targetAssets
|
|
15003
15068
|
} = params;
|
|
15004
15069
|
let { totalFilledAssets, remainingAssets } = params;
|
|
15005
|
-
const
|
|
15006
|
-
const bidFailCounts = /* @__PURE__ */ new Map();
|
|
15007
|
-
const isFreshBid = (bid) => {
|
|
15008
|
-
const key = userOpHashKey(bid.userOp);
|
|
15009
|
-
return !usedUserOps.has(key) && (bidFailCounts.get(key) ?? 0) < MAX_BID_ATTEMPTS;
|
|
15010
|
-
};
|
|
15070
|
+
const isFreshBid = (bid) => !usedUserOps.has(userOpHashKey(bid.userOp));
|
|
15011
15071
|
const solverLockStartTime = Date.now();
|
|
15012
15072
|
yield { status: "AWAITING_BIDS", commitment, totalFilledAssets, remainingAssets };
|
|
15013
15073
|
try {
|
|
@@ -15016,13 +15076,13 @@ var OrderExecutor = class {
|
|
|
15016
15076
|
while (Date.now() < auctionEnd) {
|
|
15017
15077
|
try {
|
|
15018
15078
|
const bids = await this.fetchBids({ commitment, solver, solverLockStartTime });
|
|
15019
|
-
const
|
|
15020
|
-
|
|
15021
|
-
|
|
15022
|
-
|
|
15023
|
-
|
|
15024
|
-
|
|
15025
|
-
yield { status: "NEW_BID", commitment,
|
|
15079
|
+
const newBids = bids.filter(
|
|
15080
|
+
(bid) => isFreshBid(bid) && !auctionSeenBids.has(userOpHashKey(bid.userOp))
|
|
15081
|
+
);
|
|
15082
|
+
for (const fillerBid of newBids) {
|
|
15083
|
+
auctionSeenBids.add(userOpHashKey(fillerBid.userOp));
|
|
15084
|
+
const [bid] = this.bidManager.buildBids(order, [fillerBid], sessionPrivateKey);
|
|
15085
|
+
if (bid) yield { status: "NEW_BID", commitment, bid };
|
|
15026
15086
|
}
|
|
15027
15087
|
} catch {
|
|
15028
15088
|
}
|
|
@@ -15034,8 +15094,8 @@ var OrderExecutor = class {
|
|
|
15034
15094
|
while (true) {
|
|
15035
15095
|
let freshBids;
|
|
15036
15096
|
try {
|
|
15037
|
-
const
|
|
15038
|
-
freshBids =
|
|
15097
|
+
const bids2 = await this.fetchBids({ commitment, solver, solverLockStartTime });
|
|
15098
|
+
freshBids = bids2.filter(isFreshBid);
|
|
15039
15099
|
} catch {
|
|
15040
15100
|
await sleep(pollIntervalMs);
|
|
15041
15101
|
continue;
|
|
@@ -15044,56 +15104,33 @@ var OrderExecutor = class {
|
|
|
15044
15104
|
await sleep(pollIntervalMs);
|
|
15045
15105
|
continue;
|
|
15046
15106
|
}
|
|
15047
|
-
|
|
15048
|
-
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
|
|
15054
|
-
commitment,
|
|
15055
|
-
usedUserOps,
|
|
15056
|
-
userOpHashKey
|
|
15057
|
-
});
|
|
15058
|
-
} catch (err) {
|
|
15059
|
-
yield {
|
|
15060
|
-
status: "FAILED",
|
|
15061
|
-
commitment,
|
|
15062
|
-
totalFilledAssets,
|
|
15063
|
-
remainingAssets,
|
|
15064
|
-
error: `Failed to select bid and submit: ${err instanceof Error ? err.message : String(err)}`
|
|
15065
|
-
};
|
|
15066
|
-
try {
|
|
15067
|
-
const sorted = await this.bidManager.validateAndSortBids(freshBids, order);
|
|
15068
|
-
if (sorted.length > 0) {
|
|
15069
|
-
const key = userOpHashKey(sorted[0].bid.userOp);
|
|
15070
|
-
bidFailCounts.set(key, (bidFailCounts.get(key) ?? 0) + 1);
|
|
15071
|
-
}
|
|
15072
|
-
} catch {
|
|
15073
|
-
}
|
|
15107
|
+
const bids = this.bidManager.buildBids(order, freshBids, sessionPrivateKey);
|
|
15108
|
+
if (bids.length === 0) {
|
|
15109
|
+
await sleep(pollIntervalMs);
|
|
15110
|
+
continue;
|
|
15111
|
+
}
|
|
15112
|
+
const result = yield { status: "BIDS_RECEIVED", commitment, bidCount: bids.length, bids };
|
|
15113
|
+
if (!result) {
|
|
15074
15114
|
await sleep(pollIntervalMs);
|
|
15075
15115
|
continue;
|
|
15076
15116
|
}
|
|
15117
|
+
usedUserOps.add(userOpHashKey(result.userOp));
|
|
15118
|
+
await this.persistUsedUserOps(commitment, usedUserOps);
|
|
15077
15119
|
yield {
|
|
15078
15120
|
status: "BID_SELECTED",
|
|
15079
15121
|
commitment,
|
|
15080
|
-
selectedSolver:
|
|
15081
|
-
userOpHash:
|
|
15082
|
-
userOp:
|
|
15083
|
-
transactionHash:
|
|
15122
|
+
selectedSolver: result.solverAddress,
|
|
15123
|
+
userOpHash: result.userOpHash,
|
|
15124
|
+
userOp: result.userOp,
|
|
15125
|
+
transactionHash: result.txnHash
|
|
15084
15126
|
};
|
|
15085
|
-
const fill = this.processFillResult(
|
|
15086
|
-
submitResult,
|
|
15087
|
-
commitment,
|
|
15088
|
-
targetAssets,
|
|
15089
|
-
totalFilledAssets,
|
|
15090
|
-
remainingAssets
|
|
15091
|
-
);
|
|
15127
|
+
const fill = this.processFillResult(result, commitment, targetAssets, totalFilledAssets, remainingAssets);
|
|
15092
15128
|
totalFilledAssets = fill.totalFilledAssets;
|
|
15093
15129
|
remainingAssets = fill.remainingAssets;
|
|
15094
15130
|
if (fill.update) {
|
|
15095
15131
|
yield fill.update;
|
|
15096
15132
|
}
|
|
15133
|
+
if (fill.done) return;
|
|
15097
15134
|
}
|
|
15098
15135
|
} catch (err) {
|
|
15099
15136
|
yield {
|
|
@@ -15568,183 +15605,148 @@ var OrderCanceller = class {
|
|
|
15568
15605
|
return feeInDestFeeToken * 1005n / 1000n;
|
|
15569
15606
|
}
|
|
15570
15607
|
};
|
|
15571
|
-
var
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
*/
|
|
15578
|
-
constructor(ctx, crypto) {
|
|
15579
|
-
this.ctx = ctx;
|
|
15580
|
-
this.crypto = crypto;
|
|
15581
|
-
}
|
|
15608
|
+
var BidImpl = class {
|
|
15609
|
+
solverAddress;
|
|
15610
|
+
outputs;
|
|
15611
|
+
relayerFee;
|
|
15612
|
+
nativeDispatchFee;
|
|
15613
|
+
userOp;
|
|
15582
15614
|
ctx;
|
|
15583
15615
|
crypto;
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15593
|
-
|
|
15594
|
-
|
|
15595
|
-
|
|
15596
|
-
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15616
|
+
order;
|
|
15617
|
+
fillOptions;
|
|
15618
|
+
priceOutputs;
|
|
15619
|
+
sessionPrivateKey;
|
|
15620
|
+
intentGatewayV2Address;
|
|
15621
|
+
domainSeparator;
|
|
15622
|
+
/** Cached session-key signature over the `SelectSolver` message. */
|
|
15623
|
+
cachedSignature;
|
|
15624
|
+
constructor(params) {
|
|
15625
|
+
this.ctx = params.ctx;
|
|
15626
|
+
this.crypto = params.crypto;
|
|
15627
|
+
this.order = params.order;
|
|
15628
|
+
this.fillOptions = params.fillOptions;
|
|
15629
|
+
this.priceOutputs = params.priceOutputs;
|
|
15630
|
+
this.sessionPrivateKey = params.sessionPrivateKey;
|
|
15631
|
+
this.solverAddress = params.fillerBid.userOp.sender;
|
|
15632
|
+
this.outputs = params.fillOptions.outputs;
|
|
15633
|
+
this.relayerFee = params.fillOptions.relayerFee;
|
|
15634
|
+
this.nativeDispatchFee = params.fillOptions.nativeDispatchFee;
|
|
15635
|
+
this.userOp = params.fillerBid.userOp;
|
|
15636
|
+
this.intentGatewayV2Address = this.ctx.dest.configService.getIntentGatewayAddress(
|
|
15637
|
+
normalizeStateMachineId(this.order.destination)
|
|
15638
|
+
);
|
|
15639
|
+
this.domainSeparator = CryptoUtils.getDomainSeparator(
|
|
15640
|
+
"IntentGateway",
|
|
15641
|
+
"2",
|
|
15642
|
+
this.chainId(),
|
|
15643
|
+
this.intentGatewayV2Address
|
|
15644
|
+
);
|
|
15645
|
+
}
|
|
15646
|
+
/** Resolves the destination chain id from the client or the state-machine id. */
|
|
15647
|
+
chainId() {
|
|
15648
|
+
return BigInt(
|
|
15613
15649
|
this.ctx.dest.client.chain?.id ?? Number.parseInt(this.ctx.dest.config.stateMachineId.split("-")[1])
|
|
15614
15650
|
);
|
|
15615
|
-
const accountGasLimits = CryptoUtils.packGasLimits(verificationGasLimit, callGasLimit);
|
|
15616
|
-
const gasFees = CryptoUtils.packGasFees(maxPriorityFeePerGas, maxFeePerGas);
|
|
15617
|
-
const userOp = {
|
|
15618
|
-
sender: solverAccount,
|
|
15619
|
-
nonce,
|
|
15620
|
-
initCode: "0x",
|
|
15621
|
-
callData,
|
|
15622
|
-
accountGasLimits,
|
|
15623
|
-
preVerificationGas,
|
|
15624
|
-
gasFees,
|
|
15625
|
-
paymasterAndData,
|
|
15626
|
-
signature: "0x"
|
|
15627
|
-
};
|
|
15628
|
-
const userOpHash = CryptoUtils.computeUserOpHash(userOp, entryPointAddress, chainId);
|
|
15629
|
-
const sessionKey = order.session;
|
|
15630
|
-
const messageHash = viem.keccak256(viem.concat([userOpHash, order.id, sessionKey]));
|
|
15631
|
-
const solverSignature = await solverSigner.signMessage(messageHash, Number(chainId));
|
|
15632
|
-
const signature = viem.concat([order.id, solverSignature]);
|
|
15633
|
-
return { ...userOp, signature };
|
|
15634
15651
|
}
|
|
15635
15652
|
/**
|
|
15636
|
-
*
|
|
15637
|
-
* solver
|
|
15638
|
-
* UserOperation to the bundler.
|
|
15639
|
-
*
|
|
15640
|
-
* **Selection algorithm:**
|
|
15641
|
-
* 1. Decodes `fillOrder` calldata from each bid's `callData`.
|
|
15642
|
-
* 2. Sorts bids by output value (single-output: amount; all-stables: normalised
|
|
15643
|
-
* USD; mixed: DEX-quoted USD; fallback: raw amount).
|
|
15644
|
-
* 3. Iterates sorted bids, simulating each with `eth_call` until one passes.
|
|
15645
|
-
* 4. Appends the session-key's `SelectSolver` signature to the solver's
|
|
15646
|
-
* existing signature and submits via `eth_sendUserOperation`.
|
|
15647
|
-
* 5. For same-chain orders, waits for the transaction receipt and reads
|
|
15648
|
-
* `OrderFilled` / `PartialFill` events to determine fill status.
|
|
15653
|
+
* Resolves the session key, signs the `SelectSolver` message for this bid's
|
|
15654
|
+
* solver, and caches the signature. Signs at most once per bid.
|
|
15649
15655
|
*
|
|
15650
|
-
* @
|
|
15651
|
-
* @param bids - Raw bids fetched from the Hyperbridge coprocessor.
|
|
15652
|
-
* @param sessionPrivateKey - Optional override; if omitted, the key is
|
|
15653
|
-
* looked up from `sessionKeyStorage` using `order.session`.
|
|
15654
|
-
* @returns A {@link SelectBidResult} containing the submitted UserOperation,
|
|
15655
|
-
* its hash, the winning solver address, transaction hash, and fill status.
|
|
15656
|
-
* @throws If the session key is not found, no valid bids exist, all
|
|
15657
|
-
* simulations fail, or the bundler rejects the UserOperation.
|
|
15656
|
+
* @throws If the session key is missing or signing fails.
|
|
15658
15657
|
*/
|
|
15659
|
-
async
|
|
15660
|
-
|
|
15661
|
-
const
|
|
15662
|
-
|
|
15663
|
-
const sessionKeyData = sessionPrivateKey ? { privateKey: sessionPrivateKey } : await this.ctx.sessionKeyStorage.getSessionKeyByAddress(sessionKeyAddress);
|
|
15658
|
+
async signSelection() {
|
|
15659
|
+
if (this.cachedSignature) return this.cachedSignature;
|
|
15660
|
+
const commitment = this.order.id;
|
|
15661
|
+
const sessionKeyAddress = this.order.session;
|
|
15662
|
+
const sessionKeyData = this.sessionPrivateKey ? { privateKey: this.sessionPrivateKey } : await this.ctx.sessionKeyStorage.getSessionKeyByAddress(sessionKeyAddress);
|
|
15664
15663
|
if (!sessionKeyData) {
|
|
15665
15664
|
throw new Error("SessionKey not found for commitment: " + commitment);
|
|
15666
15665
|
}
|
|
15667
|
-
|
|
15668
|
-
|
|
15666
|
+
const signature = await CryptoUtils.signSolverSelection(
|
|
15667
|
+
commitment,
|
|
15668
|
+
this.solverAddress,
|
|
15669
|
+
this.domainSeparator,
|
|
15670
|
+
sessionKeyData.privateKey
|
|
15671
|
+
);
|
|
15672
|
+
if (!signature) {
|
|
15673
|
+
throw new Error("Failed to sign solver selection");
|
|
15669
15674
|
}
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
|
|
15679
|
-
|
|
15680
|
-
|
|
15681
|
-
|
|
15682
|
-
|
|
15683
|
-
|
|
15684
|
-
|
|
15685
|
-
|
|
15686
|
-
|
|
15687
|
-
|
|
15675
|
+
this.cachedSignature = signature;
|
|
15676
|
+
return signature;
|
|
15677
|
+
}
|
|
15678
|
+
/**
|
|
15679
|
+
* Simulates this bid on-chain by batching the `select` and `fillOrder` calls
|
|
15680
|
+
* via `eth_call` from the solver's account, using the IntentGatewayV2 ERC-7821
|
|
15681
|
+
* batch-execute pattern.
|
|
15682
|
+
*
|
|
15683
|
+
* The native value forwarded to the simulation is the sum of any native-token
|
|
15684
|
+
* (`address(0)`) output amounts plus the Hyperbridge dispatch fee.
|
|
15685
|
+
*
|
|
15686
|
+
* @throws If the `eth_call` simulation reverts or errors.
|
|
15687
|
+
*/
|
|
15688
|
+
async simulate() {
|
|
15689
|
+
const signature = await this.signSelection();
|
|
15690
|
+
const selectOptions = {
|
|
15691
|
+
commitment: this.order.id,
|
|
15692
|
+
solver: this.solverAddress,
|
|
15693
|
+
signature
|
|
15694
|
+
};
|
|
15695
|
+
const nativeOutputs = this.fillOptions.outputs.reduce(
|
|
15696
|
+
(acc, o) => bytes32ToBytes20(o.token) === ADDRESS_ZERO2 ? acc + o.amount : acc,
|
|
15697
|
+
0n
|
|
15688
15698
|
);
|
|
15689
|
-
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
|
|
15694
|
-
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
|
|
15702
|
-
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
|
|
15706
|
-
|
|
15707
|
-
|
|
15708
|
-
|
|
15709
|
-
signature
|
|
15710
|
-
};
|
|
15711
|
-
try {
|
|
15712
|
-
await this.simulate(bidWithOptions.bid, bidWithOptions.options, selectOptions, intentGatewayV2Address);
|
|
15713
|
-
console.log(`[BidManager] Bid ${idx + 1} from solver=${solverAddress2}: simulation PASSED`);
|
|
15714
|
-
selectedBid = bidWithOptions;
|
|
15715
|
-
sessionSignature = signature;
|
|
15716
|
-
break;
|
|
15717
|
-
} catch (err) {
|
|
15718
|
-
console.warn(
|
|
15719
|
-
`[BidManager] Bid ${idx + 1} from solver=${solverAddress2}: simulation FAILED: ${err instanceof Error ? err.message : String(err)}`
|
|
15720
|
-
);
|
|
15721
|
-
continue;
|
|
15722
|
-
}
|
|
15699
|
+
const simulationValue = nativeOutputs + this.fillOptions.nativeDispatchFee;
|
|
15700
|
+
const selectCalldata = viem.encodeFunctionData({
|
|
15701
|
+
abi: ABI3,
|
|
15702
|
+
functionName: "select",
|
|
15703
|
+
args: [selectOptions]
|
|
15704
|
+
});
|
|
15705
|
+
const calls = [
|
|
15706
|
+
{ target: this.intentGatewayV2Address, value: 0n, data: selectCalldata },
|
|
15707
|
+
{ target: this.solverAddress, value: simulationValue, data: this.userOp.callData }
|
|
15708
|
+
];
|
|
15709
|
+
const batchedCalldata = this.crypto.encodeERC7821Execute(calls);
|
|
15710
|
+
try {
|
|
15711
|
+
await this.ctx.dest.client.call({
|
|
15712
|
+
account: this.solverAddress,
|
|
15713
|
+
to: this.solverAddress,
|
|
15714
|
+
data: batchedCalldata,
|
|
15715
|
+
value: simulationValue
|
|
15716
|
+
});
|
|
15717
|
+
} catch (e) {
|
|
15718
|
+
throw new Error(`Simulation failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
15723
15719
|
}
|
|
15724
|
-
|
|
15725
|
-
|
|
15726
|
-
|
|
15720
|
+
}
|
|
15721
|
+
/**
|
|
15722
|
+
* Signs the `SelectSolver` message with the session key, appends it to the
|
|
15723
|
+
* solver's existing UserOp signature, and submits the UserOperation to the
|
|
15724
|
+
* bundler. For same-chain orders, waits for the receipt and reads
|
|
15725
|
+
* `OrderFilled` / `PartialFill` logs to determine fill status.
|
|
15726
|
+
*
|
|
15727
|
+
* @returns A {@link SelectBidResult} with the submitted UserOperation, its hash,
|
|
15728
|
+
* the solver address, transaction hash, and fill status.
|
|
15729
|
+
* @throws If the bundler is not configured, the session key is missing, or the
|
|
15730
|
+
* bundler rejects the UserOperation.
|
|
15731
|
+
*/
|
|
15732
|
+
async execute() {
|
|
15733
|
+
const commitment = this.order.id;
|
|
15734
|
+
if (!this.ctx.bundlerUrl) {
|
|
15735
|
+
throw new Error("Bundler URL not configured");
|
|
15727
15736
|
}
|
|
15728
|
-
const
|
|
15729
|
-
const finalSignature = viem.concat([
|
|
15730
|
-
selectedBid.bid.userOp.signature,
|
|
15731
|
-
sessionSignature
|
|
15732
|
-
]);
|
|
15737
|
+
const sessionSignature = await this.signSelection();
|
|
15738
|
+
const finalSignature = viem.concat([this.userOp.signature, sessionSignature]);
|
|
15733
15739
|
const signedUserOp = {
|
|
15734
|
-
...
|
|
15740
|
+
...this.userOp,
|
|
15735
15741
|
signature: finalSignature
|
|
15736
15742
|
};
|
|
15737
15743
|
const entryPointAddress = this.ctx.dest.configService.getEntryPointV08Address(
|
|
15738
|
-
normalizeStateMachineId(order.destination)
|
|
15744
|
+
normalizeStateMachineId(this.order.destination)
|
|
15739
15745
|
);
|
|
15740
|
-
|
|
15741
|
-
this.ctx.dest.client.chain?.id ?? Number.parseInt(this.ctx.dest.config.stateMachineId.split("-")[1])
|
|
15742
|
-
);
|
|
15743
|
-
const bundlerResult = await this.crypto.sendBundler(BundlerMethod.ETH_SEND_USER_OPERATION, [
|
|
15746
|
+
const userOpHash = await this.crypto.sendBundler(BundlerMethod.ETH_SEND_USER_OPERATION, [
|
|
15744
15747
|
CryptoUtils.prepareBundlerCall(signedUserOp),
|
|
15745
15748
|
entryPointAddress
|
|
15746
15749
|
]);
|
|
15747
|
-
const userOpHash = bundlerResult;
|
|
15748
15750
|
let txnHash;
|
|
15749
15751
|
let fillStatus;
|
|
15750
15752
|
let filledAssets;
|
|
@@ -15760,7 +15762,7 @@ var BidManager = class {
|
|
|
15760
15762
|
{ maxRetries: 5, backoffMs: 2e3, logMessage: "Fetching user operation receipt" }
|
|
15761
15763
|
);
|
|
15762
15764
|
txnHash = receipt.receipt.transactionHash;
|
|
15763
|
-
if (order.source === order.destination) {
|
|
15765
|
+
if (this.order.source === this.order.destination) {
|
|
15764
15766
|
try {
|
|
15765
15767
|
const chainReceipt = await this.ctx.dest.client.waitForTransactionReceipt({
|
|
15766
15768
|
hash: txnHash,
|
|
@@ -15789,12 +15791,12 @@ var BidManager = class {
|
|
|
15789
15791
|
}
|
|
15790
15792
|
}
|
|
15791
15793
|
} catch (err) {
|
|
15792
|
-
throw new Error(`Failed to
|
|
15794
|
+
throw new Error(`Failed to execute bid: ${err instanceof Error ? err.message : String(err)}`);
|
|
15793
15795
|
}
|
|
15794
15796
|
return {
|
|
15795
15797
|
userOp: signedUserOp,
|
|
15796
15798
|
userOpHash,
|
|
15797
|
-
solverAddress,
|
|
15799
|
+
solverAddress: this.solverAddress,
|
|
15798
15800
|
commitment,
|
|
15799
15801
|
txnHash,
|
|
15800
15802
|
fillStatus,
|
|
@@ -15802,7 +15804,177 @@ var BidManager = class {
|
|
|
15802
15804
|
};
|
|
15803
15805
|
}
|
|
15804
15806
|
/**
|
|
15805
|
-
*
|
|
15807
|
+
* Prices this bid's outputs in USD using the destination chain's DEX-quote
|
|
15808
|
+
* helpers. Returns `null` when any output token cannot be priced.
|
|
15809
|
+
*/
|
|
15810
|
+
async outputUsdValue() {
|
|
15811
|
+
return this.priceOutputs(this.outputs);
|
|
15812
|
+
}
|
|
15813
|
+
};
|
|
15814
|
+
var BidManager = class {
|
|
15815
|
+
/**
|
|
15816
|
+
* @param ctx - Shared IntentsV2 context providing the destination chain
|
|
15817
|
+
* client, coprocessor, bundler URL, and session-key storage.
|
|
15818
|
+
* @param crypto - Crypto utilities used for gas packing, UserOp hashing,
|
|
15819
|
+
* EIP-712 signing, and bundler calls.
|
|
15820
|
+
*/
|
|
15821
|
+
constructor(ctx, crypto) {
|
|
15822
|
+
this.ctx = ctx;
|
|
15823
|
+
this.crypto = crypto;
|
|
15824
|
+
}
|
|
15825
|
+
ctx;
|
|
15826
|
+
crypto;
|
|
15827
|
+
/**
|
|
15828
|
+
* Constructs a signed `PackedUserOperation` that a solver can submit to the
|
|
15829
|
+
* Hyperbridge coprocessor as a bid to fill an order.
|
|
15830
|
+
*
|
|
15831
|
+
* The solver's signature covers a hash that binds the UserOperation to the
|
|
15832
|
+
* order commitment and the session key address, so the IntentGatewayV2
|
|
15833
|
+
* contract can verify the solver's intent on-chain.
|
|
15834
|
+
*
|
|
15835
|
+
* @param options - Parameters describing the solver account, gas limits, fee
|
|
15836
|
+
* market values, and pre-built `callData` for the fill operation.
|
|
15837
|
+
* @returns A `PackedUserOperation` with the solver's signature prepended
|
|
15838
|
+
* with the order commitment.
|
|
15839
|
+
*/
|
|
15840
|
+
async prepareSubmitBid(options) {
|
|
15841
|
+
const {
|
|
15842
|
+
order,
|
|
15843
|
+
solverAccount,
|
|
15844
|
+
solverSigner,
|
|
15845
|
+
nonce,
|
|
15846
|
+
entryPointAddress,
|
|
15847
|
+
callGasLimit,
|
|
15848
|
+
verificationGasLimit,
|
|
15849
|
+
preVerificationGas,
|
|
15850
|
+
maxFeePerGas,
|
|
15851
|
+
maxPriorityFeePerGas,
|
|
15852
|
+
callData,
|
|
15853
|
+
paymasterAndData = "0x"
|
|
15854
|
+
} = options;
|
|
15855
|
+
const chainId = BigInt(
|
|
15856
|
+
this.ctx.dest.client.chain?.id ?? Number.parseInt(this.ctx.dest.config.stateMachineId.split("-")[1])
|
|
15857
|
+
);
|
|
15858
|
+
const accountGasLimits = CryptoUtils.packGasLimits(verificationGasLimit, callGasLimit);
|
|
15859
|
+
const gasFees = CryptoUtils.packGasFees(maxPriorityFeePerGas, maxFeePerGas);
|
|
15860
|
+
const userOp = {
|
|
15861
|
+
sender: solverAccount,
|
|
15862
|
+
nonce,
|
|
15863
|
+
initCode: "0x",
|
|
15864
|
+
callData,
|
|
15865
|
+
accountGasLimits,
|
|
15866
|
+
preVerificationGas,
|
|
15867
|
+
gasFees,
|
|
15868
|
+
paymasterAndData,
|
|
15869
|
+
signature: "0x"
|
|
15870
|
+
};
|
|
15871
|
+
const userOpHash = CryptoUtils.computeUserOpHash(userOp, entryPointAddress, chainId);
|
|
15872
|
+
const sessionKey = order.session;
|
|
15873
|
+
const messageHash = viem.keccak256(viem.concat([userOpHash, order.id, sessionKey]));
|
|
15874
|
+
const solverSignature = await solverSigner.signMessage(messageHash, Number(chainId));
|
|
15875
|
+
const signature = viem.concat([order.id, solverSignature]);
|
|
15876
|
+
return { ...userOp, signature };
|
|
15877
|
+
}
|
|
15878
|
+
/**
|
|
15879
|
+
* Decodes raw filler bids into first-class {@link Bid} objects.
|
|
15880
|
+
*
|
|
15881
|
+
* Each bid's `fillOrder` fill-options are decoded from its ERC-7821 calldata;
|
|
15882
|
+
* bids whose calldata cannot be decoded into a valid `fillOrder` call are
|
|
15883
|
+
* silently dropped with a warning. The returned `Bid` instances are ready to
|
|
15884
|
+
* be ranked, simulated, and executed by the consumer.
|
|
15885
|
+
*
|
|
15886
|
+
* @param order - The placed order the bids are competing to fill.
|
|
15887
|
+
* @param bids - Raw filler bids fetched from the coprocessor.
|
|
15888
|
+
* @param sessionPrivateKey - Optional session-key override; looked up from
|
|
15889
|
+
* storage by `order.session` if omitted.
|
|
15890
|
+
* @returns Array of executable `Bid` objects (one per successfully decoded bid).
|
|
15891
|
+
*/
|
|
15892
|
+
buildBids(order, bids, sessionPrivateKey) {
|
|
15893
|
+
const chainId = this.ctx.dest.config.stateMachineId;
|
|
15894
|
+
const priceOutputs = (outputs) => this.computeOutputsUsdValue(outputs, chainId);
|
|
15895
|
+
const result = [];
|
|
15896
|
+
for (const fillerBid of bids) {
|
|
15897
|
+
const fillOptions = this.decodeBidFillOptions(fillerBid);
|
|
15898
|
+
if (!fillOptions) {
|
|
15899
|
+
console.warn(`[BidManager] Failed to decode fillOptions from bid by solver=${fillerBid.userOp.sender}`);
|
|
15900
|
+
continue;
|
|
15901
|
+
}
|
|
15902
|
+
result.push(
|
|
15903
|
+
new BidImpl({
|
|
15904
|
+
ctx: this.ctx,
|
|
15905
|
+
crypto: this.crypto,
|
|
15906
|
+
order,
|
|
15907
|
+
fillerBid,
|
|
15908
|
+
fillOptions,
|
|
15909
|
+
priceOutputs,
|
|
15910
|
+
sessionPrivateKey
|
|
15911
|
+
})
|
|
15912
|
+
);
|
|
15913
|
+
}
|
|
15914
|
+
console.log(`[BidManager] Built ${result.length}/${bids.length} bid(s) successfully`);
|
|
15915
|
+
return result;
|
|
15916
|
+
}
|
|
15917
|
+
/**
|
|
15918
|
+
* Decodes raw filler bids, sorts them, simulates each until one passes, signs
|
|
15919
|
+
* the `SelectSolver` message, and submits — all with no per-bid input from the
|
|
15920
|
+
* caller.
|
|
15921
|
+
*
|
|
15922
|
+
* Equivalent to `selectAndExecuteBest(order, buildBids(order, bids, key))`.
|
|
15923
|
+
*
|
|
15924
|
+
* @param order - The placed order to fill.
|
|
15925
|
+
* @param bids - Raw filler bids fetched from the coprocessor.
|
|
15926
|
+
* @param sessionPrivateKey - Optional session-key override; looked up from
|
|
15927
|
+
* storage by `order.session` if omitted.
|
|
15928
|
+
* @returns A {@link SelectBidResult} for the executed bid.
|
|
15929
|
+
*/
|
|
15930
|
+
async selectBid(order, bids, sessionPrivateKey) {
|
|
15931
|
+
return this.selectAndExecuteBest(order, this.buildBids(order, bids, sessionPrivateKey));
|
|
15932
|
+
}
|
|
15933
|
+
/**
|
|
15934
|
+
* Autopilot bid selection: sorts the given bids by output value, simulates
|
|
15935
|
+
* each in order until one passes, then executes that bid. For consumers that
|
|
15936
|
+
* do not need custom selection logic.
|
|
15937
|
+
*
|
|
15938
|
+
* @param order - The placed order to fill.
|
|
15939
|
+
* @param bids - Candidate bids (from {@link buildBids}).
|
|
15940
|
+
* @returns A {@link SelectBidResult} for the executed bid.
|
|
15941
|
+
* @throws If no valid bids exist, all simulations fail, or the bundler rejects
|
|
15942
|
+
* the UserOperation.
|
|
15943
|
+
*/
|
|
15944
|
+
async selectAndExecuteBest(order, bids) {
|
|
15945
|
+
const commitment = order.id;
|
|
15946
|
+
console.log(`[BidManager] selectAndExecuteBest called for commitment=${commitment}, ${bids.length} bid(s)`);
|
|
15947
|
+
if (!this.ctx.bundlerUrl) {
|
|
15948
|
+
throw new Error("Bundler URL not configured");
|
|
15949
|
+
}
|
|
15950
|
+
if (!this.ctx.intentsCoprocessor) {
|
|
15951
|
+
throw new Error("IntentsCoprocessor required");
|
|
15952
|
+
}
|
|
15953
|
+
const sortedBids = await this.sortBids(order, bids);
|
|
15954
|
+
console.log(`[BidManager] ${sortedBids.length}/${bids.length} bid(s) passed validation and sorting`);
|
|
15955
|
+
if (sortedBids.length === 0) {
|
|
15956
|
+
throw new Error("No valid bids found");
|
|
15957
|
+
}
|
|
15958
|
+
console.log(`[BidManager] Simulating ${sortedBids.length} sorted bid(s) to find a valid one`);
|
|
15959
|
+
for (let idx = 0; idx < sortedBids.length; idx++) {
|
|
15960
|
+
const bid = sortedBids[idx];
|
|
15961
|
+
console.log(`[BidManager] Simulating bid ${idx + 1}/${sortedBids.length} from solver=${bid.solverAddress}`);
|
|
15962
|
+
try {
|
|
15963
|
+
await bid.simulate();
|
|
15964
|
+
} catch (err) {
|
|
15965
|
+
console.warn(
|
|
15966
|
+
`[BidManager] Bid ${idx + 1} from solver=${bid.solverAddress}: simulation FAILED: ${err instanceof Error ? err.message : String(err)}`
|
|
15967
|
+
);
|
|
15968
|
+
continue;
|
|
15969
|
+
}
|
|
15970
|
+
console.log(`[BidManager] Bid ${idx + 1} from solver=${bid.solverAddress}: simulation PASSED`);
|
|
15971
|
+
return bid.execute();
|
|
15972
|
+
}
|
|
15973
|
+
console.error(`[BidManager] All ${sortedBids.length} bid(s) failed simulation for commitment=${commitment}`);
|
|
15974
|
+
throw new Error("No bids passed simulation");
|
|
15975
|
+
}
|
|
15976
|
+
/**
|
|
15977
|
+
* Sorts a list of bids for the given order by output value.
|
|
15806
15978
|
*
|
|
15807
15979
|
* Delegates to one of three strategies based on the order's output token
|
|
15808
15980
|
* composition:
|
|
@@ -15811,47 +15983,26 @@ var BidManager = class {
|
|
|
15811
15983
|
* - Mixed outputs: sort by DEX-quoted USD value descending, with a raw-amount
|
|
15812
15984
|
* fallback if pricing fails.
|
|
15813
15985
|
*
|
|
15814
|
-
*
|
|
15986
|
+
* Bids that cannot satisfy the order's token set are dropped.
|
|
15987
|
+
*
|
|
15815
15988
|
* @param order - The placed order whose output spec drives sorting logic.
|
|
15816
|
-
* @
|
|
15989
|
+
* @param bids - Executable bids to sort (from {@link buildBids}).
|
|
15990
|
+
* @returns Sorted array of `Bid` objects ready for simulation.
|
|
15817
15991
|
*/
|
|
15818
|
-
async
|
|
15992
|
+
async sortBids(order, bids) {
|
|
15819
15993
|
const outputs = order.output.assets;
|
|
15820
|
-
const decodedBids = this.decodeBids(bids);
|
|
15821
15994
|
if (outputs.length <= 1) {
|
|
15822
15995
|
console.log(`[BidManager] Using single-output sorting (1 output asset)`);
|
|
15823
|
-
return this.sortSingleOutput(
|
|
15996
|
+
return this.sortSingleOutput(bids, outputs[0]);
|
|
15824
15997
|
}
|
|
15825
15998
|
const chainId = this.ctx.dest.config.stateMachineId;
|
|
15826
15999
|
const allStables = outputs.every((o) => this.isStableToken(bytes32ToBytes20(o.token), chainId));
|
|
15827
16000
|
if (allStables) {
|
|
15828
16001
|
console.log(`[BidManager] Using all-stables sorting (${outputs.length} stable output assets)`);
|
|
15829
|
-
return this.sortAllStables(
|
|
16002
|
+
return this.sortAllStables(bids, outputs, chainId);
|
|
15830
16003
|
}
|
|
15831
16004
|
console.log(`[BidManager] Using mixed-output sorting (${outputs.length} output assets, some non-stable)`);
|
|
15832
|
-
return this.sortMixedOutputs(
|
|
15833
|
-
}
|
|
15834
|
-
/**
|
|
15835
|
-
* Decodes the `fillOrder` fill-options from each bid's ERC-7821 calldata.
|
|
15836
|
-
*
|
|
15837
|
-
* Bids whose calldata cannot be decoded or do not contain a valid
|
|
15838
|
-
* `fillOrder` call are silently dropped with a warning.
|
|
15839
|
-
*
|
|
15840
|
-
* @param bids - Raw bids to decode.
|
|
15841
|
-
* @returns Array of successfully decoded `{ bid, options }` pairs.
|
|
15842
|
-
*/
|
|
15843
|
-
decodeBids(bids) {
|
|
15844
|
-
const result = [];
|
|
15845
|
-
for (const bid of bids) {
|
|
15846
|
-
const fillOptions = this.decodeBidFillOptions(bid);
|
|
15847
|
-
if (fillOptions) {
|
|
15848
|
-
result.push({ bid, options: fillOptions });
|
|
15849
|
-
} else {
|
|
15850
|
-
console.warn(`[BidManager] Failed to decode fillOptions from bid by solver=${bid.userOp.sender}`);
|
|
15851
|
-
}
|
|
15852
|
-
}
|
|
15853
|
-
console.log(`[BidManager] Decoded ${result.length}/${bids.length} bid(s) successfully`);
|
|
15854
|
-
return result;
|
|
16005
|
+
return this.sortMixedOutputs(bids, outputs, chainId);
|
|
15855
16006
|
}
|
|
15856
16007
|
/**
|
|
15857
16008
|
* Extracts the `FillOptions` struct from a single bid's ERC-7821
|
|
@@ -15884,149 +16035,103 @@ var BidManager = class {
|
|
|
15884
16035
|
}
|
|
15885
16036
|
return null;
|
|
15886
16037
|
}
|
|
15887
|
-
/**
|
|
15888
|
-
* Simulates a bid on-chain by batching the `select` and `fillOrder` calls
|
|
15889
|
-
* via `eth_call` from the solver's account, using the IntentGatewayV2
|
|
15890
|
-
* ERC-7821 batch-execute pattern.
|
|
15891
|
-
*
|
|
15892
|
-
* The native value forwarded to the simulation is computed from the fill options:
|
|
15893
|
-
* sum of any native-token (address(0)) output amounts plus the dispatch fee.
|
|
15894
|
-
*
|
|
15895
|
-
* @param bid - The filler bid to simulate.
|
|
15896
|
-
* @param fillOptions - Decoded fill options from the bid's calldata.
|
|
15897
|
-
* @param selectOptions - The signed solver-selection parameters.
|
|
15898
|
-
* @param intentGatewayV2Address - Address of the IntentGatewayV2 contract on the destination chain.
|
|
15899
|
-
* @throws If the `eth_call` simulation reverts or errors.
|
|
15900
|
-
*/
|
|
15901
|
-
async simulate(bid, fillOptions, selectOptions, intentGatewayV2Address) {
|
|
15902
|
-
const solverAddress = bid.userOp.sender;
|
|
15903
|
-
const nativeOutputs = fillOptions.outputs.reduce(
|
|
15904
|
-
(acc, o) => bytes32ToBytes20(o.token) === ADDRESS_ZERO2 ? acc + o.amount : acc,
|
|
15905
|
-
0n
|
|
15906
|
-
);
|
|
15907
|
-
const simulationValue = nativeOutputs + fillOptions.nativeDispatchFee;
|
|
15908
|
-
const selectCalldata = viem.encodeFunctionData({
|
|
15909
|
-
abi: ABI3,
|
|
15910
|
-
functionName: "select",
|
|
15911
|
-
args: [selectOptions]
|
|
15912
|
-
});
|
|
15913
|
-
const calls = [
|
|
15914
|
-
{ target: intentGatewayV2Address, value: 0n, data: selectCalldata },
|
|
15915
|
-
{ target: solverAddress, value: simulationValue, data: bid.userOp.callData }
|
|
15916
|
-
];
|
|
15917
|
-
const batchedCalldata = this.crypto.encodeERC7821Execute(calls);
|
|
15918
|
-
try {
|
|
15919
|
-
await this.ctx.dest.client.call({
|
|
15920
|
-
account: solverAddress,
|
|
15921
|
-
to: solverAddress,
|
|
15922
|
-
data: batchedCalldata,
|
|
15923
|
-
value: simulationValue
|
|
15924
|
-
});
|
|
15925
|
-
} catch (e) {
|
|
15926
|
-
throw new Error(`Simulation failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
15927
|
-
}
|
|
15928
|
-
}
|
|
15929
16038
|
/**
|
|
15930
16039
|
* Case A: single output token.
|
|
15931
16040
|
* Filter bids by token match only, sort descending by amount.
|
|
15932
16041
|
* Partial fill bids are allowed — the contract determines fill status.
|
|
15933
16042
|
*/
|
|
15934
|
-
sortSingleOutput(
|
|
16043
|
+
sortSingleOutput(bids, requiredAsset) {
|
|
15935
16044
|
const requiredAmount = new Decimal2__default.default(requiredAsset.amount.toString());
|
|
15936
16045
|
console.log(
|
|
15937
16046
|
`[BidManager] sortSingleOutput: required token=${requiredAsset.token}, amount=${requiredAmount.toString()}`
|
|
15938
16047
|
);
|
|
15939
16048
|
const validBids = [];
|
|
15940
|
-
for (const
|
|
15941
|
-
const bidOutput =
|
|
16049
|
+
for (const bid of bids) {
|
|
16050
|
+
const bidOutput = bid.outputs[0];
|
|
15942
16051
|
const bidAmount = new Decimal2__default.default(bidOutput.amount.toString());
|
|
15943
16052
|
if (bidOutput.token.toLowerCase() !== requiredAsset.token.toLowerCase()) {
|
|
15944
16053
|
console.warn(
|
|
15945
|
-
`[BidManager] Bid from solver=${bid.
|
|
16054
|
+
`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: token mismatch (bid=${bidOutput.token}, required=${requiredAsset.token})`
|
|
15946
16055
|
);
|
|
15947
16056
|
continue;
|
|
15948
16057
|
}
|
|
15949
16058
|
if (bidAmount.lt(requiredAmount)) {
|
|
15950
16059
|
console.log(
|
|
15951
|
-
`[BidManager] Bid from solver=${bid.
|
|
16060
|
+
`[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidAmount.toString()}, required=${requiredAmount.toString()}, covers=${bidAmount.div(requiredAmount).mul(100).toFixed(2)}%)`
|
|
15952
16061
|
);
|
|
15953
16062
|
} else {
|
|
15954
16063
|
console.log(
|
|
15955
|
-
`[BidManager] Bid from solver=${bid.
|
|
16064
|
+
`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: amount=${bidAmount.toString()} (surplus=${bidAmount.minus(requiredAmount).toString()})`
|
|
15956
16065
|
);
|
|
15957
16066
|
}
|
|
15958
|
-
validBids.push({ bid,
|
|
16067
|
+
validBids.push({ bid, amount: bidOutput.amount });
|
|
15959
16068
|
}
|
|
15960
16069
|
validBids.sort((a, b) => {
|
|
15961
16070
|
const aAmt = new Decimal2__default.default(a.amount.toString());
|
|
15962
16071
|
const bAmt = new Decimal2__default.default(b.amount.toString());
|
|
15963
16072
|
return bAmt.comparedTo(aAmt);
|
|
15964
16073
|
});
|
|
15965
|
-
return validBids.map(({
|
|
16074
|
+
return validBids.map(({ bid }) => bid);
|
|
15966
16075
|
}
|
|
15967
16076
|
/**
|
|
15968
16077
|
* Case B: all outputs are USDC/USDT.
|
|
15969
16078
|
* Sum normalised USD values (treating each stable as $1) and sort descending.
|
|
15970
16079
|
* Partial fill bids are allowed.
|
|
15971
16080
|
*/
|
|
15972
|
-
sortAllStables(
|
|
16081
|
+
sortAllStables(bids, orderOutputs, chainId) {
|
|
15973
16082
|
const requiredUsd = this.computeStablesUsdValue(orderOutputs, chainId);
|
|
15974
16083
|
console.log(`[BidManager] sortAllStables: required USD value=${requiredUsd.toString()}`);
|
|
15975
16084
|
const validBids = [];
|
|
15976
|
-
for (const
|
|
15977
|
-
const bidUsd = this.computeStablesUsdValue(
|
|
16085
|
+
for (const bid of bids) {
|
|
16086
|
+
const bidUsd = this.computeStablesUsdValue(bid.outputs, chainId);
|
|
15978
16087
|
if (bidUsd === null) {
|
|
15979
|
-
console.warn(`[BidManager] Bid from solver=${bid.
|
|
16088
|
+
console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: unable to compute USD value`);
|
|
15980
16089
|
continue;
|
|
15981
16090
|
}
|
|
15982
16091
|
if (bidUsd.lt(requiredUsd)) {
|
|
15983
16092
|
console.log(
|
|
15984
|
-
`[BidManager] Bid from solver=${bid.
|
|
16093
|
+
`[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidUsd.toString()}, required=${requiredUsd.toString()}, covers=${bidUsd.div(requiredUsd).mul(100).toFixed(2)}%)`
|
|
15985
16094
|
);
|
|
15986
16095
|
} else {
|
|
15987
|
-
console.log(
|
|
15988
|
-
`[BidManager] Bid from solver=${bid.userOp.sender} ACCEPTED: USD value=${bidUsd.toString()}`
|
|
15989
|
-
);
|
|
16096
|
+
console.log(`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: USD value=${bidUsd.toString()}`);
|
|
15990
16097
|
}
|
|
15991
|
-
validBids.push({ bid,
|
|
16098
|
+
validBids.push({ bid, usdValue: bidUsd });
|
|
15992
16099
|
}
|
|
15993
16100
|
validBids.sort((a, b) => b.usdValue.comparedTo(a.usdValue));
|
|
15994
|
-
return validBids.map(({
|
|
16101
|
+
return validBids.map(({ bid }) => bid);
|
|
15995
16102
|
}
|
|
15996
16103
|
/**
|
|
15997
16104
|
* Case C: mixed output tokens (at least one non-stable).
|
|
15998
16105
|
* Price every token via on-chain DEX quotes, fall back to raw amounts
|
|
15999
16106
|
* if pricing is unavailable. Partial fill bids are allowed.
|
|
16000
16107
|
*/
|
|
16001
|
-
async sortMixedOutputs(
|
|
16108
|
+
async sortMixedOutputs(bids, orderOutputs, chainId) {
|
|
16002
16109
|
const requiredUsd = await this.computeOutputsUsdValue(orderOutputs, chainId);
|
|
16003
16110
|
if (requiredUsd === null) {
|
|
16004
16111
|
console.warn("[BidManager] sortMixedOutputs: output tokens unpriceable, falling back to raw-amount sort");
|
|
16005
|
-
return this.sortByRawAmountFallback(
|
|
16112
|
+
return this.sortByRawAmountFallback(bids, orderOutputs);
|
|
16006
16113
|
}
|
|
16007
16114
|
console.log(`[BidManager] sortMixedOutputs: required USD value=${requiredUsd.toString()}`);
|
|
16008
16115
|
const validBids = [];
|
|
16009
|
-
for (const
|
|
16010
|
-
const bidUsd = await this.computeOutputsUsdValue(
|
|
16116
|
+
for (const bid of bids) {
|
|
16117
|
+
const bidUsd = await this.computeOutputsUsdValue(bid.outputs, chainId);
|
|
16011
16118
|
if (bidUsd === null) {
|
|
16012
|
-
console.warn(
|
|
16013
|
-
`[BidManager] Bid from solver=${bid.userOp.sender} REJECTED: unable to price mixed outputs`
|
|
16014
|
-
);
|
|
16119
|
+
console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED: unable to price mixed outputs`);
|
|
16015
16120
|
continue;
|
|
16016
16121
|
}
|
|
16017
16122
|
if (bidUsd.lt(requiredUsd)) {
|
|
16018
16123
|
console.log(
|
|
16019
|
-
`[BidManager] Bid from solver=${bid.
|
|
16124
|
+
`[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (bid=${bidUsd.toString()}, required=${requiredUsd.toString()}, covers=${bidUsd.div(requiredUsd).mul(100).toFixed(2)}%)`
|
|
16020
16125
|
);
|
|
16021
16126
|
} else {
|
|
16022
16127
|
console.log(
|
|
16023
|
-
`[BidManager] Bid from solver=${bid.
|
|
16128
|
+
`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED: mixed USD value=${bidUsd.toString()}`
|
|
16024
16129
|
);
|
|
16025
16130
|
}
|
|
16026
|
-
validBids.push({ bid,
|
|
16131
|
+
validBids.push({ bid, usdValue: bidUsd });
|
|
16027
16132
|
}
|
|
16028
16133
|
validBids.sort((a, b) => b.usdValue.comparedTo(a.usdValue));
|
|
16029
|
-
return validBids.map(({
|
|
16134
|
+
return validBids.map(({ bid }) => bid);
|
|
16030
16135
|
}
|
|
16031
16136
|
/**
|
|
16032
16137
|
* Fallback when DEX pricing is unavailable.
|
|
@@ -16034,17 +16139,17 @@ var BidManager = class {
|
|
|
16034
16139
|
* Bids offering less than required for a token are allowed (partial fill).
|
|
16035
16140
|
* Sorted by total offered amount descending.
|
|
16036
16141
|
*/
|
|
16037
|
-
sortByRawAmountFallback(
|
|
16142
|
+
sortByRawAmountFallback(bids, orderOutputs) {
|
|
16038
16143
|
console.log(
|
|
16039
|
-
`[BidManager] sortByRawAmountFallback: checking ${
|
|
16144
|
+
`[BidManager] sortByRawAmountFallback: checking ${bids.length} bid(s) against ${orderOutputs.length} required output(s)`
|
|
16040
16145
|
);
|
|
16041
16146
|
const validBids = [];
|
|
16042
|
-
for (const
|
|
16147
|
+
for (const bid of bids) {
|
|
16043
16148
|
let valid = true;
|
|
16044
16149
|
let totalOffered = new Decimal2__default.default(0);
|
|
16045
16150
|
let rejectReason = "";
|
|
16046
16151
|
for (const required of orderOutputs) {
|
|
16047
|
-
const matching =
|
|
16152
|
+
const matching = bid.outputs.find((o) => o.token.toLowerCase() === required.token.toLowerCase());
|
|
16048
16153
|
if (!matching) {
|
|
16049
16154
|
valid = false;
|
|
16050
16155
|
rejectReason = `missing output token=${required.token}`;
|
|
@@ -16053,7 +16158,7 @@ var BidManager = class {
|
|
|
16053
16158
|
totalOffered = totalOffered.plus(new Decimal2__default.default(matching.amount.toString()));
|
|
16054
16159
|
}
|
|
16055
16160
|
if (!valid) {
|
|
16056
|
-
console.warn(`[BidManager] Bid from solver=${bid.
|
|
16161
|
+
console.warn(`[BidManager] Bid from solver=${bid.solverAddress} REJECTED (fallback): ${rejectReason}`);
|
|
16057
16162
|
continue;
|
|
16058
16163
|
}
|
|
16059
16164
|
const totalRequired = orderOutputs.reduce(
|
|
@@ -16062,17 +16167,17 @@ var BidManager = class {
|
|
|
16062
16167
|
);
|
|
16063
16168
|
if (totalOffered.lt(totalRequired)) {
|
|
16064
16169
|
console.log(
|
|
16065
|
-
`[BidManager] Bid from solver=${bid.
|
|
16170
|
+
`[BidManager] Bid from solver=${bid.solverAddress}: partial fill candidate (fallback) (offered=${totalOffered.toString()}, required=${totalRequired.toString()}, covers=${totalOffered.div(totalRequired).mul(100).toFixed(2)}%)`
|
|
16066
16171
|
);
|
|
16067
16172
|
} else {
|
|
16068
16173
|
console.log(
|
|
16069
|
-
`[BidManager] Bid from solver=${bid.
|
|
16174
|
+
`[BidManager] Bid from solver=${bid.solverAddress} ACCEPTED (fallback): totalOffered=${totalOffered.toString()}`
|
|
16070
16175
|
);
|
|
16071
16176
|
}
|
|
16072
|
-
validBids.push({ bid,
|
|
16177
|
+
validBids.push({ bid, totalOffered });
|
|
16073
16178
|
}
|
|
16074
16179
|
validBids.sort((a, b) => b.totalOffered.comparedTo(a.totalOffered));
|
|
16075
|
-
return validBids.map(({
|
|
16180
|
+
return validBids.map(({ bid }) => bid);
|
|
16076
16181
|
}
|
|
16077
16182
|
// ── Token classification helpers ──────────────────────────────────
|
|
16078
16183
|
/**
|
|
@@ -16681,6 +16786,262 @@ var OrderStatusChecker = class {
|
|
|
16681
16786
|
}
|
|
16682
16787
|
};
|
|
16683
16788
|
|
|
16789
|
+
// src/protocols/intents/quote/types.ts
|
|
16790
|
+
var UnsupportedIntentQuoteStrategyError = class extends Error {
|
|
16791
|
+
constructor(strategy) {
|
|
16792
|
+
super(`Unsupported intent quote strategy: ${strategy}`);
|
|
16793
|
+
this.name = "UnsupportedIntentQuoteStrategyError";
|
|
16794
|
+
}
|
|
16795
|
+
};
|
|
16796
|
+
var UnsupportedIntentQuotePairError = class extends Error {
|
|
16797
|
+
constructor(params) {
|
|
16798
|
+
super(
|
|
16799
|
+
`No Uniswap v4 pool config found for ${params.tokenIn.symbol ?? params.tokenIn.address} -> ${params.tokenOut.symbol ?? params.tokenOut.address} on ${params.source} -> ${params.destination}`
|
|
16800
|
+
);
|
|
16801
|
+
this.name = "UnsupportedIntentQuotePairError";
|
|
16802
|
+
}
|
|
16803
|
+
};
|
|
16804
|
+
|
|
16805
|
+
// src/protocols/intents/quote/uniswapV4.ts
|
|
16806
|
+
var UNISWAP_INTENT_QUOTE_CHAIN = "EVM-8453" /* BASE_MAINNET */;
|
|
16807
|
+
var BPS_DENOMINATOR = 10000n;
|
|
16808
|
+
var UniswapV4IntentQuoteStrategy = class {
|
|
16809
|
+
constructor(chainConfigService) {
|
|
16810
|
+
this.chainConfigService = chainConfigService;
|
|
16811
|
+
}
|
|
16812
|
+
chainConfigService;
|
|
16813
|
+
baseQuoteClient;
|
|
16814
|
+
async quote(params, source, destination) {
|
|
16815
|
+
this.validateQuoteParams(params);
|
|
16816
|
+
const protocolFeeBps = await this.readProtocolFeeBps(source.client, source.stateMachineId);
|
|
16817
|
+
const quoteClient = this.resolveQuoteClient(source, destination);
|
|
16818
|
+
const poolConfig = this.resolvePoolConfig(params, source.stateMachineId, UNISWAP_INTENT_QUOTE_CHAIN);
|
|
16819
|
+
return params.amountIn !== void 0 ? this.quoteExactInput({ params, client: quoteClient, protocolFeeBps, poolConfig }) : this.quoteExactOutput({ params, client: quoteClient, protocolFeeBps, poolConfig });
|
|
16820
|
+
}
|
|
16821
|
+
resolveQuoteClient(source, destination) {
|
|
16822
|
+
if (source.stateMachineId === UNISWAP_INTENT_QUOTE_CHAIN) return source.client;
|
|
16823
|
+
if (destination.stateMachineId === UNISWAP_INTENT_QUOTE_CHAIN) return destination.client;
|
|
16824
|
+
if (this.baseQuoteClient) return this.baseQuoteClient;
|
|
16825
|
+
const rpcUrl = this.chainConfigService.getRpcUrl(UNISWAP_INTENT_QUOTE_CHAIN);
|
|
16826
|
+
if (!rpcUrl) throw new Error(`RPC URL is not configured for ${UNISWAP_INTENT_QUOTE_CHAIN}`);
|
|
16827
|
+
const baseQuoteClient = viem.createPublicClient({
|
|
16828
|
+
chain: chains$1.base,
|
|
16829
|
+
transport: viem.http(rpcUrl)
|
|
16830
|
+
});
|
|
16831
|
+
this.baseQuoteClient = baseQuoteClient;
|
|
16832
|
+
return baseQuoteClient;
|
|
16833
|
+
}
|
|
16834
|
+
validateQuoteParams(params) {
|
|
16835
|
+
const hasAmountIn = params.amountIn !== void 0;
|
|
16836
|
+
const hasAmountOut = params.amountOut !== void 0;
|
|
16837
|
+
if (hasAmountIn === hasAmountOut) throw new Error("Provide exactly one of amountIn or amountOut");
|
|
16838
|
+
if (hasAmountIn && params.amountIn <= 0n) throw new Error("amountIn must be greater than zero");
|
|
16839
|
+
if (hasAmountOut && params.amountOut <= 0n) throw new Error("amountOut must be greater than zero");
|
|
16840
|
+
if (params.tokenIn.address.toLowerCase() === params.tokenOut.address.toLowerCase()) {
|
|
16841
|
+
throw new Error("tokenIn and tokenOut cannot be the same");
|
|
16842
|
+
}
|
|
16843
|
+
}
|
|
16844
|
+
async readProtocolFeeBps(client, chain) {
|
|
16845
|
+
const gatewayAddress = this.chainConfigService.getIntentGatewayAddress(chain);
|
|
16846
|
+
if (!gatewayAddress || gatewayAddress === "0x" || gatewayAddress === viem.zeroAddress) {
|
|
16847
|
+
throw new Error(`IntentGatewayV2 is not configured for chain ${chain}`);
|
|
16848
|
+
}
|
|
16849
|
+
const gatewayParams = await client.readContract({
|
|
16850
|
+
address: gatewayAddress,
|
|
16851
|
+
abi: IntentGatewayV2_default.ABI,
|
|
16852
|
+
functionName: "params"
|
|
16853
|
+
});
|
|
16854
|
+
if (isGatewayParamsTuple(gatewayParams)) return BigInt(gatewayParams[4]);
|
|
16855
|
+
return BigInt(gatewayParams.protocolFeeBps ?? 0);
|
|
16856
|
+
}
|
|
16857
|
+
resolvePoolConfig(params, source, destination) {
|
|
16858
|
+
const override = params.uniswapV4?.poolKey;
|
|
16859
|
+
if (override) {
|
|
16860
|
+
const poolKey = normalizePoolKey(override);
|
|
16861
|
+
const tokenInForQuote = viem.getAddress(override.currencyIn ?? params.tokenIn.address);
|
|
16862
|
+
if (tokenInForQuote !== poolKey.currency0 && tokenInForQuote !== poolKey.currency1) {
|
|
16863
|
+
throw new Error(
|
|
16864
|
+
`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.`
|
|
16865
|
+
);
|
|
16866
|
+
}
|
|
16867
|
+
return {
|
|
16868
|
+
poolKey,
|
|
16869
|
+
quoterAddress: this.resolveQuoterAddress(destination, override.quoterAddress),
|
|
16870
|
+
tokenInForQuote
|
|
16871
|
+
};
|
|
16872
|
+
}
|
|
16873
|
+
const poolConfig = this.resolveConfiguredPool(params, destination);
|
|
16874
|
+
if (poolConfig) return poolConfig;
|
|
16875
|
+
throw new UnsupportedIntentQuotePairError({
|
|
16876
|
+
source,
|
|
16877
|
+
destination,
|
|
16878
|
+
tokenIn: params.tokenIn,
|
|
16879
|
+
tokenOut: params.tokenOut
|
|
16880
|
+
});
|
|
16881
|
+
}
|
|
16882
|
+
resolveConfiguredPool(params, chain) {
|
|
16883
|
+
for (const pool of this.chainConfigService.getUniswapV4PoolConfigs(chain)) {
|
|
16884
|
+
const resolvedPool = this.resolveConfiguredPoolTokens(chain, pool);
|
|
16885
|
+
if (!resolvedPool) continue;
|
|
16886
|
+
const tokenInForQuote = matchPoolToken(params.tokenIn, resolvedPool);
|
|
16887
|
+
const tokenOutForQuote = matchPoolToken(params.tokenOut, resolvedPool);
|
|
16888
|
+
if (!tokenInForQuote || !tokenOutForQuote || tokenInForQuote.symbol === tokenOutForQuote.symbol) continue;
|
|
16889
|
+
const { currency0, currency1 } = sortCurrencies(resolvedPool[0].address, resolvedPool[1].address);
|
|
16890
|
+
return {
|
|
16891
|
+
poolKey: {
|
|
16892
|
+
currency0,
|
|
16893
|
+
currency1,
|
|
16894
|
+
fee: pool.fee,
|
|
16895
|
+
tickSpacing: pool.tickSpacing,
|
|
16896
|
+
hooks: viem.getAddress(pool.hooks ?? viem.zeroAddress)
|
|
16897
|
+
},
|
|
16898
|
+
quoterAddress: this.resolveQuoterAddress(chain),
|
|
16899
|
+
tokenInForQuote: tokenInForQuote.address
|
|
16900
|
+
};
|
|
16901
|
+
}
|
|
16902
|
+
return null;
|
|
16903
|
+
}
|
|
16904
|
+
resolveQuoterAddress(chain, override) {
|
|
16905
|
+
const address = override ?? this.chainConfigService.getUniswapV4QuoterAddress(chain);
|
|
16906
|
+
if (!address || address === "0x" || address === viem.zeroAddress) {
|
|
16907
|
+
throw new Error(`Uniswap V4 quoter is not configured for chain ${chain}`);
|
|
16908
|
+
}
|
|
16909
|
+
return viem.getAddress(address);
|
|
16910
|
+
}
|
|
16911
|
+
resolveConfiguredPoolTokens(chain, pool) {
|
|
16912
|
+
const first = this.resolveConfiguredPoolToken(chain, pool.tokens[0]);
|
|
16913
|
+
const second = this.resolveConfiguredPoolToken(chain, pool.tokens[1]);
|
|
16914
|
+
return first && second ? [first, second] : null;
|
|
16915
|
+
}
|
|
16916
|
+
resolveConfiguredPoolToken(chain, symbol) {
|
|
16917
|
+
const address = this.chainConfigService.getAssetAddress(chain, symbol);
|
|
16918
|
+
if (!address || address === "0x") return null;
|
|
16919
|
+
return { symbol, address: viem.getAddress(address) };
|
|
16920
|
+
}
|
|
16921
|
+
async quoteExactInput(args) {
|
|
16922
|
+
const amountIn = args.params.amountIn;
|
|
16923
|
+
const swapAmountIn = deductProtocolFee(amountIn, args.protocolFeeBps);
|
|
16924
|
+
const amountOut = await this.readV4QuoteExactInput(args.client, args.poolConfig, swapAmountIn);
|
|
16925
|
+
return {
|
|
16926
|
+
strategy: "uniswap_v4",
|
|
16927
|
+
tradeType: "EXACT_INPUT",
|
|
16928
|
+
amountIn,
|
|
16929
|
+
amountOut,
|
|
16930
|
+
quoteMetadata: {
|
|
16931
|
+
quoteChain: UNISWAP_INTENT_QUOTE_CHAIN,
|
|
16932
|
+
poolKey: args.poolConfig.poolKey,
|
|
16933
|
+
quoterAddress: args.poolConfig.quoterAddress,
|
|
16934
|
+
protocolFeeBps: args.protocolFeeBps
|
|
16935
|
+
}
|
|
16936
|
+
};
|
|
16937
|
+
}
|
|
16938
|
+
async quoteExactOutput(args) {
|
|
16939
|
+
const amountOut = args.params.amountOut;
|
|
16940
|
+
const swapAmountIn = await this.readV4QuoteExactOutput(args.client, args.poolConfig, amountOut);
|
|
16941
|
+
const amountIn = grossUpForProtocolFee(swapAmountIn, args.protocolFeeBps);
|
|
16942
|
+
return {
|
|
16943
|
+
strategy: "uniswap_v4",
|
|
16944
|
+
tradeType: "EXACT_OUTPUT",
|
|
16945
|
+
amountIn,
|
|
16946
|
+
amountOut,
|
|
16947
|
+
quoteMetadata: {
|
|
16948
|
+
quoteChain: UNISWAP_INTENT_QUOTE_CHAIN,
|
|
16949
|
+
poolKey: args.poolConfig.poolKey,
|
|
16950
|
+
quoterAddress: args.poolConfig.quoterAddress,
|
|
16951
|
+
protocolFeeBps: args.protocolFeeBps
|
|
16952
|
+
}
|
|
16953
|
+
};
|
|
16954
|
+
}
|
|
16955
|
+
async readV4QuoteExactInput(client, poolConfig, amountIn) {
|
|
16956
|
+
const data = viem.encodeFunctionData({
|
|
16957
|
+
abi: UNISWAP_V4_QUOTER_ABI,
|
|
16958
|
+
functionName: "quoteExactInputSingle",
|
|
16959
|
+
args: [
|
|
16960
|
+
{
|
|
16961
|
+
poolKey: poolConfig.poolKey,
|
|
16962
|
+
zeroForOne: getZeroForOne(poolConfig.tokenInForQuote, poolConfig.poolKey),
|
|
16963
|
+
exactAmount: amountIn,
|
|
16964
|
+
hookData: "0x"
|
|
16965
|
+
}
|
|
16966
|
+
]
|
|
16967
|
+
});
|
|
16968
|
+
const response = await client.call({ to: poolConfig.quoterAddress, data });
|
|
16969
|
+
if (!response.data || response.data === "0x") {
|
|
16970
|
+
throw new Error(`Uniswap V4 quoter at ${poolConfig.quoterAddress} returned no data`);
|
|
16971
|
+
}
|
|
16972
|
+
const [amountOut] = viem.decodeFunctionResult({
|
|
16973
|
+
abi: UNISWAP_V4_QUOTER_ABI,
|
|
16974
|
+
functionName: "quoteExactInputSingle",
|
|
16975
|
+
data: response.data
|
|
16976
|
+
});
|
|
16977
|
+
return amountOut;
|
|
16978
|
+
}
|
|
16979
|
+
async readV4QuoteExactOutput(client, poolConfig, amountOut) {
|
|
16980
|
+
const data = viem.encodeFunctionData({
|
|
16981
|
+
abi: UNISWAP_V4_QUOTER_ABI,
|
|
16982
|
+
functionName: "quoteExactOutputSingle",
|
|
16983
|
+
args: [
|
|
16984
|
+
{
|
|
16985
|
+
poolKey: poolConfig.poolKey,
|
|
16986
|
+
zeroForOne: getZeroForOne(poolConfig.tokenInForQuote, poolConfig.poolKey),
|
|
16987
|
+
exactAmount: amountOut,
|
|
16988
|
+
hookData: "0x"
|
|
16989
|
+
}
|
|
16990
|
+
]
|
|
16991
|
+
});
|
|
16992
|
+
const response = await client.call({ to: poolConfig.quoterAddress, data });
|
|
16993
|
+
if (!response.data || response.data === "0x") {
|
|
16994
|
+
throw new Error(`Uniswap V4 quoter at ${poolConfig.quoterAddress} returned no data`);
|
|
16995
|
+
}
|
|
16996
|
+
const [amountIn] = viem.decodeFunctionResult({
|
|
16997
|
+
abi: UNISWAP_V4_QUOTER_ABI,
|
|
16998
|
+
functionName: "quoteExactOutputSingle",
|
|
16999
|
+
data: response.data
|
|
17000
|
+
});
|
|
17001
|
+
return amountIn;
|
|
17002
|
+
}
|
|
17003
|
+
};
|
|
17004
|
+
function normalizePoolKey(poolKey) {
|
|
17005
|
+
return {
|
|
17006
|
+
currency0: viem.getAddress(poolKey.currency0),
|
|
17007
|
+
currency1: viem.getAddress(poolKey.currency1),
|
|
17008
|
+
fee: poolKey.fee,
|
|
17009
|
+
tickSpacing: poolKey.tickSpacing,
|
|
17010
|
+
hooks: viem.getAddress(poolKey.hooks)
|
|
17011
|
+
};
|
|
17012
|
+
}
|
|
17013
|
+
function sortCurrencies(tokenIn, tokenOut) {
|
|
17014
|
+
const input = viem.getAddress(tokenIn);
|
|
17015
|
+
const output = viem.getAddress(tokenOut);
|
|
17016
|
+
return BigInt(input) < BigInt(output) ? { currency0: input, currency1: output } : { currency0: output, currency1: input };
|
|
17017
|
+
}
|
|
17018
|
+
function matchPoolToken(token, poolTokens) {
|
|
17019
|
+
const tokenAddress = token.address.toLowerCase();
|
|
17020
|
+
const addressMatch = poolTokens.find((poolToken) => poolToken.address.toLowerCase() === tokenAddress);
|
|
17021
|
+
if (addressMatch) return addressMatch;
|
|
17022
|
+
const tokenSymbol = token.symbol?.toUpperCase();
|
|
17023
|
+
if (!tokenSymbol) return null;
|
|
17024
|
+
return poolTokens.find((poolToken) => poolToken.symbol.toUpperCase() === tokenSymbol) ?? null;
|
|
17025
|
+
}
|
|
17026
|
+
function getZeroForOne(tokenIn, poolKey) {
|
|
17027
|
+
return viem.getAddress(tokenIn).toLowerCase() === viem.getAddress(poolKey.currency0).toLowerCase();
|
|
17028
|
+
}
|
|
17029
|
+
function isGatewayParamsTuple(value) {
|
|
17030
|
+
return Array.isArray(value);
|
|
17031
|
+
}
|
|
17032
|
+
function deductProtocolFee(amount, protocolFeeBps) {
|
|
17033
|
+
if (protocolFeeBps <= 0n) return amount;
|
|
17034
|
+
const fee = amount * protocolFeeBps / BPS_DENOMINATOR;
|
|
17035
|
+
return amount - fee;
|
|
17036
|
+
}
|
|
17037
|
+
function grossUpForProtocolFee(netAmount, protocolFeeBps) {
|
|
17038
|
+
if (protocolFeeBps <= 0n) return netAmount;
|
|
17039
|
+
return divCeil(netAmount * BPS_DENOMINATOR, BPS_DENOMINATOR - protocolFeeBps);
|
|
17040
|
+
}
|
|
17041
|
+
function divCeil(numerator, denominator) {
|
|
17042
|
+
return (numerator + denominator - 1n) / denominator;
|
|
17043
|
+
}
|
|
17044
|
+
|
|
16684
17045
|
// src/protocols/intents/IntentGateway.ts
|
|
16685
17046
|
var IntentGateway = class _IntentGateway {
|
|
16686
17047
|
/** EVM chain on which orders are placed and escrowed. */
|
|
@@ -16707,6 +17068,8 @@ var IntentGateway = class _IntentGateway {
|
|
|
16707
17068
|
bidManager;
|
|
16708
17069
|
/** Estimates gas costs for filling an order and converts them to fee-token amounts. */
|
|
16709
17070
|
gasEstimator;
|
|
17071
|
+
/** Quote strategies for pricing orders before placement, keyed by strategy name. */
|
|
17072
|
+
quoteStrategies;
|
|
16710
17073
|
/**
|
|
16711
17074
|
* Private constructor — use {@link IntentGateway.create} instead.
|
|
16712
17075
|
*
|
|
@@ -16744,12 +17107,15 @@ var IntentGateway = class _IntentGateway {
|
|
|
16744
17107
|
const bidManager = new BidManager(this.ctx, crypto);
|
|
16745
17108
|
const gasEstimator = new GasEstimator(this.ctx, crypto);
|
|
16746
17109
|
this.orderPlacer = new OrderPlacer(this.ctx);
|
|
16747
|
-
this.orderExecutor = new OrderExecutor(this.ctx, bidManager
|
|
17110
|
+
this.orderExecutor = new OrderExecutor(this.ctx, bidManager);
|
|
16748
17111
|
this.orderCanceller = new OrderCanceller(this.ctx);
|
|
16749
17112
|
this.orderStatusChecker = new OrderStatusChecker(this.ctx);
|
|
16750
17113
|
this.bidManager = bidManager;
|
|
16751
17114
|
this.gasEstimator = gasEstimator;
|
|
16752
17115
|
this._crypto = crypto;
|
|
17116
|
+
this.quoteStrategies = {
|
|
17117
|
+
uniswap_v4: new UniswapV4IntentQuoteStrategy(dest.configService)
|
|
17118
|
+
};
|
|
16753
17119
|
}
|
|
16754
17120
|
/**
|
|
16755
17121
|
* Creates an initialized IntentGateway instance.
|
|
@@ -16794,6 +17160,33 @@ var IntentGateway = class _IntentGateway {
|
|
|
16794
17160
|
}
|
|
16795
17161
|
}
|
|
16796
17162
|
}
|
|
17163
|
+
/**
|
|
17164
|
+
* Quotes an intent between this gateway's source and destination chains.
|
|
17165
|
+
*
|
|
17166
|
+
* `strategy` defaults to `uniswap_v4`, currently the only supported
|
|
17167
|
+
* strategy. Provide exactly one of `amountIn` or `amountOut`.
|
|
17168
|
+
*
|
|
17169
|
+
* The Uniswap quote strategy always prices against the configured Base
|
|
17170
|
+
* pool, regardless of this gateway's destination chain. Returned
|
|
17171
|
+
* `amountIn`/`amountOut` already account for the gateway's protocol fee
|
|
17172
|
+
* (`quoteMetadata.protocolFeeBps`), which the gateway deducts from order
|
|
17173
|
+
* inputs; apply only your own slippage tolerance before placing the order.
|
|
17174
|
+
*
|
|
17175
|
+
* @param params - Token pair, amount, and optional strategy/pool overrides.
|
|
17176
|
+
* @returns The quoted amounts plus strategy-specific metadata.
|
|
17177
|
+
* @throws {UnsupportedIntentQuoteStrategyError} For unknown strategies.
|
|
17178
|
+
* @throws {UnsupportedIntentQuotePairError} When no pool is configured for the pair.
|
|
17179
|
+
*/
|
|
17180
|
+
async quoteIntent(params) {
|
|
17181
|
+
const strategy = params.strategy ?? "uniswap_v4";
|
|
17182
|
+
const handler = this.quoteStrategies[strategy];
|
|
17183
|
+
if (!handler) throw new UnsupportedIntentQuoteStrategyError(strategy);
|
|
17184
|
+
return handler.quote(
|
|
17185
|
+
{ ...params, strategy },
|
|
17186
|
+
{ stateMachineId: this.source.config.stateMachineId, client: this.source.client },
|
|
17187
|
+
{ stateMachineId: this.dest.config.stateMachineId, client: this.dest.client }
|
|
17188
|
+
);
|
|
17189
|
+
}
|
|
16797
17190
|
/**
|
|
16798
17191
|
* Bidirectional async generator that orchestrates the full order lifecycle:
|
|
16799
17192
|
* placement, fee estimation, bid collection, and execution.
|
|
@@ -16848,17 +17241,38 @@ var IntentGateway = class _IntentGateway {
|
|
|
16848
17241
|
}
|
|
16849
17242
|
const { order: finalizedOrder, receipt: placementReceipt } = placeOrderSecond.value;
|
|
16850
17243
|
yield { status: "ORDER_PLACED", order: finalizedOrder, receipt: placementReceipt };
|
|
16851
|
-
|
|
16852
|
-
|
|
16853
|
-
|
|
16854
|
-
|
|
16855
|
-
|
|
16856
|
-
|
|
16857
|
-
|
|
16858
|
-
|
|
16859
|
-
|
|
17244
|
+
yield* this.driveExecution(
|
|
17245
|
+
this.orderExecutor.executeOrder({
|
|
17246
|
+
order: finalizedOrder,
|
|
17247
|
+
sessionPrivateKey,
|
|
17248
|
+
auctionTimeMs: options.auctionTimeMs,
|
|
17249
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
17250
|
+
solver: options.solver
|
|
17251
|
+
})
|
|
17252
|
+
);
|
|
16860
17253
|
return;
|
|
16861
17254
|
}
|
|
17255
|
+
/**
|
|
17256
|
+
* Forwards updates from the executor's bidirectional generator to the caller,
|
|
17257
|
+
* threading the {@link SelectBidResult} the caller feeds back after a
|
|
17258
|
+
* `BIDS_RECEIVED` yield into the executor's `.next()`. Other yields expect no
|
|
17259
|
+
* feedback. This is what lets the consumer own `bid.execute()` while the
|
|
17260
|
+
* executor keeps tracking fills and continuing the auction.
|
|
17261
|
+
*/
|
|
17262
|
+
async *driveExecution(execGen) {
|
|
17263
|
+
try {
|
|
17264
|
+
let input;
|
|
17265
|
+
while (true) {
|
|
17266
|
+
const { value, done } = await execGen.next(input);
|
|
17267
|
+
input = void 0;
|
|
17268
|
+
if (done) break;
|
|
17269
|
+
const fed = yield value;
|
|
17270
|
+
if (value.status === "BIDS_RECEIVED") input = fed;
|
|
17271
|
+
}
|
|
17272
|
+
} finally {
|
|
17273
|
+
await execGen.return();
|
|
17274
|
+
}
|
|
17275
|
+
}
|
|
16862
17276
|
/**
|
|
16863
17277
|
* Validates that an order has the minimum fields required for post-placement
|
|
16864
17278
|
* resume (i.e. it was previously placed and has an on-chain identity).
|
|
@@ -16895,14 +17309,99 @@ var IntentGateway = class _IntentGateway {
|
|
|
16895
17309
|
*/
|
|
16896
17310
|
async *resume(order, options) {
|
|
16897
17311
|
this.assertOrderCanResume(order);
|
|
16898
|
-
|
|
16899
|
-
|
|
16900
|
-
|
|
16901
|
-
|
|
16902
|
-
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
17312
|
+
yield* this.driveExecution(
|
|
17313
|
+
this.orderExecutor.executeOrder({
|
|
17314
|
+
order,
|
|
17315
|
+
sessionPrivateKey: options.sessionPrivateKey,
|
|
17316
|
+
auctionTimeMs: options.auctionTimeMs,
|
|
17317
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
17318
|
+
solver: options.solver
|
|
17319
|
+
})
|
|
17320
|
+
);
|
|
17321
|
+
}
|
|
17322
|
+
/**
|
|
17323
|
+
* Batteries-included variant of {@link execute}: places the order and then
|
|
17324
|
+
* auto-selects the best bid each round via {@link selectAndExecuteBest}, with
|
|
17325
|
+
* no bid-selection input from the caller.
|
|
17326
|
+
*
|
|
17327
|
+
* The caller still signs the placement transaction: this generator yields
|
|
17328
|
+
* `AWAITING_PLACE_ORDER` and the caller must hand the signed tx back via
|
|
17329
|
+
* `gen.next(signedTx)` exactly as with {@link execute}. Every other stage
|
|
17330
|
+
* (`BIDS_RECEIVED`, `BID_SELECTED`, `FILLED`, …) is handled automatically and
|
|
17331
|
+
* surfaced for observation, so the rest of the loop needs no feedback.
|
|
17332
|
+
*
|
|
17333
|
+
* @param order - The order to place and execute.
|
|
17334
|
+
* @param graffiti - Optional orderflow-attribution tag.
|
|
17335
|
+
* @param options - Same tuning parameters as {@link execute}.
|
|
17336
|
+
* @yields {@link IntentOrderStatusUpdate} at each lifecycle stage.
|
|
17337
|
+
*/
|
|
17338
|
+
async *executeBest(order, graffiti = DEFAULT_GRAFFITI, options) {
|
|
17339
|
+
const gen = this.execute(order, graffiti, options);
|
|
17340
|
+
try {
|
|
17341
|
+
let input;
|
|
17342
|
+
while (true) {
|
|
17343
|
+
const { value, done } = await gen.next(input);
|
|
17344
|
+
input = void 0;
|
|
17345
|
+
if (done) break;
|
|
17346
|
+
if (value.status === "BIDS_RECEIVED") {
|
|
17347
|
+
yield value;
|
|
17348
|
+
input = await this.autoSelect(order, value.bids);
|
|
17349
|
+
} else if (value.status === "AWAITING_PLACE_ORDER") {
|
|
17350
|
+
input = yield value;
|
|
17351
|
+
} else {
|
|
17352
|
+
yield value;
|
|
17353
|
+
}
|
|
17354
|
+
}
|
|
17355
|
+
} finally {
|
|
17356
|
+
await gen.return();
|
|
17357
|
+
}
|
|
17358
|
+
}
|
|
17359
|
+
/**
|
|
17360
|
+
* Batteries-included variant of {@link resume}: auto-selects the best bid each
|
|
17361
|
+
* round via {@link selectAndExecuteBest}, with no bid-selection input from the
|
|
17362
|
+
* caller. A plain `for await` loop is sufficient — there is no placement step.
|
|
17363
|
+
*
|
|
17364
|
+
* @param order - A previously placed order with a valid `id` and `session`.
|
|
17365
|
+
* @param options - Optional tuning parameters for bid collection and execution.
|
|
17366
|
+
* @yields {@link IntentOrderStatusUpdate} at each execution stage.
|
|
17367
|
+
*/
|
|
17368
|
+
async *resumeBest(order, options) {
|
|
17369
|
+
const gen = this.resume(order, options);
|
|
17370
|
+
try {
|
|
17371
|
+
let input;
|
|
17372
|
+
while (true) {
|
|
17373
|
+
const { value, done } = await gen.next(input);
|
|
17374
|
+
input = void 0;
|
|
17375
|
+
if (done) break;
|
|
17376
|
+
yield value;
|
|
17377
|
+
if (value.status === "BIDS_RECEIVED") {
|
|
17378
|
+
input = await this.autoSelect(order, value.bids);
|
|
17379
|
+
}
|
|
17380
|
+
}
|
|
17381
|
+
} finally {
|
|
17382
|
+
await gen.return();
|
|
17383
|
+
}
|
|
17384
|
+
}
|
|
17385
|
+
/**
|
|
17386
|
+
* Auto-select wrapper used by {@link executeBest} / {@link resumeBest}.
|
|
17387
|
+
*
|
|
17388
|
+
* Runs {@link selectAndExecuteBest} and returns the {@link SelectBidResult} to
|
|
17389
|
+
* feed back to the executor. If selection fails this round — all bids fail
|
|
17390
|
+
* simulation, no valid bids, or the bundler rejects the UserOp — it swallows
|
|
17391
|
+
* the error and returns `undefined`, which tells the executor to keep polling
|
|
17392
|
+
* for fresh bids until the deadline rather than aborting the order. Swallowing
|
|
17393
|
+
* the error here (rather than letting it propagate) also keeps the executor's
|
|
17394
|
+
* `finally` teardown intact, since nothing throws across the suspended
|
|
17395
|
+
* generators.
|
|
17396
|
+
*/
|
|
17397
|
+
async autoSelect(order, bids) {
|
|
17398
|
+
try {
|
|
17399
|
+
return await this.selectAndExecuteBest(order, bids);
|
|
17400
|
+
} catch (err) {
|
|
17401
|
+
console.warn(
|
|
17402
|
+
`[IntentGateway] autoSelect: bid selection failed this round, continuing to poll: ${err instanceof Error ? err.message : String(err)}`
|
|
17403
|
+
);
|
|
17404
|
+
return void 0;
|
|
16906
17405
|
}
|
|
16907
17406
|
}
|
|
16908
17407
|
/**
|
|
@@ -16958,10 +17457,52 @@ var IntentGateway = class _IntentGateway {
|
|
|
16958
17457
|
return this.bidManager.prepareSubmitBid(options);
|
|
16959
17458
|
}
|
|
16960
17459
|
/**
|
|
16961
|
-
*
|
|
16962
|
-
*
|
|
17460
|
+
* Decodes raw filler bids into first-class {@link Bid} objects that can be
|
|
17461
|
+
* ranked, simulated, and executed by the consumer.
|
|
17462
|
+
*
|
|
17463
|
+
* Delegates to {@link BidManager.buildBids}.
|
|
17464
|
+
*
|
|
17465
|
+
* @param order - The placed order the bids are competing to fill.
|
|
17466
|
+
* @param bids - Raw filler bids fetched from the coprocessor.
|
|
17467
|
+
* @param sessionPrivateKey - Optional session key override; looked up from
|
|
17468
|
+
* storage by `order.session` if omitted.
|
|
17469
|
+
* @returns Array of executable {@link Bid} objects.
|
|
17470
|
+
*/
|
|
17471
|
+
buildBids(order, bids, sessionPrivateKey) {
|
|
17472
|
+
return this.bidManager.buildBids(order, bids, sessionPrivateKey);
|
|
17473
|
+
}
|
|
17474
|
+
/**
|
|
17475
|
+
* Sorts bids by output value using the same strategy the autopilot uses.
|
|
17476
|
+
*
|
|
17477
|
+
* Delegates to {@link BidManager.sortBids}.
|
|
17478
|
+
*
|
|
17479
|
+
* @param order - The placed order whose output spec drives sorting.
|
|
17480
|
+
* @param bids - Bids to sort (from {@link buildBids}).
|
|
17481
|
+
* @returns Sorted array of {@link Bid} objects.
|
|
17482
|
+
*/
|
|
17483
|
+
async sortBids(order, bids) {
|
|
17484
|
+
return this.bidManager.sortBids(order, bids);
|
|
17485
|
+
}
|
|
17486
|
+
/**
|
|
17487
|
+
* Autopilot bid selection: sorts the given bids, simulates each until one
|
|
17488
|
+
* passes, then executes it.
|
|
17489
|
+
*
|
|
17490
|
+
* Delegates to {@link BidManager.selectAndExecuteBest}.
|
|
17491
|
+
*
|
|
17492
|
+
* @param order - The placed order to fill.
|
|
17493
|
+
* @param bids - Candidate bids (from {@link buildBids}).
|
|
17494
|
+
* @returns A {@link SelectBidResult} with the submitted UserOperation, hashes,
|
|
17495
|
+
* and fill status.
|
|
17496
|
+
*/
|
|
17497
|
+
async selectAndExecuteBest(order, bids) {
|
|
17498
|
+
return this.bidManager.selectAndExecuteBest(order, bids);
|
|
17499
|
+
}
|
|
17500
|
+
/**
|
|
17501
|
+
* Decodes, sorts, simulates, signs, and submits the best of the given raw
|
|
17502
|
+
* filler bids with no per-bid input from the caller.
|
|
16963
17503
|
*
|
|
16964
|
-
* Delegates to {@link BidManager.selectBid}.
|
|
17504
|
+
* Delegates to {@link BidManager.selectBid}. Prefer {@link buildBids} +
|
|
17505
|
+
* {@link selectAndExecuteBest} (or {@link executeBest}) for new code.
|
|
16965
17506
|
*
|
|
16966
17507
|
* @param order - The placed order to fill.
|
|
16967
17508
|
* @param bids - Raw filler bids fetched from the coprocessor.
|
|
@@ -17881,7 +18422,7 @@ var HyperFungibleTokenABI = [
|
|
|
17881
18422
|
internalType: "uint256"
|
|
17882
18423
|
}
|
|
17883
18424
|
],
|
|
17884
|
-
stateMutability: "
|
|
18425
|
+
stateMutability: "nonpayable"
|
|
17885
18426
|
},
|
|
17886
18427
|
{
|
|
17887
18428
|
type: "function",
|
|
@@ -17932,7 +18473,7 @@ var HyperFungibleTokenABI = [
|
|
|
17932
18473
|
internalType: "uint256"
|
|
17933
18474
|
}
|
|
17934
18475
|
],
|
|
17935
|
-
stateMutability: "
|
|
18476
|
+
stateMutability: "nonpayable"
|
|
17936
18477
|
},
|
|
17937
18478
|
{
|
|
17938
18479
|
type: "function",
|
|
@@ -17983,7 +18524,7 @@ var HyperFungibleTokenABI = [
|
|
|
17983
18524
|
internalType: "uint256"
|
|
17984
18525
|
}
|
|
17985
18526
|
],
|
|
17986
|
-
stateMutability: "
|
|
18527
|
+
stateMutability: "nonpayable"
|
|
17987
18528
|
},
|
|
17988
18529
|
{
|
|
17989
18530
|
type: "function",
|
|
@@ -19002,7 +19543,7 @@ var WrappedHyperFungibleTokenABI = [
|
|
|
19002
19543
|
internalType: "uint256"
|
|
19003
19544
|
}
|
|
19004
19545
|
],
|
|
19005
|
-
stateMutability: "
|
|
19546
|
+
stateMutability: "nonpayable"
|
|
19006
19547
|
},
|
|
19007
19548
|
{
|
|
19008
19549
|
type: "function",
|
|
@@ -19053,7 +19594,7 @@ var WrappedHyperFungibleTokenABI = [
|
|
|
19053
19594
|
internalType: "uint256"
|
|
19054
19595
|
}
|
|
19055
19596
|
],
|
|
19056
|
-
stateMutability: "
|
|
19597
|
+
stateMutability: "nonpayable"
|
|
19057
19598
|
},
|
|
19058
19599
|
{
|
|
19059
19600
|
type: "function",
|
|
@@ -19104,7 +19645,7 @@ var WrappedHyperFungibleTokenABI = [
|
|
|
19104
19645
|
internalType: "uint256"
|
|
19105
19646
|
}
|
|
19106
19647
|
],
|
|
19107
|
-
stateMutability: "
|
|
19648
|
+
stateMutability: "nonpayable"
|
|
19108
19649
|
},
|
|
19109
19650
|
{
|
|
19110
19651
|
type: "function",
|
|
@@ -19646,13 +20187,13 @@ var HyperFungibleToken = class {
|
|
|
19646
20187
|
};
|
|
19647
20188
|
let totalNativeCost = 0n;
|
|
19648
20189
|
try {
|
|
19649
|
-
|
|
20190
|
+
const { result } = await this.source.client.simulateContract({
|
|
19650
20191
|
address: params.token,
|
|
19651
20192
|
abi: HyperFungibleTokenABI,
|
|
19652
20193
|
functionName: "quote",
|
|
19653
20194
|
args: [sendParams]
|
|
19654
20195
|
});
|
|
19655
|
-
totalNativeCost =
|
|
20196
|
+
totalNativeCost = result * 101n / 100n;
|
|
19656
20197
|
} catch {
|
|
19657
20198
|
}
|
|
19658
20199
|
return {
|
|
@@ -21606,6 +22147,8 @@ exports.TimeoutStatus = TimeoutStatus;
|
|
|
21606
22147
|
exports.TokenGateway = TokenGateway;
|
|
21607
22148
|
exports.TronChain = TronChain;
|
|
21608
22149
|
exports.USE_ETHERSCAN_CHAINS = USE_ETHERSCAN_CHAINS;
|
|
22150
|
+
exports.UnsupportedIntentQuotePairError = UnsupportedIntentQuotePairError;
|
|
22151
|
+
exports.UnsupportedIntentQuoteStrategyError = UnsupportedIntentQuoteStrategyError;
|
|
21609
22152
|
exports.WrappedHyperFungibleTokenABI = WrappedHyperFungibleTokenABI;
|
|
21610
22153
|
exports.__test = __test;
|
|
21611
22154
|
exports.adjustDecimals = adjustDecimals;
|