@guru-fund/sdk 0.2.10 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addresses.d.ts +1 -0
- package/dist/addresses.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/quotes/quoteTrade.d.ts +2 -0
- package/dist/quotes/quoteTrade.js +71 -9
- package/dist/router/getFallbackRoutes.js +54 -17
- package/dist/router/poolHelper.d.ts +4 -3
- package/dist/router/poolHelper.js +9 -3
- package/dist/router/quoteWethTrade.d.ts +2 -0
- package/dist/router/quoteWethTrade.js +6 -4
- package/dist/router/simulation.js +8 -4
- package/dist/router/v4PoolDiscovery.d.ts +1 -0
- package/dist/router/v4PoolDiscovery.js +39 -2
- package/dist/txBuilders/buildTradesTx.d.ts +9 -0
- package/dist/txBuilders/buildTradesTx.js +9 -0
- package/dist/txBuilders/index.d.ts +2 -0
- package/dist/txBuilders/index.js +1 -0
- package/package.json +1 -1
package/dist/addresses.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type GuruProtocolAddresses = {
|
|
|
22
22
|
readonly factories: Readonly<Partial<Record<FactoryKey, string>>>;
|
|
23
23
|
readonly routers: Readonly<Partial<Record<RouterKey, string>>>;
|
|
24
24
|
readonly quoters: Readonly<Partial<Record<QuoterKey, string>>>;
|
|
25
|
+
readonly routeBridges?: readonly string[];
|
|
25
26
|
readonly tokens: Readonly<{
|
|
26
27
|
WETH: string;
|
|
27
28
|
USDC: string;
|
package/dist/addresses.js
CHANGED
|
@@ -121,6 +121,7 @@ const ROBINHOOD = {
|
|
|
121
121
|
uniswapV3: '0x33e885ed0ec9bf04ecfb19341582aadcb4c8a9e7',
|
|
122
122
|
uniswapV4: '0x8dc178efb8111bb0973dd9d722ebeff267c98f94',
|
|
123
123
|
},
|
|
124
|
+
routeBridges: ['0xc6911796042b15d7fa4f6cde69e245ddcd3d9c31'],
|
|
124
125
|
tokens: {
|
|
125
126
|
WETH: '0x0bd7d308f8e1639fab988df18a8011f41eacad73',
|
|
126
127
|
USDC: '0x5fc5360d0400a0fd4f2af552add042d716f1d168',
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export type { GuruProtocolAddresses, GuruProtocolChainId } from './addresses';
|
|
|
4
4
|
export { GuruProtocol } from './GuruProtocol';
|
|
5
5
|
export type { GuruProtocolOptions } from './GuruProtocol';
|
|
6
6
|
export type { BuildDepositTxParams, BuildCloseTxParams, BuildHarvestTxParams, BuildTradeTxParams, BuildWithdrawTxParams, } from './txBuilders';
|
|
7
|
+
export type { BuildTradesTxParams } from './txBuilders/buildTradesTx';
|
|
7
8
|
export type { QuoteDepositParams, QuoteDepositResult, QuoteDepositLogs, } from './quotes/quoteDeposit';
|
|
8
9
|
export type { QuoteWithdrawalParams, QuoteWithdrawalResult, QuoteWithdrawalLogs, } from './quotes/quoteWithdrawal';
|
|
9
10
|
export type { QuoteTradeParams, QuoteTradeResult } from './quotes/quoteTrade';
|
|
@@ -13,6 +13,8 @@ export interface QuoteTradeContext extends RouterContext {
|
|
|
13
13
|
chainId: GuruProtocolChainId;
|
|
14
14
|
}
|
|
15
15
|
export interface QuoteTradeResult extends Route {
|
|
16
|
+
routes: Route[];
|
|
16
17
|
txData: TransactionRequest;
|
|
17
18
|
}
|
|
19
|
+
export declare function bestOfDirectAndComposite(direct: () => Promise<Route>, composite: () => Promise<Route[]>): Promise<Route[]>;
|
|
18
20
|
export default function quoteTrade(params: QuoteTradeParams, ctx: QuoteTradeContext): Promise<QuoteTradeResult>;
|
|
@@ -1,9 +1,53 @@
|
|
|
1
1
|
import { getGuruProtocolAddresses, } from '../addresses.js';
|
|
2
2
|
import compareAddresses from '../helpers/compareAddresses.js';
|
|
3
3
|
import { getRouteIn, getRouteOut } from '../router/index.js';
|
|
4
|
+
import { encodeVaultExecute } from '../router/simulation.js';
|
|
4
5
|
import { quoteTradeSchema } from '../schemas/quoteTrade.js';
|
|
5
6
|
import buildTradeTx from '../txBuilders/buildTradeTx.js';
|
|
7
|
+
import buildTradesTx from '../txBuilders/buildTradesTx.js';
|
|
6
8
|
import { FundLedger__factory, Protocol__factory } from '../typechain/index.js';
|
|
9
|
+
async function buildCompositeWethRoute(routeParams, weth, controllerAddress, vaultAddress, ctx) {
|
|
10
|
+
const first = await getRouteIn({ ...routeParams, tokenOut: weth }, ctx);
|
|
11
|
+
const prefixTxs = [
|
|
12
|
+
{
|
|
13
|
+
from: controllerAddress,
|
|
14
|
+
to: vaultAddress,
|
|
15
|
+
callData: encodeVaultExecute(String(first.adapter), String(first.callData)),
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
const second = await getRouteIn({
|
|
19
|
+
...routeParams,
|
|
20
|
+
tokenIn: weth,
|
|
21
|
+
amountIn: BigInt(first.data.amountToReceive),
|
|
22
|
+
prefixTxs,
|
|
23
|
+
}, ctx);
|
|
24
|
+
return [first, second];
|
|
25
|
+
}
|
|
26
|
+
export async function bestOfDirectAndComposite(direct, composite) {
|
|
27
|
+
const [directResult, compositeResult] = await Promise.allSettled([
|
|
28
|
+
direct(),
|
|
29
|
+
composite(),
|
|
30
|
+
]);
|
|
31
|
+
if (directResult.status === 'rejected' &&
|
|
32
|
+
directResult.reason instanceof Error &&
|
|
33
|
+
(directResult.reason.message.includes('TOKEN_LOCKED_BY_V4_HOOK') ||
|
|
34
|
+
directResult.reason.message.includes('UNISWAP_V4_HOOK_ROUTE_REVERTED'))) {
|
|
35
|
+
throw directResult.reason;
|
|
36
|
+
}
|
|
37
|
+
if (directResult.status === 'fulfilled' &&
|
|
38
|
+
compositeResult.status === 'fulfilled') {
|
|
39
|
+
const compositeRoute = compositeResult.value.at(-1);
|
|
40
|
+
return BigInt(directResult.value.data.amountToReceive) >=
|
|
41
|
+
BigInt(compositeRoute.data.amountToReceive)
|
|
42
|
+
? [directResult.value]
|
|
43
|
+
: compositeResult.value;
|
|
44
|
+
}
|
|
45
|
+
if (directResult.status === 'fulfilled')
|
|
46
|
+
return [directResult.value];
|
|
47
|
+
if (compositeResult.status === 'fulfilled')
|
|
48
|
+
return compositeResult.value;
|
|
49
|
+
throw directResult.reason;
|
|
50
|
+
}
|
|
7
51
|
export default async function quoteTrade(params, ctx) {
|
|
8
52
|
const parsed = quoteTradeSchema.parse(params);
|
|
9
53
|
const provider = ctx.provider;
|
|
@@ -27,25 +71,43 @@ export default async function quoteTrade(params, ctx) {
|
|
|
27
71
|
ledger.controller(),
|
|
28
72
|
ledger.manager(),
|
|
29
73
|
]);
|
|
30
|
-
const
|
|
74
|
+
const slippageE2 = parsed.maxSlippage != null
|
|
75
|
+
? Number(parsed.maxSlippage / 10n)
|
|
76
|
+
: undefined;
|
|
77
|
+
const routeParams = {
|
|
31
78
|
chainId: ctx.chainId,
|
|
32
79
|
tokenIn: parsed.tokenIn,
|
|
33
80
|
tokenOut: parsed.tokenOut,
|
|
34
81
|
amountIn: parsed.amountIn,
|
|
35
|
-
slippageE2
|
|
36
|
-
? Number(parsed.maxSlippage / 10n)
|
|
37
|
-
: undefined,
|
|
82
|
+
slippageE2,
|
|
38
83
|
account: vaultAddress,
|
|
39
84
|
vault: vaultAddress,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
85
|
+
};
|
|
86
|
+
const weth = addresses.tokens.WETH;
|
|
87
|
+
const routes = compareAddresses(parsed.tokenIn, weth) ||
|
|
88
|
+
compareAddresses(parsed.tokenOut, weth)
|
|
89
|
+
? [await getRoute(routeParams, ctx)]
|
|
90
|
+
: await bestOfDirectAndComposite(() => getRoute(routeParams, ctx), () => buildCompositeWethRoute(routeParams, weth, controllerAddress, vaultAddress, ctx));
|
|
91
|
+
const route = routes.at(-1);
|
|
92
|
+
const txData = routes.length === 1
|
|
93
|
+
? buildTradeTx({
|
|
44
94
|
controller: controllerAddress,
|
|
45
95
|
ledger: parsed.ledger,
|
|
46
96
|
adapter: String(route.adapter),
|
|
47
97
|
callData: String(route.callData),
|
|
48
98
|
from: manager,
|
|
49
|
-
})
|
|
99
|
+
})
|
|
100
|
+
: buildTradesTx({
|
|
101
|
+
controller: controllerAddress,
|
|
102
|
+
ledger: parsed.ledger,
|
|
103
|
+
adapters: routes.map((item) => String(item.adapter)),
|
|
104
|
+
callData: routes.map((item) => String(item.callData)),
|
|
105
|
+
from: manager,
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
...route,
|
|
109
|
+
routes,
|
|
110
|
+
hops: routes.reduce((sum, item) => sum + item.hops, 0),
|
|
111
|
+
txData,
|
|
50
112
|
};
|
|
51
113
|
}
|
|
@@ -2,6 +2,28 @@ import compareAddresses from '../helpers/compareAddresses.js';
|
|
|
2
2
|
import { FundVault__factory } from '../typechain/index.js';
|
|
3
3
|
import PoolHelper from './poolHelper.js';
|
|
4
4
|
import { quoteWethTrade } from './quoteWethTrade.js';
|
|
5
|
+
async function tryWethBridgeV2Route(poolHelper, input, finalization) {
|
|
6
|
+
const factory = poolHelper.addresses.factories.uniswapV2;
|
|
7
|
+
if (!factory || !poolHelper.addresses.adapters.uniswapV2)
|
|
8
|
+
return null;
|
|
9
|
+
for (const bridge of poolHelper.addresses.routeBridges ?? []) {
|
|
10
|
+
try {
|
|
11
|
+
return await quoteWethTrade({
|
|
12
|
+
feeTier: 0,
|
|
13
|
+
input: {
|
|
14
|
+
...input,
|
|
15
|
+
path: [input.tokenIn, bridge, input.tokenOut],
|
|
16
|
+
exchangeFactory: factory,
|
|
17
|
+
},
|
|
18
|
+
poolHelper,
|
|
19
|
+
finalization,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
5
27
|
async function tryDirectAerodromeV2RouteIn(poolHelper, { tokenIn, tokenOut, amountIn, slippage, finalization, }) {
|
|
6
28
|
const factory = poolHelper.addresses.factories.aerodromeV2;
|
|
7
29
|
if (!factory || !poolHelper.addresses.adapters.aerodromeV2)
|
|
@@ -61,14 +83,22 @@ export async function getFallbackRouteIn(params, ctx) {
|
|
|
61
83
|
maxSlippageE3: slippage,
|
|
62
84
|
};
|
|
63
85
|
if (compareAddresses(tokenIn, weth) || compareAddresses(tokenOut, weth)) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
try {
|
|
87
|
+
const pairedToken = compareAddresses(tokenIn, weth) ? tokenOut : tokenIn;
|
|
88
|
+
const coinTokenPool = await poolHelper.getUniswapCompatibleTokenPool(pairedToken);
|
|
89
|
+
return await quoteWethTrade({
|
|
90
|
+
feeTier: coinTokenPool.feeTier,
|
|
91
|
+
input: { tokenIn, tokenOut, amountIn, slippage },
|
|
92
|
+
poolHelper,
|
|
93
|
+
finalization,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (directError) {
|
|
97
|
+
const bridged = await tryWethBridgeV2Route(poolHelper, { tokenIn, tokenOut, amountIn, slippage }, finalization);
|
|
98
|
+
if (bridged)
|
|
99
|
+
return bridged;
|
|
100
|
+
throw directError;
|
|
101
|
+
}
|
|
72
102
|
}
|
|
73
103
|
const directAerodrome = await tryDirectAerodromeV2RouteIn(poolHelper, {
|
|
74
104
|
tokenIn,
|
|
@@ -113,15 +143,22 @@ export async function getFallbackRouteOut(params, ctx) {
|
|
|
113
143
|
maxSlippageE3: slippage,
|
|
114
144
|
};
|
|
115
145
|
if (compareAddresses(tokenIn, weth) || compareAddresses(tokenOut, weth)) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
146
|
+
try {
|
|
147
|
+
const pairedToken = compareAddresses(tokenIn, weth) ? tokenOut : tokenIn;
|
|
148
|
+
const coinTokenPool = await poolHelper.getUniswapCompatibleTokenPool(pairedToken);
|
|
149
|
+
return await quoteWethTrade({
|
|
150
|
+
feeTier: coinTokenPool.feeTier,
|
|
151
|
+
input: { tokenIn, tokenOut, amountIn, slippage },
|
|
152
|
+
poolHelper,
|
|
153
|
+
finalization,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
catch (directError) {
|
|
157
|
+
const bridged = await tryWethBridgeV2Route(poolHelper, { tokenIn, tokenOut, amountIn, slippage }, finalization);
|
|
158
|
+
if (bridged)
|
|
159
|
+
return bridged;
|
|
160
|
+
throw directError;
|
|
161
|
+
}
|
|
125
162
|
}
|
|
126
163
|
const directAerodrome = await tryDirectAerodromeV2RouteOut(poolHelper, {
|
|
127
164
|
tokenIn,
|
|
@@ -52,13 +52,14 @@ export type QuoteRequest = {
|
|
|
52
52
|
swapType?: SwapType;
|
|
53
53
|
tokenAmount: bigint;
|
|
54
54
|
slippage?: bigint;
|
|
55
|
-
path: [
|
|
55
|
+
path: string[];
|
|
56
|
+
exchangeFactory?: string;
|
|
56
57
|
requireSwappable?: boolean;
|
|
57
58
|
};
|
|
58
59
|
type VersionSpecificQuoteRequest = Omit<QuoteRequest, 'path' | 'slippage'> & {
|
|
59
60
|
exchangeFactory: string;
|
|
60
61
|
feeTier: FeeTier;
|
|
61
|
-
path: [
|
|
62
|
+
path: string[];
|
|
62
63
|
slippage: bigint;
|
|
63
64
|
poolAddress?: string;
|
|
64
65
|
};
|
|
@@ -115,7 +116,7 @@ export default class PoolHelper {
|
|
|
115
116
|
exchangeFactory?: string;
|
|
116
117
|
swappableOnly?: boolean;
|
|
117
118
|
}): Promise<Pool>;
|
|
118
|
-
getBestQuote({ swapType, tokenAmount, slippage, path, requireSwappable, }: QuoteRequest): Promise<Quote>;
|
|
119
|
+
getBestQuote({ swapType, tokenAmount, slippage, path, requireSwappable, exchangeFactory, }: QuoteRequest): Promise<Quote>;
|
|
119
120
|
getV2Quote({ swapType, tokenAmount, exchangeFactory, path, feeTier, slippage, }: VersionSpecificQuoteRequest): Promise<Quote>;
|
|
120
121
|
getV3Quote({ swapType, tokenAmount, exchangeFactory, path, feeTier, slippage, poolAddress, }: VersionSpecificQuoteRequest): Promise<Quote>;
|
|
121
122
|
getStableForTokenQuote({ path, inputAmount, slippage, exchangeFactory, finalization, }: StableQuoteRequest): Promise<StableQuoteResult>;
|
|
@@ -327,7 +327,7 @@ export default class PoolHelper {
|
|
|
327
327
|
}
|
|
328
328
|
return pools[0];
|
|
329
329
|
}
|
|
330
|
-
async getBestQuote({ swapType = SwapType.EXACT_INPUT, tokenAmount, slippage = 500n, path, requireSwappable = true, }) {
|
|
330
|
+
async getBestQuote({ swapType = SwapType.EXACT_INPUT, tokenAmount, slippage = 500n, path, requireSwappable = true, exchangeFactory, }) {
|
|
331
331
|
const weth = this.addresses.tokens.WETH;
|
|
332
332
|
if (!path.some((t) => compareAddresses(t, weth))) {
|
|
333
333
|
throw new Error(`${PoolHelperError.NO_ROUTE_FOUND}: WETH not in path`);
|
|
@@ -335,6 +335,7 @@ export default class PoolHelper {
|
|
|
335
335
|
const token = path.find((t) => !compareAddresses(t, weth));
|
|
336
336
|
const pools = await this.getUniswapCompatibleTokenPools(token, {
|
|
337
337
|
swappableOnly: requireSwappable,
|
|
338
|
+
exchangeFactory,
|
|
338
339
|
});
|
|
339
340
|
if (pools.length === 0) {
|
|
340
341
|
throw new Error(`${PoolHelperError.NO_ROUTE_FOUND}: token=${token}`);
|
|
@@ -406,6 +407,9 @@ export default class PoolHelper {
|
|
|
406
407
|
};
|
|
407
408
|
}
|
|
408
409
|
async getV3Quote({ swapType = SwapType.EXACT_INPUT, tokenAmount, exchangeFactory, path, feeTier, slippage, poolAddress, }) {
|
|
410
|
+
if (path.length !== 2) {
|
|
411
|
+
throw new Error(`${PoolHelperError.EXCHANGE_INCOMPATIBLE}: V3 multi-hop requires per-hop fee tiers`);
|
|
412
|
+
}
|
|
409
413
|
const [$INPUT, $OUTPUT] = path;
|
|
410
414
|
const dex = this.getDexData(exchangeFactory);
|
|
411
415
|
if (dex.type !== 'v3') {
|
|
@@ -811,7 +815,8 @@ export default class PoolHelper {
|
|
|
811
815
|
}
|
|
812
816
|
_adjustTokenAmountForSwapFee(tokenAmount, swapFeePercentage, swapType, path) {
|
|
813
817
|
const weth = this.addresses.tokens.WETH;
|
|
814
|
-
const
|
|
818
|
+
const tokenEndpoint = swapType === SwapType.EXACT_INPUT ? path[0] : path.at(-1);
|
|
819
|
+
const shouldAdjust = compareAddresses(tokenEndpoint, weth);
|
|
815
820
|
if (!shouldAdjust) {
|
|
816
821
|
return { adjustedTokenAmount: tokenAmount, swapFee: 0n };
|
|
817
822
|
}
|
|
@@ -826,7 +831,8 @@ export default class PoolHelper {
|
|
|
826
831
|
const sign = swapType === SwapType.EXACT_INPUT ? -1n : 1n;
|
|
827
832
|
const slippageTolerance = calcSlippageTolerance(quote, slippage) * sign;
|
|
828
833
|
const quoteWithSlippage = quote + slippageTolerance;
|
|
829
|
-
const
|
|
834
|
+
const quoteEndpoint = swapType === SwapType.EXACT_INPUT ? path.at(-1) : path[0];
|
|
835
|
+
const shouldAdjustQuote = compareAddresses(quoteEndpoint, weth);
|
|
830
836
|
if (!shouldAdjustQuote) {
|
|
831
837
|
return { adjustedQuote: quoteWithSlippage, swapFee: 0n };
|
|
832
838
|
}
|
|
@@ -4,6 +4,7 @@ import { AerodromeV2Adapter__factory, UniswapV2Adapter__factory, UniswapV3Adapte
|
|
|
4
4
|
import { finalizeRouteQuote } from './finalizeRoute.js';
|
|
5
5
|
export async function quoteWethTrade({ feeTier, input, poolHelper, finalization, }) {
|
|
6
6
|
const weth = poolHelper.addresses.tokens.WETH;
|
|
7
|
+
const routePath = input.path ?? [input.tokenIn, input.tokenOut];
|
|
7
8
|
const wethIn = compareAddresses(input.tokenIn, weth);
|
|
8
9
|
const wethOut = compareAddresses(input.tokenOut, weth);
|
|
9
10
|
if (!wethIn && !wethOut) {
|
|
@@ -11,8 +12,9 @@ export async function quoteWethTrade({ feeTier, input, poolHelper, finalization,
|
|
|
11
12
|
}
|
|
12
13
|
const quote = await poolHelper.getBestQuote({
|
|
13
14
|
tokenAmount: input.amountIn,
|
|
14
|
-
path:
|
|
15
|
+
path: routePath,
|
|
15
16
|
slippage: finalization ? 0n : input.slippage,
|
|
17
|
+
exchangeFactory: input.exchangeFactory,
|
|
16
18
|
});
|
|
17
19
|
const adapter = poolHelper.getLotusAdapter(quote.exchangeFactory);
|
|
18
20
|
const dex = poolHelper.getDexData(quote.exchangeFactory);
|
|
@@ -46,7 +48,7 @@ export async function quoteWethTrade({ feeTier, input, poolHelper, finalization,
|
|
|
46
48
|
const data = {
|
|
47
49
|
amountToSend: input.amountIn,
|
|
48
50
|
amountToReceive,
|
|
49
|
-
path:
|
|
51
|
+
path: routePath,
|
|
50
52
|
deadline,
|
|
51
53
|
};
|
|
52
54
|
return {
|
|
@@ -86,7 +88,7 @@ export async function quoteWethTrade({ feeTier, input, poolHelper, finalization,
|
|
|
86
88
|
adapter,
|
|
87
89
|
account: finalization.account,
|
|
88
90
|
path: dex.type === 'v2'
|
|
89
|
-
?
|
|
91
|
+
? routePath
|
|
90
92
|
: [
|
|
91
93
|
{
|
|
92
94
|
tokenIn: input.tokenIn,
|
|
@@ -119,7 +121,7 @@ export async function quoteWethTrade({ feeTier, input, poolHelper, finalization,
|
|
|
119
121
|
currency: weth,
|
|
120
122
|
amount: finalized.finalTollAmount,
|
|
121
123
|
},
|
|
122
|
-
hops: 1,
|
|
124
|
+
hops: routePath.length - 1,
|
|
123
125
|
effectiveSlippageBps: finalized.effectiveSlippageBps,
|
|
124
126
|
};
|
|
125
127
|
}
|
|
@@ -40,10 +40,14 @@ export async function findMaxPassingAmountToReceive({ chainId, blockNumber, cont
|
|
|
40
40
|
return success ? high : amountWithSlippage;
|
|
41
41
|
}
|
|
42
42
|
const candidates = generateCandidates(high, low, BUNDLE_SIMULATION_CANDIDATES);
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
.
|
|
43
|
+
const successIndices = [];
|
|
44
|
+
for (const [index, amount] of candidates.entries()) {
|
|
45
|
+
const result = await simulate(encodeVaultExecute(adapter, buildCallDataForAmount(amount)));
|
|
46
|
+
if (result.success)
|
|
47
|
+
successIndices.push(index);
|
|
48
|
+
if (successIndices.length === 2)
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
47
51
|
if (successIndices.length === 0) {
|
|
48
52
|
console.warn('Found no successful simulations for executable route quote', {
|
|
49
53
|
chainId,
|
|
@@ -28,5 +28,6 @@ interface DexscreenerPair {
|
|
|
28
28
|
export declare function selectV4PairPoolIds(pairs: DexscreenerPair[], tokenIn: string, tokenOut: string): string[];
|
|
29
29
|
export declare function stitchNativeBridgeV4Paths(firstLegs: V4Path[], secondLegs: V4Path[]): V4Path[];
|
|
30
30
|
export declare function discoverV4Paths(chainId: GuruProtocolChainId, tokenIn: string, tokenOut: string, provider: Provider, endpoint?: string): Promise<V4Path[]>;
|
|
31
|
+
export declare function findKnownV4BridgeKeys(chainId: GuruProtocolChainId, tokenIn: string, tokenOut: string): V4PoolKey[];
|
|
31
32
|
export declare function _clearV4DiscoveryCache(): void;
|
|
32
33
|
export {};
|
|
@@ -19,8 +19,37 @@ const V4_CHAIN_CONFIG = {
|
|
|
19
19
|
deployBlock: 0,
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
|
+
const KNOWN_V4_BRIDGE_KEYS = {
|
|
23
|
+
4663: [
|
|
24
|
+
{
|
|
25
|
+
currency0: V4_ZERO_ADDRESS,
|
|
26
|
+
currency1: '0x5fc5360d0400a0fd4f2af552add042d716f1d168',
|
|
27
|
+
fee: 25,
|
|
28
|
+
tickSpacing: 1,
|
|
29
|
+
hooks: V4_ZERO_ADDRESS,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
currency0: V4_ZERO_ADDRESS,
|
|
33
|
+
currency1: '0x5fc5360d0400a0fd4f2af552add042d716f1d168',
|
|
34
|
+
fee: 100,
|
|
35
|
+
tickSpacing: 1,
|
|
36
|
+
hooks: V4_ZERO_ADDRESS,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
currency0: V4_ZERO_ADDRESS,
|
|
40
|
+
currency1: '0x5fc5360d0400a0fd4f2af552add042d716f1d168',
|
|
41
|
+
fee: 500,
|
|
42
|
+
tickSpacing: 10,
|
|
43
|
+
hooks: V4_ZERO_ADDRESS,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
};
|
|
22
47
|
const POOL_INITIALIZE_TOPIC = '0xdd466e674ea557f56295e2d0218a125ea4b4f0f6f3307b95f85e6110838d6438';
|
|
23
48
|
export function isSafeDiscoveredV4PoolKey(key) {
|
|
49
|
+
const dynamicFeeFlag = 0x800000;
|
|
50
|
+
if (key.fee === dynamicFeeFlag) {
|
|
51
|
+
return !compareAddresses(key.hooks, V4_ZERO_ADDRESS);
|
|
52
|
+
}
|
|
24
53
|
return key.fee >= 0 && key.fee <= V4_DISCOVERY_MAX_LP_FEE;
|
|
25
54
|
}
|
|
26
55
|
export function computeV4PoolId(key) {
|
|
@@ -206,10 +235,12 @@ async function discoverDirectV4Paths(chainId, tokenIn, tokenOut, provider, endpo
|
|
|
206
235
|
}
|
|
207
236
|
const keys = await Promise.all(poolIds.map((poolId) => resolvePoolKey(chainId, poolId, provider, pairCreatedAtByPoolId.get(poolId))));
|
|
208
237
|
const resolvedKeys = keys.filter((key) => key !== null && isSafeDiscoveredV4PoolKey(key));
|
|
209
|
-
const
|
|
238
|
+
const knownKeys = findKnownV4BridgeKeys(chainId, tokenIn, tokenOut);
|
|
239
|
+
const onchainKeys = resolvedKeys.length > 0 || knownKeys.length > 0
|
|
210
240
|
? []
|
|
211
|
-
: await discoverOnchainDirectV4PoolKeys(chainId, tokenIn, tokenOut, provider);
|
|
241
|
+
: await discoverOnchainDirectV4PoolKeys(chainId, tokenIn, tokenOut, provider).catch(() => []);
|
|
212
242
|
const paths = resolvedKeys
|
|
243
|
+
.concat(knownKeys)
|
|
213
244
|
.concat(onchainKeys)
|
|
214
245
|
.filter((key) => key !== null)
|
|
215
246
|
.map((key) => [
|
|
@@ -224,6 +255,12 @@ async function discoverDirectV4Paths(chainId, tokenIn, tokenOut, provider, endpo
|
|
|
224
255
|
]);
|
|
225
256
|
return paths;
|
|
226
257
|
}
|
|
258
|
+
export function findKnownV4BridgeKeys(chainId, tokenIn, tokenOut) {
|
|
259
|
+
return (KNOWN_V4_BRIDGE_KEYS[chainId] ?? []).filter((key) => (compareAddresses(key.currency0, tokenIn) &&
|
|
260
|
+
compareAddresses(key.currency1, tokenOut)) ||
|
|
261
|
+
(compareAddresses(key.currency0, tokenOut) &&
|
|
262
|
+
compareAddresses(key.currency1, tokenIn)));
|
|
263
|
+
}
|
|
227
264
|
async function discoverOnchainDirectV4PoolKeys(chainId, tokenIn, tokenOut, provider) {
|
|
228
265
|
const config = V4_CHAIN_CONFIG[chainId];
|
|
229
266
|
const [currency0, currency1] = tokenIn.toLowerCase() < tokenOut.toLowerCase()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TransactionRequest } from 'ethers';
|
|
2
|
+
export interface BuildTradesTxParams {
|
|
3
|
+
controller: string;
|
|
4
|
+
ledger: string;
|
|
5
|
+
adapters: string[];
|
|
6
|
+
callData: string[];
|
|
7
|
+
from: string;
|
|
8
|
+
}
|
|
9
|
+
export default function buildTradesTx(params: BuildTradesTxParams): TransactionRequest;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { FundController__factory } from '../typechain/index.js';
|
|
2
|
+
export default function buildTradesTx(params) {
|
|
3
|
+
const data = FundController__factory.createInterface().encodeFunctionData('executeTrades', [params.ledger, params.adapters, params.callData]);
|
|
4
|
+
return {
|
|
5
|
+
to: params.controller,
|
|
6
|
+
from: params.from,
|
|
7
|
+
data,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
@@ -7,4 +7,6 @@ export type { BuildWithdrawTxParams } from './buildWithdrawTx';
|
|
|
7
7
|
export { default as buildHarvestTx } from './buildHarvestTx';
|
|
8
8
|
export type { BuildHarvestTxParams } from './buildHarvestTx';
|
|
9
9
|
export { default as buildTradeTx } from './buildTradeTx';
|
|
10
|
+
export { default as buildTradesTx } from './buildTradesTx';
|
|
10
11
|
export type { BuildTradeTxParams } from './buildTradeTx';
|
|
12
|
+
export type { BuildTradesTxParams } from './buildTradesTx';
|
package/dist/txBuilders/index.js
CHANGED
|
@@ -3,3 +3,4 @@ export { default as buildCloseTx } from './buildCloseTx.js';
|
|
|
3
3
|
export { default as buildWithdrawTx } from './buildWithdrawTx.js';
|
|
4
4
|
export { default as buildHarvestTx } from './buildHarvestTx.js';
|
|
5
5
|
export { default as buildTradeTx } from './buildTradeTx.js';
|
|
6
|
+
export { default as buildTradesTx } from './buildTradesTx.js';
|