genius-intents 0.18.2 → 0.19.0
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/CHANGELOG.md +46 -3
- package/dist/genius-intents.d.ts.map +1 -1
- package/dist/genius-intents.js +8 -2
- package/dist/genius-intents.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/dex/algebra/algebra-factory.abi.d.ts +3 -0
- package/dist/lib/dex/algebra/algebra-factory.abi.d.ts.map +1 -0
- package/dist/lib/dex/algebra/algebra-factory.abi.js +34 -0
- package/dist/lib/dex/algebra/algebra-factory.abi.js.map +1 -0
- package/dist/lib/dex/algebra/algebra-quoter.abi.d.ts +3 -0
- package/dist/lib/dex/algebra/algebra-quoter.abi.d.ts.map +1 -0
- package/dist/lib/dex/algebra/algebra-quoter.abi.js +135 -0
- package/dist/lib/dex/algebra/algebra-quoter.abi.js.map +1 -0
- package/dist/lib/dex/algebra/algebra-router.abi.d.ts +3 -0
- package/dist/lib/dex/algebra/algebra-router.abi.d.ts.map +1 -0
- package/dist/lib/dex/algebra/algebra-router.abi.js +273 -0
- package/dist/lib/dex/algebra/algebra-router.abi.js.map +1 -0
- package/dist/lib/dex/uniswapv3/slipstream-quoter.abi.d.ts +3 -0
- package/dist/lib/dex/uniswapv3/slipstream-quoter.abi.d.ts.map +1 -0
- package/dist/lib/dex/uniswapv3/slipstream-quoter.abi.js +30 -0
- package/dist/lib/dex/uniswapv3/slipstream-quoter.abi.js.map +1 -0
- package/dist/lib/dex/uniswapv3/slipstream-router.abi.d.ts +3 -0
- package/dist/lib/dex/uniswapv3/slipstream-router.abi.d.ts.map +1 -0
- package/dist/lib/dex/uniswapv3/slipstream-router.abi.js +45 -0
- package/dist/lib/dex/uniswapv3/slipstream-router.abi.js.map +1 -0
- package/dist/lib/dex/ve33/aerodrome-factory.abi.d.ts +3 -0
- package/dist/lib/dex/ve33/aerodrome-factory.abi.d.ts.map +1 -0
- package/dist/lib/dex/ve33/aerodrome-factory.abi.js +17 -0
- package/dist/lib/dex/ve33/aerodrome-factory.abi.js.map +1 -0
- package/dist/lib/dex/ve33/aerodrome-router.abi.d.ts +3 -0
- package/dist/lib/dex/ve33/aerodrome-router.abi.d.ts.map +1 -0
- package/dist/lib/dex/ve33/aerodrome-router.abi.js +119 -0
- package/dist/lib/dex/ve33/aerodrome-router.abi.js.map +1 -0
- package/dist/lib/dex/ve33/blackhole-router.abi.d.ts +3 -0
- package/dist/lib/dex/ve33/blackhole-router.abi.d.ts.map +1 -0
- package/dist/lib/dex/ve33/blackhole-router.abi.js +1492 -0
- package/dist/lib/dex/ve33/blackhole-router.abi.js.map +1 -0
- package/dist/lib/dex/ve33/pharaoh-router.abi.d.ts +3 -0
- package/dist/lib/dex/ve33/pharaoh-router.abi.d.ts.map +1 -0
- package/dist/lib/dex/ve33/pharaoh-router.abi.js +502 -0
- package/dist/lib/dex/ve33/pharaoh-router.abi.js.map +1 -0
- package/dist/lib/dex/ve33/velodrome-router.abi.d.ts +3 -0
- package/dist/lib/dex/ve33/velodrome-router.abi.d.ts.map +1 -0
- package/dist/lib/dex/ve33/velodrome-router.abi.js +634 -0
- package/dist/lib/dex/ve33/velodrome-router.abi.js.map +1 -0
- package/dist/protocols/algebra/algebra.service.d.ts +61 -0
- package/dist/protocols/algebra/algebra.service.d.ts.map +1 -0
- package/dist/protocols/algebra/algebra.service.js +562 -0
- package/dist/protocols/algebra/algebra.service.js.map +1 -0
- package/dist/protocols/algebra/algebra.types.d.ts +83 -0
- package/dist/protocols/algebra/algebra.types.d.ts.map +1 -0
- package/dist/protocols/algebra/algebra.types.js +9 -0
- package/dist/protocols/algebra/algebra.types.js.map +1 -0
- package/dist/protocols/algebra/index.d.ts +3 -0
- package/dist/protocols/algebra/index.d.ts.map +1 -0
- package/dist/protocols/algebra/index.js +21 -0
- package/dist/protocols/algebra/index.js.map +1 -0
- package/dist/protocols/okx/okx.service.d.ts +9 -8
- package/dist/protocols/okx/okx.service.d.ts.map +1 -1
- package/dist/protocols/okx/okx.service.js +133 -143
- package/dist/protocols/okx/okx.service.js.map +1 -1
- package/dist/protocols/okx/okx.types.d.ts +9 -3
- package/dist/protocols/okx/okx.types.d.ts.map +1 -1
- package/dist/protocols/relay/relay.service.d.ts +3 -0
- package/dist/protocols/relay/relay.service.d.ts.map +1 -1
- package/dist/protocols/relay/relay.service.js +7 -1
- package/dist/protocols/relay/relay.service.js.map +1 -1
- package/dist/protocols/v2-dex/v2-dex.service.d.ts +0 -3
- package/dist/protocols/v2-dex/v2-dex.service.d.ts.map +1 -1
- package/dist/protocols/v2-dex/v2-dex.service.js +54 -55
- package/dist/protocols/v2-dex/v2-dex.service.js.map +1 -1
- package/dist/protocols/v2-dex/v2-dex.types.d.ts +0 -10
- package/dist/protocols/v2-dex/v2-dex.types.d.ts.map +1 -1
- package/dist/protocols/v3-dex/v3-dex.service.d.ts +50 -6
- package/dist/protocols/v3-dex/v3-dex.service.d.ts.map +1 -1
- package/dist/protocols/v3-dex/v3-dex.service.js +369 -193
- package/dist/protocols/v3-dex/v3-dex.service.js.map +1 -1
- package/dist/protocols/v3-dex/v3-dex.types.d.ts +4 -1
- package/dist/protocols/v3-dex/v3-dex.types.d.ts.map +1 -1
- package/dist/protocols/v3-dex/v3-dex.types.js +3 -0
- package/dist/protocols/v3-dex/v3-dex.types.js.map +1 -1
- package/dist/protocols/ve33/index.d.ts +3 -0
- package/dist/protocols/ve33/index.d.ts.map +1 -0
- package/dist/protocols/ve33/index.js +6 -0
- package/dist/protocols/ve33/index.js.map +1 -0
- package/dist/protocols/ve33/ve33.handlers.d.ts +3 -0
- package/dist/protocols/ve33/ve33.handlers.d.ts.map +1 -0
- package/dist/protocols/ve33/ve33.handlers.js +187 -0
- package/dist/protocols/ve33/ve33.handlers.js.map +1 -0
- package/dist/protocols/ve33/ve33.service.d.ts +26 -0
- package/dist/protocols/ve33/ve33.service.d.ts.map +1 -0
- package/dist/protocols/ve33/ve33.service.js +486 -0
- package/dist/protocols/ve33/ve33.service.js.map +1 -0
- package/dist/protocols/ve33/ve33.types.d.ts +136 -0
- package/dist/protocols/ve33/ve33.types.d.ts.map +1 -0
- package/dist/protocols/ve33/ve33.types.js +11 -0
- package/dist/protocols/ve33/ve33.types.js.map +1 -0
- package/dist/types/enums.d.ts +5 -2
- package/dist/types/enums.d.ts.map +1 -1
- package/dist/types/enums.js +3 -0
- package/dist/types/enums.js.map +1 -1
- package/dist/types/price-params.d.ts +4 -0
- package/dist/types/price-params.d.ts.map +1 -1
- package/dist/types/price-response.d.ts +4 -2
- package/dist/types/price-response.d.ts.map +1 -1
- package/dist/types/quote-response.d.ts +3 -1
- package/dist/types/quote-response.d.ts.map +1 -1
- package/dist/utils/dex-deployment-key.d.ts +14 -0
- package/dist/utils/dex-deployment-key.d.ts.map +1 -0
- package/dist/utils/dex-deployment-key.js +17 -0
- package/dist/utils/dex-deployment-key.js.map +1 -0
- package/dist/utils/min-amount-out.d.ts +12 -0
- package/dist/utils/min-amount-out.d.ts.map +1 -0
- package/dist/utils/min-amount-out.js +18 -0
- package/dist/utils/min-amount-out.js.map +1 -0
- package/dist/utils/sort-addresses.d.ts +2 -0
- package/dist/utils/sort-addresses.d.ts.map +1 -0
- package/dist/utils/sort-addresses.js +14 -0
- package/dist/utils/sort-addresses.js.map +1 -0
- package/package.json +1 -1
|
@@ -7,17 +7,22 @@ const v3_dex_types_1 = require("./v3-dex.types");
|
|
|
7
7
|
const sushiswap_route_processor_9_abi_1 = require("../../lib/dex/sushiswap/sushiswap-route-processor-9.abi");
|
|
8
8
|
const pacakeswapV3_router_abi_1 = require("../../lib/dex/pancakeswap/pacakeswapV3-router.abi");
|
|
9
9
|
const v3_offchain_quoter_service_1 = require("../../lib/dex/uniswapv3/v3-offchain-quoter.service");
|
|
10
|
+
const slipstream_quoter_abi_1 = require("../../lib/dex/uniswapv3/slipstream-quoter.abi");
|
|
11
|
+
const slipstream_router_abi_1 = require("../../lib/dex/uniswapv3/slipstream-router.abi");
|
|
10
12
|
const uniswapV3_quoter_abi_1 = require("../../lib/dex/uniswapv3/uniswapV3-quoter.abi");
|
|
11
13
|
const uniswapV3_router_abi_1 = require("../../lib/dex/uniswapv3/uniswapV3-router.abi");
|
|
12
14
|
const uniswapV3_pool_abi_1 = require("../../lib/dex/uniswapv3/uniswapV3-pool.abi");
|
|
13
15
|
const enums_1 = require("../../types/enums");
|
|
16
|
+
const min_amount_out_1 = require("../../utils/min-amount-out");
|
|
14
17
|
const wrapped_native_1 = require("../../utils/wrapped-native");
|
|
15
18
|
const constants_1 = require("../../utils/constants");
|
|
16
19
|
const is_native_1 = require("../../utils/is-native");
|
|
17
20
|
const logger_1 = require("../../utils/logger");
|
|
18
21
|
const utils_1 = require("../../utils");
|
|
22
|
+
const sort_addresses_1 = require("../../utils/sort-addresses");
|
|
23
|
+
const dex_deployment_key_1 = require("../../utils/dex-deployment-key");
|
|
19
24
|
/**
|
|
20
|
-
* Service for interacting with V3-style decentralized exchanges (DEXs) such as Uniswap V3, PancakeSwap V3, and SushiSwap V3.
|
|
25
|
+
* Service for interacting with V3-style decentralized exchanges (DEXs) such as Uniswap V3, PancakeSwap V3, Slipstream, and SushiSwap V3.
|
|
21
26
|
*
|
|
22
27
|
* This service provides price discovery and swap quote functionality for supported chains and DEX deployments.
|
|
23
28
|
* It supports multicall-based quoting across multiple fee tiers and deployments, with fallback logic for different quoter contract versions.
|
|
@@ -25,13 +30,48 @@ const utils_1 = require("../../utils");
|
|
|
25
30
|
* @remarks
|
|
26
31
|
* - Only single-chain swaps are supported.
|
|
27
32
|
* - Deployments and RPC clients are configured via the constructor.
|
|
28
|
-
* - Supports Uniswap V3, PancakeSwap V3, and SushiSwap V3 deployments on Avalanche, Arbitrum, BSC, Base, Ethereum, Optimism, and Polygon.
|
|
33
|
+
* - Supports Uniswap V3, PancakeSwap V3, Slipstream, and SushiSwap V3 deployments on Avalanche, Arbitrum, BSC, Base, Ethereum, Optimism, HyperEVM, and Polygon.
|
|
29
34
|
*
|
|
30
35
|
* @example
|
|
31
36
|
* ```typescript
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
import { base, optimism } from 'viem/chains';
|
|
38
|
+
|
|
39
|
+
async function t() {
|
|
40
|
+
const rpcs = {
|
|
41
|
+
[ChainIdEnum.BASE]: '<url>',
|
|
42
|
+
[ChainIdEnum.OPTIMISM]: '<url>',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const solidlyChainConfigs = {
|
|
46
|
+
[ChainIdEnum.BASE]: base,
|
|
47
|
+
[ChainIdEnum.OPTIMISM]: optimism,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const s = new V3StyleDexService({ rpcs, uniV3ChainConfigs: solidlyChainConfigs });
|
|
51
|
+
const a = await s.fetchQuote({
|
|
52
|
+
from: '0x055698dead6666c0690B1419647556149005504D',
|
|
53
|
+
receiver: '0x055698dead6666c0690B1419647556149005504D',
|
|
54
|
+
tokenOut: ZERO_ADDRESS, // WETH
|
|
55
|
+
tokenIn: '0x940181a94A35A4569E4529A3CDfB74e38FD98631', // USDC
|
|
56
|
+
amountIn: '100000000000000000000', // s0.01 WETH
|
|
57
|
+
networkIn: ChainIdEnum.BASE,
|
|
58
|
+
networkOut: ChainIdEnum.BASE,
|
|
59
|
+
slippage: 10, // 1%
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const c = await s.fetchQuote({
|
|
63
|
+
from: '0x055698dead6666c0690B1419647556149005504D',
|
|
64
|
+
receiver: '0x055698dead6666c0690B1419647556149005504D',
|
|
65
|
+
tokenIn: ZERO_ADDRESS, // WETH
|
|
66
|
+
tokenOut: '0x9560e827aF36c94D2Ac33a39bCE1Fe78631088Db', // USDC
|
|
67
|
+
amountIn: '100000000000000000', // 0.01 WETH
|
|
68
|
+
networkIn: ChainIdEnum.OPTIMISM,
|
|
69
|
+
networkOut: ChainIdEnum.OPTIMISM,
|
|
70
|
+
slippage: 10, // 1%
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
t();
|
|
35
75
|
* ```
|
|
36
76
|
*
|
|
37
77
|
* @implements IIntentProtocol
|
|
@@ -74,6 +114,14 @@ class V3StyleDexService {
|
|
|
74
114
|
this.pancakeFeeTiers = [100, 500, 2500, 10000];
|
|
75
115
|
this.pangolinFeeTiers = [40, 100, 500, 2500, 8000];
|
|
76
116
|
this.hyperswapFeeTiers = [100, 500, 3000, 10000];
|
|
117
|
+
this.pharaohFeeTiers = [50, 100, 250, 500, 3000, 10000];
|
|
118
|
+
this.aerodromeFeeTiers = [100, 400, 500, 3000, 10000];
|
|
119
|
+
this.velodromeFeeTiers = [500, 3000, 10000];
|
|
120
|
+
/**
|
|
121
|
+
* Tick spacings for slipstream style dexes
|
|
122
|
+
*/
|
|
123
|
+
this.aerodromeTickSpacings = [1, 10, 60, 200];
|
|
124
|
+
this.velodromeTickSpacings = [10, 60, 200];
|
|
77
125
|
/**
|
|
78
126
|
* The deployments supported out of the box for V3 style dexes
|
|
79
127
|
*/
|
|
@@ -93,6 +141,13 @@ class V3StyleDexService {
|
|
|
93
141
|
router: '0x5485A0751a249225D3bA2f6f296551507e22547f',
|
|
94
142
|
initHash: '0x40231f6b438bce0797c9ada29b718a87ea0a5cea3fe9a771abdd76bd41a3e545',
|
|
95
143
|
},
|
|
144
|
+
{
|
|
145
|
+
dex: v3_dex_types_1.UniswapV3StyleDexEnum.PHARAOH_CLMM, // reuse existing UNISWAP_V3 branch/ABIs
|
|
146
|
+
router: '0x062c62cA66E50Cfe277A95564Fe5bB504db1Fab8', // SwapRouter (immutable & verified)
|
|
147
|
+
quoter: '0xAAAEA10b0e6FBe566FE27c3A023DC5D8cA6Bca3d', // QuoterV2
|
|
148
|
+
factory: '0xAAA32926fcE6bE95ea2c51cB4Fcb60836D320C42', // PharaohV2Factory (CL)
|
|
149
|
+
initHash: '0x1565b129f2d1790f12d45301b9b084335626f0c92410bc43130763b69971135d',
|
|
150
|
+
},
|
|
96
151
|
// {
|
|
97
152
|
// dex: UniswapV3StyleDexEnum.SUSHISWAP_V3,
|
|
98
153
|
// router: '0x81602ef321c46d73f5ba7f476947ae1a862957dc', // RouteProcessor9
|
|
@@ -159,6 +214,13 @@ class V3StyleDexService {
|
|
|
159
214
|
factory: '0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865',
|
|
160
215
|
initHash: '0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2',
|
|
161
216
|
},
|
|
217
|
+
{
|
|
218
|
+
dex: v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM,
|
|
219
|
+
quoter: '0x254cF9E1E6e233aa1AC962CB9B05b2cfeAaE15b0',
|
|
220
|
+
router: '0xBE6D8f0d05cC4be24d5167a3eF062215bE6D18a5',
|
|
221
|
+
factory: '0x5e7BB104d84c7CB9B682AaC2F3d509f5F406809A',
|
|
222
|
+
initHash: '0xffb9af9ea6d9e39da47392ecc7055277b9915b8bfc9f83f105821b7791a6ae30',
|
|
223
|
+
},
|
|
162
224
|
// {
|
|
163
225
|
// dex: UniswapV3StyleDexEnum.SUSHISWAP_V3,
|
|
164
226
|
// router: '0x81602ef321c46d73f5ba7f476947ae1a862957dc', // RouteProcessor9
|
|
@@ -195,6 +257,13 @@ class V3StyleDexService {
|
|
|
195
257
|
factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
|
|
196
258
|
initHash: '0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54',
|
|
197
259
|
},
|
|
260
|
+
{
|
|
261
|
+
dex: v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM,
|
|
262
|
+
quoter: '0x89D8218ed5fF1e46d8dcd33fb0bbeE3be1621466',
|
|
263
|
+
router: '0x0792a633F0c19c351081CF4B211F68F79bCc9676',
|
|
264
|
+
factory: '0xCc0bDDB707055e04e497aB22a59c2aF4391cd12F',
|
|
265
|
+
initHash: '0x339492e30b7a68609e535da9b0773082bfe60230ca47639ee5566007d525f5a7',
|
|
266
|
+
},
|
|
198
267
|
// {
|
|
199
268
|
// dex: UniswapV3StyleDexEnum.SUSHISWAP_V3,
|
|
200
269
|
// router: '0x81602ef321c46d73f5ba7f476947ae1a862957dc', // RouteProcessor9
|
|
@@ -272,15 +341,17 @@ class V3StyleDexService {
|
|
|
272
341
|
if (!deployments.length) {
|
|
273
342
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `No V3-style deployments configured for network ${networkIn}`);
|
|
274
343
|
}
|
|
344
|
+
// Early exit if caller supplied a known pair & minOut (optimistic path)
|
|
275
345
|
if (pair && expectedAmountOutMin && dex) {
|
|
276
|
-
/**
|
|
277
|
-
* Use the dex enum to find the corresponding router given the networkIn
|
|
278
|
-
*/
|
|
279
346
|
const router = deployments.find(d => d.dex === dex)?.router;
|
|
280
347
|
if (!router) {
|
|
281
348
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `No router found for dex ${dex} on chainId ${networkIn}`);
|
|
282
349
|
}
|
|
283
|
-
const dexKey =
|
|
350
|
+
const dexKey = (0, dex_deployment_key_1.dexDeploymentKey)({
|
|
351
|
+
dex: dex,
|
|
352
|
+
router: router,
|
|
353
|
+
network: networkIn,
|
|
354
|
+
});
|
|
284
355
|
const earlyResponse = {
|
|
285
356
|
dex: dexKey,
|
|
286
357
|
amountOut: expectedAmountOutMin,
|
|
@@ -293,7 +364,7 @@ class V3StyleDexService {
|
|
|
293
364
|
path: pair,
|
|
294
365
|
notes: `Early return due to supplied pair and amountOutMin`,
|
|
295
366
|
};
|
|
296
|
-
|
|
367
|
+
return {
|
|
297
368
|
protocol: this.protocol,
|
|
298
369
|
networkIn,
|
|
299
370
|
networkOut,
|
|
@@ -304,11 +375,9 @@ class V3StyleDexService {
|
|
|
304
375
|
slippage,
|
|
305
376
|
protocolResponse: earlyResponse,
|
|
306
377
|
};
|
|
307
|
-
return priceResponse;
|
|
308
378
|
}
|
|
309
379
|
const client = this._clients[networkIn];
|
|
310
380
|
if (!client) {
|
|
311
|
-
console.log(`No RPC client configured for chainId ${networkIn}`);
|
|
312
381
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `No RPC client configured for chainId ${networkIn}`);
|
|
313
382
|
}
|
|
314
383
|
// === Native <-> Wrapped normalization ===
|
|
@@ -316,7 +385,6 @@ class V3StyleDexService {
|
|
|
316
385
|
const nativeOut = (0, is_native_1.isNative)(tokenOut);
|
|
317
386
|
const wrapped = wrapped_native_1.wrappedNativeTokens[networkIn];
|
|
318
387
|
if ((nativeIn || nativeOut) && !wrapped) {
|
|
319
|
-
console.log(`No wrapped native token mapping for chainId ${networkIn}`);
|
|
320
388
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `Missing wrapped native token mapping for chainId ${networkIn}`);
|
|
321
389
|
}
|
|
322
390
|
const tokenInForQuote = nativeIn ? wrapped : tokenIn;
|
|
@@ -349,10 +417,37 @@ class V3StyleDexService {
|
|
|
349
417
|
const orthodox = deployments.filter(d => !!d.quoter);
|
|
350
418
|
const quoterless = deployments.filter(d => !('quoter' in d) || !d.quoter);
|
|
351
419
|
let best;
|
|
420
|
+
// Helper: execute a batch (multicall vs per-call for HyperEVM)
|
|
421
|
+
const execBatch = async (contracts) => {
|
|
422
|
+
if (!contracts.length)
|
|
423
|
+
return [];
|
|
424
|
+
if (networkIn === enums_1.ChainIdEnum.HYPEREVM) {
|
|
425
|
+
return Promise.all(contracts.map(async (c) => {
|
|
426
|
+
try {
|
|
427
|
+
const result = await client.readContract({
|
|
428
|
+
address: c.address,
|
|
429
|
+
abi: c.abi,
|
|
430
|
+
functionName: c.functionName,
|
|
431
|
+
args: c.args,
|
|
432
|
+
});
|
|
433
|
+
return { status: 'success', result };
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
return { status: 'failure', error };
|
|
437
|
+
}
|
|
438
|
+
}));
|
|
439
|
+
}
|
|
440
|
+
return client.multicall({ allowFailure: true, contracts });
|
|
441
|
+
};
|
|
352
442
|
/** ---------------------------------------
|
|
353
|
-
* Round 1: QuoterV2
|
|
443
|
+
* Round 1: QuoterV2-style calls
|
|
444
|
+
* - Uni/Pancake/Pangolin/etc: use fee (uint24)
|
|
445
|
+
* - Slipstream (Velodrome/Aerodrome): use tickSpacing (int24)
|
|
354
446
|
* ------------------------------------- */
|
|
355
|
-
|
|
447
|
+
// A) Uni-like group (fee tiers)
|
|
448
|
+
const v2ContractsUniLike = orthodox
|
|
449
|
+
.filter(d => !this._isSlipstreamDexString(d.dex))
|
|
450
|
+
.flatMap(d => {
|
|
356
451
|
const feeTiers = this._determineFeeTiersForDex(d.dex);
|
|
357
452
|
return feeTiers.map(fee => ({
|
|
358
453
|
address: d.quoter,
|
|
@@ -369,104 +464,136 @@ class V3StyleDexService {
|
|
|
369
464
|
],
|
|
370
465
|
}));
|
|
371
466
|
});
|
|
372
|
-
const
|
|
467
|
+
const v2MetasUniLike = orthodox
|
|
468
|
+
.filter(d => !this._isSlipstreamDexString(d.dex))
|
|
469
|
+
.flatMap(d => {
|
|
373
470
|
const feeTiers = this._determineFeeTiersForDex(d.dex);
|
|
374
471
|
return feeTiers.map(fee => ({
|
|
375
|
-
dex:
|
|
472
|
+
dex: (0, dex_deployment_key_1.dexDeploymentKey)({
|
|
473
|
+
dex: d.dex,
|
|
474
|
+
network: networkIn,
|
|
475
|
+
router: d.router,
|
|
476
|
+
}),
|
|
376
477
|
fee,
|
|
377
478
|
quoter: d.quoter,
|
|
378
479
|
method: 'QuoterV2.quoteExactInputSingle',
|
|
480
|
+
isSlip: false,
|
|
481
|
+
}));
|
|
482
|
+
});
|
|
483
|
+
// B) Slipstream group (tick spacing)
|
|
484
|
+
const v2ContractsSlip = orthodox
|
|
485
|
+
.filter(d => this._isSlipstreamDexString(d.dex))
|
|
486
|
+
.flatMap(d => {
|
|
487
|
+
const tss = this._determineTickSpacingsForDex(d.dex);
|
|
488
|
+
return tss.map(ts => ({
|
|
489
|
+
address: d.quoter,
|
|
490
|
+
abi: slipstream_quoter_abi_1.slipstreamQuoterAbi,
|
|
491
|
+
functionName: 'quoteExactInputSingle',
|
|
492
|
+
args: [
|
|
493
|
+
{
|
|
494
|
+
tokenIn: (0, viem_1.getAddress)(tokenInForQuote),
|
|
495
|
+
tokenOut: (0, viem_1.getAddress)(tokenOutForQuote),
|
|
496
|
+
amountIn: amountInBI,
|
|
497
|
+
tickSpacing: ts,
|
|
498
|
+
sqrtPriceLimitX96: 0n,
|
|
499
|
+
},
|
|
500
|
+
],
|
|
379
501
|
}));
|
|
380
502
|
});
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
503
|
+
const v2MetasSlip = orthodox
|
|
504
|
+
.filter(d => this._isSlipstreamDexString(d.dex))
|
|
505
|
+
.flatMap(d => this._determineTickSpacingsForDex(d.dex).map(ts => ({
|
|
506
|
+
dex: (0, dex_deployment_key_1.dexDeploymentKey)({
|
|
507
|
+
dex: d.dex,
|
|
508
|
+
network: networkIn,
|
|
509
|
+
router: d.router,
|
|
510
|
+
}),
|
|
511
|
+
fee: ts, // store tickSpacing in "fee" slot to reuse payload shape
|
|
512
|
+
quoter: d.quoter,
|
|
513
|
+
method: 'QuoterV2.quoteExactInputSingle',
|
|
514
|
+
isSlip: true,
|
|
515
|
+
})));
|
|
516
|
+
// Execute both groups
|
|
517
|
+
const [v2ResultsUniLike, v2ResultsSlip] = await Promise.all([
|
|
518
|
+
execBatch(v2ContractsUniLike),
|
|
519
|
+
execBatch(v2ContractsSlip),
|
|
520
|
+
]);
|
|
521
|
+
// Unified handling for success/revert-decode
|
|
522
|
+
const handleV2Group = (results, metas, abi) => {
|
|
523
|
+
for (let i = 0; i < results.length; i++) {
|
|
524
|
+
const res = results[i];
|
|
525
|
+
const meta = metas[i];
|
|
526
|
+
if (!res || !meta)
|
|
527
|
+
continue;
|
|
528
|
+
// success path
|
|
529
|
+
if (res.status === 'success' && Array.isArray(res.result)) {
|
|
530
|
+
const [amountOut, sqrtAfter, ticks, gas] = res.result;
|
|
531
|
+
const candidate = {
|
|
532
|
+
amountOut,
|
|
533
|
+
payload: {
|
|
534
|
+
dex: meta.dex,
|
|
535
|
+
quoterAddress: meta.quoter,
|
|
536
|
+
method: meta.method,
|
|
537
|
+
feeTier: meta.fee, // NOTE: tickSpacing for Slipstream
|
|
538
|
+
amountOut: amountOut.toString(),
|
|
539
|
+
gasEstimate: gas ? gas.toString() : undefined,
|
|
540
|
+
sqrtPriceX96After: sqrtAfter ? sqrtAfter.toString() : undefined,
|
|
541
|
+
initializedTicksCrossed: ticks,
|
|
542
|
+
notes: (meta.isSlip ? 'Slipstream tickSpacing; ' : '') +
|
|
543
|
+
(nativeIn || nativeOut
|
|
544
|
+
? `Quoted with wrapped native: in=${nativeIn ? 'wrapped' : 'erc20'}, out=${nativeOut ? 'wrapped' : 'erc20'}`
|
|
545
|
+
: ''),
|
|
546
|
+
},
|
|
547
|
+
};
|
|
548
|
+
if (!best || candidate.amountOut > best.amountOut)
|
|
549
|
+
best = candidate;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
// revert decode (some quoters return data on revert)
|
|
553
|
+
// @ts-ignore – Viem surfaces revert data in error.data
|
|
554
|
+
const data = res?.error?.data;
|
|
555
|
+
if (!data || data === '0x')
|
|
556
|
+
continue;
|
|
385
557
|
try {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
args: contract.args,
|
|
558
|
+
const [amountOut, sqrtAfter, ticks, gas] = (0, viem_1.decodeFunctionResult)({
|
|
559
|
+
abi,
|
|
560
|
+
functionName: 'quoteExactInputSingle',
|
|
561
|
+
data,
|
|
391
562
|
});
|
|
392
|
-
|
|
563
|
+
const candidate = {
|
|
564
|
+
amountOut,
|
|
565
|
+
payload: {
|
|
566
|
+
dex: meta.dex,
|
|
567
|
+
quoterAddress: meta.quoter,
|
|
568
|
+
method: meta.method,
|
|
569
|
+
feeTier: meta.fee, // NOTE: tickSpacing for Slipstream
|
|
570
|
+
amountOut: amountOut.toString(),
|
|
571
|
+
gasEstimate: gas ? gas.toString() : undefined,
|
|
572
|
+
sqrtPriceX96After: sqrtAfter ? sqrtAfter.toString() : undefined,
|
|
573
|
+
initializedTicksCrossed: ticks,
|
|
574
|
+
notes: (meta.isSlip ? 'Slipstream tickSpacing; ' : '') +
|
|
575
|
+
(nativeIn || nativeOut
|
|
576
|
+
? `Quoted with wrapped native: in=${nativeIn ? 'wrapped' : 'erc20'}, out=${nativeOut ? 'wrapped' : 'erc20'}`
|
|
577
|
+
: ''),
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
if (!best || candidate.amountOut > (best.amountOut ?? 0n))
|
|
581
|
+
best = candidate;
|
|
393
582
|
}
|
|
394
|
-
catch
|
|
395
|
-
|
|
583
|
+
catch {
|
|
584
|
+
/* ignore; continue */
|
|
396
585
|
}
|
|
397
|
-
}));
|
|
398
|
-
}
|
|
399
|
-
else {
|
|
400
|
-
v2Results = v2Contracts.length
|
|
401
|
-
? await client.multicall({ allowFailure: true, contracts: v2Contracts })
|
|
402
|
-
: [];
|
|
403
|
-
}
|
|
404
|
-
for (let i = 0; i < v2Results.length; i++) {
|
|
405
|
-
const res = v2Results[i];
|
|
406
|
-
const meta = v2Metas[i];
|
|
407
|
-
if (!res || !meta)
|
|
408
|
-
continue;
|
|
409
|
-
if (res.status === 'success' && Array.isArray(res.result)) {
|
|
410
|
-
const [amountOut, sqrtAfter, ticks, gas] = res.result;
|
|
411
|
-
const candidate = {
|
|
412
|
-
amountOut,
|
|
413
|
-
payload: {
|
|
414
|
-
dex: meta.dex,
|
|
415
|
-
quoterAddress: meta.quoter,
|
|
416
|
-
method: meta.method,
|
|
417
|
-
feeTier: meta.fee,
|
|
418
|
-
amountOut: amountOut.toString(),
|
|
419
|
-
gasEstimate: gas ? gas.toString() : undefined,
|
|
420
|
-
sqrtPriceX96After: sqrtAfter ? sqrtAfter.toString() : undefined,
|
|
421
|
-
initializedTicksCrossed: ticks,
|
|
422
|
-
notes: nativeIn || nativeOut
|
|
423
|
-
? `Quoted with wrapped native: in=${nativeIn ? 'wrapped' : 'erc20'}, out=${nativeOut ? 'wrapped' : 'erc20'}`
|
|
424
|
-
: undefined,
|
|
425
|
-
},
|
|
426
|
-
};
|
|
427
|
-
if (!best || candidate.amountOut > best.amountOut)
|
|
428
|
-
best = candidate;
|
|
429
|
-
continue;
|
|
430
|
-
}
|
|
431
|
-
// Try to decode revert (some quoters return data on revert)
|
|
432
|
-
// @ts-ignore – Viem surfaces revert data in error.data
|
|
433
|
-
const data = res?.error?.data;
|
|
434
|
-
if (!data || data === '0x')
|
|
435
|
-
continue;
|
|
436
|
-
try {
|
|
437
|
-
const [amountOut, sqrtAfter, ticks, gas] = (0, viem_1.decodeFunctionResult)({
|
|
438
|
-
abi: uniswapV3_quoter_abi_1.uniswapV3QuoterAbi,
|
|
439
|
-
functionName: 'quoteExactInputSingle',
|
|
440
|
-
data,
|
|
441
|
-
});
|
|
442
|
-
const candidate = {
|
|
443
|
-
amountOut,
|
|
444
|
-
payload: {
|
|
445
|
-
dex: meta.dex,
|
|
446
|
-
quoterAddress: meta.quoter,
|
|
447
|
-
method: meta.method,
|
|
448
|
-
feeTier: meta.fee,
|
|
449
|
-
amountOut: amountOut.toString(),
|
|
450
|
-
gasEstimate: gas ? gas.toString() : undefined,
|
|
451
|
-
sqrtPriceX96After: sqrtAfter ? sqrtAfter.toString() : undefined,
|
|
452
|
-
initializedTicksCrossed: ticks,
|
|
453
|
-
notes: nativeIn || nativeOut
|
|
454
|
-
? `Quoted with wrapped native: in=${nativeIn ? 'wrapped' : 'erc20'}, out=${nativeOut ? 'wrapped' : 'erc20'}`
|
|
455
|
-
: undefined,
|
|
456
|
-
},
|
|
457
|
-
};
|
|
458
|
-
if (!best || candidate.amountOut > (best.amountOut ?? 0n))
|
|
459
|
-
best = candidate;
|
|
460
|
-
}
|
|
461
|
-
catch {
|
|
462
|
-
/* ignore; fallback below */
|
|
463
586
|
}
|
|
464
|
-
}
|
|
587
|
+
};
|
|
588
|
+
handleV2Group(v2ResultsUniLike, v2MetasUniLike, uniswapV3_quoter_abi_1.uniswapV3QuoterAbi);
|
|
589
|
+
handleV2Group(v2ResultsSlip, v2MetasSlip, slipstream_quoter_abi_1.slipstreamQuoterAbi);
|
|
465
590
|
/** ---------------------------------------
|
|
466
|
-
* Round 2: Quoter (
|
|
591
|
+
* Round 2: Legacy Quoter (tuple) fallback
|
|
592
|
+
* - Only for Uni-like deployments (Slipstream has no v1 tuple form)
|
|
467
593
|
* ------------------------------------- */
|
|
468
594
|
if (!best) {
|
|
469
|
-
const
|
|
595
|
+
const orthodoxUniLike = orthodox.filter(d => !this._isSlipstreamDexString(d.dex));
|
|
596
|
+
const v1Contracts = orthodoxUniLike.flatMap(d => {
|
|
470
597
|
const feeTiers = this._determineFeeTiersForDex(d.dex);
|
|
471
598
|
return feeTiers.map(fee => ({
|
|
472
599
|
address: d.quoter,
|
|
@@ -476,22 +603,23 @@ class V3StyleDexService {
|
|
|
476
603
|
{
|
|
477
604
|
tokenIn: (0, viem_1.getAddress)(tokenInForQuote),
|
|
478
605
|
tokenOut: (0, viem_1.getAddress)(tokenOutForQuote),
|
|
479
|
-
amountIn: amountInBI,
|
|
480
606
|
fee,
|
|
607
|
+
amountIn: amountInBI,
|
|
481
608
|
sqrtPriceLimitX96: 0n,
|
|
482
609
|
},
|
|
483
610
|
],
|
|
484
611
|
}));
|
|
485
612
|
});
|
|
486
|
-
const v1Metas =
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
613
|
+
const v1Metas = orthodoxUniLike.flatMap(d => this._determineFeeTiersForDex(d.dex).map(fee => ({
|
|
614
|
+
dex: (0, dex_deployment_key_1.dexDeploymentKey)({
|
|
615
|
+
dex: d.dex,
|
|
616
|
+
network: networkIn,
|
|
617
|
+
router: d.router,
|
|
618
|
+
}),
|
|
619
|
+
fee,
|
|
620
|
+
quoter: d.quoter,
|
|
621
|
+
method: 'Quoter.quoteExactInputSingle',
|
|
622
|
+
})));
|
|
495
623
|
const v1Results = v1Contracts.length
|
|
496
624
|
? await client.multicall({
|
|
497
625
|
allowFailure: true,
|
|
@@ -553,23 +681,21 @@ class V3StyleDexService {
|
|
|
553
681
|
best = candidate;
|
|
554
682
|
}
|
|
555
683
|
catch {
|
|
556
|
-
/* ignore
|
|
684
|
+
/* ignore */
|
|
557
685
|
}
|
|
558
686
|
}
|
|
559
687
|
}
|
|
560
688
|
/** ---------------------------------------
|
|
561
|
-
* Round 3: Off-chain quoter (no quoter
|
|
689
|
+
* Round 3: Off-chain quoter (no on-chain quoter)
|
|
562
690
|
* ------------------------------------- */
|
|
563
691
|
{
|
|
564
|
-
// Only consider quoter-less deployments that ALSO provide a factory address in config/uniV3ExtraDeployments.
|
|
565
|
-
// (If factory is absent, we cannot discover pools; we skip those entries.)
|
|
566
692
|
const offchainCapable = quoterless.filter(d => !!d?.factory);
|
|
567
693
|
if (offchainCapable.length) {
|
|
568
694
|
const offchain = new v3_offchain_quoter_service_1.V3OffchainQuoter();
|
|
569
|
-
// sequential to keep RPC load predictable across many tiers/words;
|
|
570
|
-
// user can parallelize externally if desired
|
|
571
695
|
for (const d of offchainCapable) {
|
|
572
696
|
try {
|
|
697
|
+
// For off-chain we keep fee tiers (Uni-like). Slipstream off-chain
|
|
698
|
+
// could be added later by scanning tickSpacing set; omitted here intentionally.
|
|
573
699
|
const res = await offchain.fetchV3Quote({
|
|
574
700
|
client,
|
|
575
701
|
tokenIn: (0, viem_1.getAddress)(tokenInForQuote),
|
|
@@ -577,16 +703,18 @@ class V3StyleDexService {
|
|
|
577
703
|
amountIn: amountInBI,
|
|
578
704
|
factory: (0, viem_1.getAddress)(d.factory),
|
|
579
705
|
fees: this.uniFeeTiers,
|
|
580
|
-
// optional safety knobs can be wired via UniV3Config in the future:
|
|
581
|
-
// maxTickReads, prefetchWords
|
|
582
706
|
});
|
|
583
707
|
if (res?.best && res.best.ok && res.best.amountOut > 0n) {
|
|
584
|
-
const dexKey =
|
|
708
|
+
const dexKey = (0, dex_deployment_key_1.dexDeploymentKey)({
|
|
709
|
+
network: networkIn,
|
|
710
|
+
router: d.router,
|
|
711
|
+
dex: d.dex,
|
|
712
|
+
});
|
|
585
713
|
const candidate = {
|
|
586
714
|
amountOut: res.best.amountOut,
|
|
587
715
|
payload: {
|
|
588
716
|
dex: dexKey,
|
|
589
|
-
quoterAddress: undefined,
|
|
717
|
+
quoterAddress: undefined,
|
|
590
718
|
method: 'OffchainQuoter.simulateExactInputSingle',
|
|
591
719
|
feeTier: res.best.fee,
|
|
592
720
|
amountOut: res.best.amountOut.toString(),
|
|
@@ -610,7 +738,7 @@ class V3StyleDexService {
|
|
|
610
738
|
}
|
|
611
739
|
}
|
|
612
740
|
if (!best) {
|
|
613
|
-
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, `No
|
|
741
|
+
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, `No V3-style quote could be found`);
|
|
614
742
|
}
|
|
615
743
|
return {
|
|
616
744
|
protocol: this.protocol,
|
|
@@ -655,7 +783,7 @@ class V3StyleDexService {
|
|
|
655
783
|
}
|
|
656
784
|
const tokenInExec = (expectNativeIn ? wrapped : tokenIn);
|
|
657
785
|
const tokenOutExec = (expectNativeOut ? wrapped : tokenOut);
|
|
658
|
-
// Price discovery (now includes
|
|
786
|
+
// Price discovery (now includes Slipstream tickSpacing handling via fetchPrice)
|
|
659
787
|
let v3Price;
|
|
660
788
|
if (!params?.priceResponse) {
|
|
661
789
|
let priceRes;
|
|
@@ -677,18 +805,20 @@ class V3StyleDexService {
|
|
|
677
805
|
}
|
|
678
806
|
v3Price = priceRes.protocolResponse;
|
|
679
807
|
}
|
|
808
|
+
else {
|
|
809
|
+
v3Price = params.priceResponse.protocolResponse;
|
|
810
|
+
}
|
|
680
811
|
if (!v3Price || !v3Price.amountOut || v3Price.amountOut === '0') {
|
|
681
812
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, 'No V3-style quote could be found');
|
|
682
813
|
}
|
|
683
814
|
// Slippage handling (percent)
|
|
684
815
|
const bestAmountOut = BigInt(v3Price.amountOut);
|
|
685
816
|
const slippagePct = typeof slippage === 'number' ? slippage : Number(slippage || 0);
|
|
686
|
-
const amountOutMin =
|
|
687
|
-
|
|
688
|
-
:
|
|
817
|
+
const amountOutMin = (0, min_amount_out_1.calculateMinAmountOut)({
|
|
818
|
+
amountOut: bestAmountOut.toString(),
|
|
819
|
+
slippage: slippagePct,
|
|
820
|
+
});
|
|
689
821
|
// Router resolution:
|
|
690
|
-
// - If quoterAddress is present, match router by that quoter's deployment
|
|
691
|
-
// - Else (off-chain path), pick any deployment with the same dex id that has a router
|
|
692
822
|
const deployments = this.deployments[networkIn] ?? [];
|
|
693
823
|
let chosenRouter;
|
|
694
824
|
if (v3Price.quoterAddress) {
|
|
@@ -701,7 +831,6 @@ class V3StyleDexService {
|
|
|
701
831
|
chosenRouter = maybeRouter;
|
|
702
832
|
}
|
|
703
833
|
if (!chosenRouter) {
|
|
704
|
-
// final fallback: any router on this network (highly unlikely to hit)
|
|
705
834
|
chosenRouter = deployments.find(d => !!d.router)?.router;
|
|
706
835
|
}
|
|
707
836
|
if (!chosenRouter) {
|
|
@@ -714,6 +843,9 @@ class V3StyleDexService {
|
|
|
714
843
|
const isSushiV3 = this._isDex(v3_dex_types_1.UniswapV3StyleDexEnum.SUSHISWAP_V3, v3Price);
|
|
715
844
|
const isPancakeV3 = this._isDex(v3_dex_types_1.UniswapV3StyleDexEnum.PANCAKESWAP_V3, v3Price);
|
|
716
845
|
const isPangolinV3 = this._isDex(v3_dex_types_1.UniswapV3StyleDexEnum.PANGOLIN_V3, v3Price);
|
|
846
|
+
const isPharaoh = this._isDex(v3_dex_types_1.UniswapV3StyleDexEnum.PHARAOH_CLMM, v3Price);
|
|
847
|
+
const isSlipstream = this._isDex(v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM, v3Price) ||
|
|
848
|
+
this._isDex(v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM, v3Price);
|
|
717
849
|
let data;
|
|
718
850
|
let methodForResponse;
|
|
719
851
|
let txValue = expectNativeIn ? amountInBI.toString() : '0';
|
|
@@ -722,51 +854,42 @@ class V3StyleDexService {
|
|
|
722
854
|
* -------------------------
|
|
723
855
|
* Sushi Route Processor 9
|
|
724
856
|
* -------------------------
|
|
725
|
-
*
|
|
726
|
-
* - Native in (expectNativeIn): use `processRouteWithTransferValueInput` (payable) so we can pass msg.value.
|
|
727
|
-
* - ERC20 in: use `processRoute` (no value transfer needed).
|
|
728
|
-
*
|
|
729
|
-
* NOTE: `route` bytes are expected to be produced off-chain (e.g., Sushi Tines pathfinder).
|
|
730
|
-
* Here we set it to `0x` as a sentinel; upstream code should replace with a valid route prior to execution.
|
|
731
|
-
* If your pipeline already has the route bytes available at this stage, replace `routeBytes` accordingly.
|
|
857
|
+
* Uses route bytes (off-chain). We set a sentinel here.
|
|
732
858
|
*/
|
|
733
859
|
const routeBytes = '0x';
|
|
734
860
|
const takeSurplus = false;
|
|
735
861
|
const referralCode = 0;
|
|
736
862
|
if (expectNativeIn) {
|
|
737
|
-
// Payable path: send msg.value and call *WithTransferValueInput*
|
|
738
|
-
// We forward value to the RP9 itself (common pattern); adjust if you have a dedicated treasury.
|
|
739
863
|
data = (0, viem_1.encodeFunctionData)({
|
|
740
864
|
abi: sushiswap_route_processor_9_abi_1.sushiswapRouteProcessor9Abi,
|
|
741
865
|
functionName: 'processRouteWithTransferValueInput',
|
|
742
866
|
args: [
|
|
743
|
-
(0, viem_1.getAddress)(chosenRouter), // transferValueTo
|
|
867
|
+
(0, viem_1.getAddress)(chosenRouter), // transferValueTo (RP9)
|
|
744
868
|
amountInBI, // amountValueTransfer
|
|
745
|
-
(0, viem_1.getAddress)(tokenInExec),
|
|
746
|
-
amountInBI,
|
|
747
|
-
(0, viem_1.getAddress)(tokenOutExec),
|
|
748
|
-
amountOutMin,
|
|
749
|
-
(0, viem_1.getAddress)(receiver || from),
|
|
750
|
-
routeBytes,
|
|
869
|
+
(0, viem_1.getAddress)(tokenInExec),
|
|
870
|
+
amountInBI,
|
|
871
|
+
(0, viem_1.getAddress)(tokenOutExec),
|
|
872
|
+
amountOutMin,
|
|
873
|
+
(0, viem_1.getAddress)(receiver || from),
|
|
874
|
+
routeBytes,
|
|
751
875
|
takeSurplus,
|
|
752
876
|
referralCode,
|
|
753
877
|
],
|
|
754
878
|
});
|
|
755
879
|
methodForResponse = 'RouteProcessor9.processRouteWithTransferValueInput';
|
|
756
|
-
// txValue already
|
|
880
|
+
// txValue already equals amountIn for native-in
|
|
757
881
|
}
|
|
758
882
|
else {
|
|
759
|
-
// ERC-20 in: no msg.value required; standard processRoute
|
|
760
883
|
data = (0, viem_1.encodeFunctionData)({
|
|
761
884
|
abi: sushiswap_route_processor_9_abi_1.sushiswapRouteProcessor9Abi,
|
|
762
885
|
functionName: 'processRoute',
|
|
763
886
|
args: [
|
|
764
|
-
(0, viem_1.getAddress)(tokenInExec),
|
|
765
|
-
amountInBI,
|
|
766
|
-
(0, viem_1.getAddress)(tokenOutExec),
|
|
767
|
-
amountOutMin,
|
|
768
|
-
(0, viem_1.getAddress)(receiver || from),
|
|
769
|
-
routeBytes,
|
|
887
|
+
(0, viem_1.getAddress)(tokenInExec),
|
|
888
|
+
amountInBI,
|
|
889
|
+
(0, viem_1.getAddress)(tokenOutExec),
|
|
890
|
+
amountOutMin,
|
|
891
|
+
(0, viem_1.getAddress)(receiver || from),
|
|
892
|
+
routeBytes,
|
|
770
893
|
takeSurplus,
|
|
771
894
|
referralCode,
|
|
772
895
|
],
|
|
@@ -776,32 +899,60 @@ class V3StyleDexService {
|
|
|
776
899
|
}
|
|
777
900
|
}
|
|
778
901
|
else {
|
|
779
|
-
|
|
780
|
-
const
|
|
781
|
-
?
|
|
902
|
+
// Choose router ABI (Slipstream vs Uni-like)
|
|
903
|
+
const routerAbi = isSlipstream
|
|
904
|
+
? slipstream_router_abi_1.slipstreamRouterAbi
|
|
905
|
+
: isPancakeV3 || isPangolinV3 || isPharaoh
|
|
906
|
+
? pacakeswapV3_router_abi_1.pancakeswapV3RouterAbi
|
|
907
|
+
: uniswapV3_router_abi_1.uniswapV3RouterAbi;
|
|
908
|
+
// Build exactInputSingle calldata
|
|
909
|
+
let exactInputSingleCalldata;
|
|
910
|
+
if (isSlipstream) {
|
|
911
|
+
// Slipstream uses tickSpacing (int24) and includes deadline in the struct
|
|
912
|
+
const struct = {
|
|
782
913
|
tokenIn: (0, viem_1.getAddress)(tokenInExec),
|
|
783
914
|
tokenOut: (0, viem_1.getAddress)(tokenOutExec),
|
|
784
|
-
|
|
915
|
+
tickSpacing: Number(v3Price.feeTier ?? 60), // feeTier carries tickSpacing for Slipstream
|
|
785
916
|
recipient: recipientForFirstLeg,
|
|
786
917
|
deadline,
|
|
787
918
|
amountIn: amountInBI,
|
|
788
919
|
amountOutMinimum: amountOutMin,
|
|
789
920
|
sqrtPriceLimitX96: 0n,
|
|
790
|
-
}
|
|
791
|
-
: {
|
|
792
|
-
tokenIn: (0, viem_1.getAddress)(tokenInExec),
|
|
793
|
-
tokenOut: (0, viem_1.getAddress)(tokenOutExec),
|
|
794
|
-
fee: (v3Price.feeTier ?? 3000),
|
|
795
|
-
recipient: recipientForFirstLeg,
|
|
796
|
-
amountIn: amountInBI,
|
|
797
|
-
amountOutMinimum: amountOutMin,
|
|
798
|
-
sqrtPriceLimitX96: 0n,
|
|
799
921
|
};
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
922
|
+
exactInputSingleCalldata = (0, viem_1.encodeFunctionData)({
|
|
923
|
+
abi: routerAbi,
|
|
924
|
+
functionName: 'exactInputSingle',
|
|
925
|
+
args: [struct],
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
else {
|
|
929
|
+
// Uni/Pancake/Pangolin use fee (uint24)
|
|
930
|
+
const struct = isPancakeV3 || isPangolinV3 || isPharaoh
|
|
931
|
+
? {
|
|
932
|
+
tokenIn: (0, viem_1.getAddress)(tokenInExec),
|
|
933
|
+
tokenOut: (0, viem_1.getAddress)(tokenOutExec),
|
|
934
|
+
fee: (v3Price.feeTier ?? 3000),
|
|
935
|
+
recipient: recipientForFirstLeg,
|
|
936
|
+
deadline,
|
|
937
|
+
amountIn: amountInBI,
|
|
938
|
+
amountOutMinimum: amountOutMin,
|
|
939
|
+
sqrtPriceLimitX96: 0n,
|
|
940
|
+
}
|
|
941
|
+
: {
|
|
942
|
+
tokenIn: (0, viem_1.getAddress)(tokenInExec),
|
|
943
|
+
tokenOut: (0, viem_1.getAddress)(tokenOutExec),
|
|
944
|
+
fee: (v3Price.feeTier ?? 3000),
|
|
945
|
+
recipient: recipientForFirstLeg,
|
|
946
|
+
amountIn: amountInBI,
|
|
947
|
+
amountOutMinimum: amountOutMin,
|
|
948
|
+
sqrtPriceLimitX96: 0n,
|
|
949
|
+
};
|
|
950
|
+
exactInputSingleCalldata = (0, viem_1.encodeFunctionData)({
|
|
951
|
+
abi: routerAbi,
|
|
952
|
+
functionName: 'exactInputSingle',
|
|
953
|
+
args: [struct],
|
|
954
|
+
});
|
|
955
|
+
}
|
|
805
956
|
if (expectNativeOut) {
|
|
806
957
|
const unwrapCalldata = (0, viem_1.encodeFunctionData)({
|
|
807
958
|
abi: routerAbi,
|
|
@@ -821,10 +972,6 @@ class V3StyleDexService {
|
|
|
821
972
|
}
|
|
822
973
|
}
|
|
823
974
|
// ETH value handling:
|
|
824
|
-
// - For Sushi RP9 branch with native in: msg.value == amountIn
|
|
825
|
-
// - For UniV3 branch with native in: msg.value == amountIn
|
|
826
|
-
// - Else: 0
|
|
827
|
-
// (txValue already assigned above)
|
|
828
975
|
const approval = expectNativeIn
|
|
829
976
|
? undefined
|
|
830
977
|
: {
|
|
@@ -847,6 +994,7 @@ class V3StyleDexService {
|
|
|
847
994
|
if (!deployment) {
|
|
848
995
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `No deployment found for dex ${dexEnum} on network ${networkIn}.`);
|
|
849
996
|
}
|
|
997
|
+
// CREATE2 pair computation:
|
|
850
998
|
const pairAddress = this._create2Address({
|
|
851
999
|
dex: dexEnum,
|
|
852
1000
|
factory: deployment?.factory,
|
|
@@ -854,14 +1002,14 @@ class V3StyleDexService {
|
|
|
854
1002
|
initHash: deployment?.initHash,
|
|
855
1003
|
tokenA: tokenInExec,
|
|
856
1004
|
tokenB: tokenOutExec,
|
|
857
|
-
fee: v3Price.feeTier,
|
|
1005
|
+
fee: v3Price.feeTier, // NOTE: for Slipstream this is tickSpacing (int24)
|
|
858
1006
|
});
|
|
859
1007
|
const protocolResponse = {
|
|
860
1008
|
dex: v3Price.dex,
|
|
861
1009
|
pair: (0, viem_1.getAddress)(pairAddress),
|
|
862
1010
|
routerAddress: chosenRouter,
|
|
863
|
-
quoterAddress: v3Price.quoterAddress,
|
|
864
|
-
feeTier: v3Price.feeTier,
|
|
1011
|
+
quoterAddress: v3Price.quoterAddress,
|
|
1012
|
+
feeTier: v3Price.feeTier, // tickSpacing for Slipstream
|
|
865
1013
|
method: methodForResponse,
|
|
866
1014
|
amountOut: bestAmountOut.toString(),
|
|
867
1015
|
amountOutMin: amountOutMin.toString(),
|
|
@@ -871,13 +1019,14 @@ class V3StyleDexService {
|
|
|
871
1019
|
sqrtPriceX96After: v3Price.sqrtPriceX96After,
|
|
872
1020
|
initializedTicksCrossed: v3Price.initializedTicksCrossed,
|
|
873
1021
|
notes: (v3Price.method?.startsWith('OffchainQuoter') ? 'Off-chain pool simulation; ' : '') +
|
|
1022
|
+
(isSlipstream ? 'Slipstream tickSpacing; ' : '') +
|
|
874
1023
|
(isSushiV3
|
|
875
1024
|
? 'Sushi Route Processor 9 calldata (route bytes expected from off-chain pathfinder); '
|
|
876
1025
|
: '') +
|
|
877
1026
|
(expectNativeIn ? 'Native in; ' : '') +
|
|
878
1027
|
(expectNativeOut
|
|
879
1028
|
? isSushiV3
|
|
880
|
-
? 'Native out
|
|
1029
|
+
? 'Native out handled inside route bytes.'
|
|
881
1030
|
: 'Native out via multicall(unwrapWETH9).'
|
|
882
1031
|
: 'ERC-20 out to recipient.'),
|
|
883
1032
|
};
|
|
@@ -897,14 +1046,6 @@ class V3StyleDexService {
|
|
|
897
1046
|
protocolResponse,
|
|
898
1047
|
};
|
|
899
1048
|
}
|
|
900
|
-
_buildDexKey(network, router) {
|
|
901
|
-
// Find the deployment from `deployments` where the router address is equal to the router param
|
|
902
|
-
const deployment = this.deployments[network]?.find(d => d.router.toLowerCase() === router.toLowerCase());
|
|
903
|
-
if (!deployment) {
|
|
904
|
-
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `No deployment found for router ${router} on network ${network}.`);
|
|
905
|
-
}
|
|
906
|
-
return `${deployment.dex}:${router}:${network}`;
|
|
907
|
-
}
|
|
908
1049
|
_convertKeyToDexEnum(dexKey) {
|
|
909
1050
|
const parts = dexKey.split(':');
|
|
910
1051
|
if (parts.length !== 3) {
|
|
@@ -917,6 +1058,9 @@ class V3StyleDexService {
|
|
|
917
1058
|
v3_dex_types_1.UniswapV3StyleDexEnum.PANCAKESWAP_V3,
|
|
918
1059
|
v3_dex_types_1.UniswapV3StyleDexEnum.PANGOLIN_V3,
|
|
919
1060
|
v3_dex_types_1.UniswapV3StyleDexEnum.HYPERSWAP_V3,
|
|
1061
|
+
v3_dex_types_1.UniswapV3StyleDexEnum.PHARAOH_CLMM,
|
|
1062
|
+
v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM,
|
|
1063
|
+
v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM,
|
|
920
1064
|
].includes(dexEnum)) {
|
|
921
1065
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `Unknown dex enum in key: ${dexKey}`);
|
|
922
1066
|
}
|
|
@@ -931,7 +1075,13 @@ class V3StyleDexService {
|
|
|
931
1075
|
? v3_dex_types_1.UniswapV3StyleDexEnum.SUSHISWAP_V3
|
|
932
1076
|
: dexString === v3_dex_types_1.UniswapV3StyleDexEnum.HYPERSWAP_V3
|
|
933
1077
|
? v3_dex_types_1.UniswapV3StyleDexEnum.HYPERSWAP_V3
|
|
934
|
-
: v3_dex_types_1.UniswapV3StyleDexEnum.
|
|
1078
|
+
: dexString === v3_dex_types_1.UniswapV3StyleDexEnum.PHARAOH_CLMM
|
|
1079
|
+
? v3_dex_types_1.UniswapV3StyleDexEnum.PHARAOH_CLMM
|
|
1080
|
+
: dexString === v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM
|
|
1081
|
+
? v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM
|
|
1082
|
+
: dexString === v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM
|
|
1083
|
+
? v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM
|
|
1084
|
+
: v3_dex_types_1.UniswapV3StyleDexEnum.UNISWAP_V3;
|
|
935
1085
|
switch (dex) {
|
|
936
1086
|
case v3_dex_types_1.UniswapV3StyleDexEnum.PANCAKESWAP_V3:
|
|
937
1087
|
return this.pancakeFeeTiers;
|
|
@@ -941,6 +1091,12 @@ class V3StyleDexService {
|
|
|
941
1091
|
return this.uniFeeTiers;
|
|
942
1092
|
case v3_dex_types_1.UniswapV3StyleDexEnum.HYPERSWAP_V3:
|
|
943
1093
|
return this.hyperswapFeeTiers;
|
|
1094
|
+
case v3_dex_types_1.UniswapV3StyleDexEnum.PHARAOH_CLMM:
|
|
1095
|
+
return this.pharaohFeeTiers;
|
|
1096
|
+
case v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM:
|
|
1097
|
+
return this.aerodromeFeeTiers;
|
|
1098
|
+
case v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM:
|
|
1099
|
+
return this.velodromeFeeTiers;
|
|
944
1100
|
case v3_dex_types_1.UniswapV3StyleDexEnum.UNISWAP_V3:
|
|
945
1101
|
default:
|
|
946
1102
|
return this.uniFeeTiers;
|
|
@@ -951,23 +1107,25 @@ class V3StyleDexService {
|
|
|
951
1107
|
}
|
|
952
1108
|
_create2Address(params) {
|
|
953
1109
|
const { dex, initHash, tokenA, tokenB, fee, deployer, factory } = params;
|
|
1110
|
+
const isSlip = dex === v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM || dex === v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM;
|
|
1111
|
+
// For Pancake V3 you already special-case deployer vs factory; Slipstream uses factory as deployer.
|
|
954
1112
|
const addressToUse = dex === v3_dex_types_1.UniswapV3StyleDexEnum.PANCAKESWAP_V3 ? deployer : factory;
|
|
955
|
-
// Uniswap/Pancake V3 salt uses token0 < token1 ordering
|
|
956
1113
|
const a = (0, viem_1.getAddress)(tokenA);
|
|
957
1114
|
const b = (0, viem_1.getAddress)(tokenB);
|
|
958
|
-
const [token0, token1] =
|
|
959
|
-
//
|
|
960
|
-
|
|
1115
|
+
const [token0, token1] = (0, sort_addresses_1.sortAddresses)(a, b);
|
|
1116
|
+
// SLIPSTREAM: salt = keccak256(abi.encode(address,address,int24 tickSpacing))
|
|
1117
|
+
// UNI-LIKE: salt = keccak256(abi.encode(address,address,uint24 fee))
|
|
1118
|
+
const encoded = isSlip
|
|
1119
|
+
? (0, viem_1.encodeAbiParameters)([{ type: 'address' }, { type: 'address' }, { type: 'int24' }], [token0, token1, fee])
|
|
1120
|
+
: (0, viem_1.encodeAbiParameters)([{ type: 'address' }, { type: 'address' }, { type: 'uint24' }], [token0, token1, fee]);
|
|
961
1121
|
const salt = (0, ethers_1.keccak256)(encoded);
|
|
962
|
-
// CREATE2 addr = last 20 bytes of keccak256(0xff ++ addressToUse ++ salt ++ initCodeHash)
|
|
963
1122
|
const digest = (0, ethers_1.keccak256)((0, viem_1.concat)([
|
|
964
1123
|
(0, viem_1.hexToBytes)('0xff'),
|
|
965
1124
|
(0, viem_1.hexToBytes)(addressToUse),
|
|
966
1125
|
(0, viem_1.hexToBytes)(salt),
|
|
967
1126
|
(0, viem_1.hexToBytes)(initHash),
|
|
968
1127
|
]));
|
|
969
|
-
|
|
970
|
-
return (0, viem_1.getAddress)(`0x${digest.slice(26)}`); // remove '0x' + 24 hex (12 bytes)
|
|
1128
|
+
return (0, viem_1.getAddress)(`0x${digest.slice(26)}`);
|
|
971
1129
|
}
|
|
972
1130
|
async _validateNativeLiquidity(params) {
|
|
973
1131
|
const { tokenIn, networkIn, dexes, nativePrice, tokenInPrice, tokenInDecimals } = params;
|
|
@@ -1193,7 +1351,11 @@ class V3StyleDexService {
|
|
|
1193
1351
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.INVALID_PARAMS, `Supplied pair/token direction mismatch. Pair ${pair} tokens are ${token0}/${token1}; received ${tokenInForQuote}/${tokenOutForQuote}`);
|
|
1194
1352
|
}
|
|
1195
1353
|
// Build dex key for response routing
|
|
1196
|
-
const dexKey =
|
|
1354
|
+
const dexKey = (0, dex_deployment_key_1.dexDeploymentKey)({
|
|
1355
|
+
dex: deployment.dex,
|
|
1356
|
+
router: deployment.router,
|
|
1357
|
+
network,
|
|
1358
|
+
});
|
|
1197
1359
|
// 2) Try on-chain quoter (struct signature first, then legacy)
|
|
1198
1360
|
if (deployment?.quoter) {
|
|
1199
1361
|
// V2 (struct)
|
|
@@ -1296,6 +1458,20 @@ class V3StyleDexService {
|
|
|
1296
1458
|
}
|
|
1297
1459
|
throw (0, utils_1.sdkError)(enums_1.SdkErrorEnum.QUOTE_NOT_FOUND, `Unable to quote via known pair ${pair}`);
|
|
1298
1460
|
}
|
|
1461
|
+
_isSlipstreamDexString(dexString) {
|
|
1462
|
+
return (dexString === v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM ||
|
|
1463
|
+
dexString === v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM);
|
|
1464
|
+
}
|
|
1465
|
+
_determineTickSpacingsForDex(dexString) {
|
|
1466
|
+
switch (dexString) {
|
|
1467
|
+
case v3_dex_types_1.UniswapV3StyleDexEnum.AERODROME_CLMM:
|
|
1468
|
+
return this.aerodromeTickSpacings;
|
|
1469
|
+
case v3_dex_types_1.UniswapV3StyleDexEnum.VELODROME_CLMM:
|
|
1470
|
+
return this.velodromeTickSpacings;
|
|
1471
|
+
default:
|
|
1472
|
+
return [];
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1299
1475
|
}
|
|
1300
1476
|
exports.V3StyleDexService = V3StyleDexService;
|
|
1301
1477
|
//# sourceMappingURL=v3-dex.service.js.map
|