@andrewkimjoseph/celina-sdk 0.27.6 → 0.28.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/README.md +2 -2
- package/build/a2a/skills.js +1 -1
- package/build/a2a/skills.js.map +1 -1
- package/build/abis/uniswap-v3.d.ts +171 -0
- package/build/abis/uniswap-v3.js +120 -0
- package/build/abis/uniswap-v3.js.map +1 -0
- package/build/config/uniswap.d.ts +15 -0
- package/build/config/uniswap.js +9 -1
- package/build/config/uniswap.js.map +1 -1
- package/build/index.d.ts +3 -3
- package/build/services/swap-pairs.d.ts +18 -4
- package/build/services/swap-pairs.js +35 -2
- package/build/services/swap-pairs.js.map +1 -1
- package/build/services/uniswap-path-router.d.ts +11 -1
- package/build/services/uniswap-path-router.js +36 -10
- package/build/services/uniswap-path-router.js.map +1 -1
- package/build/services/uniswap-v3-path-router.d.ts +40 -0
- package/build/services/uniswap-v3-path-router.js +183 -0
- package/build/services/uniswap-v3-path-router.js.map +1 -0
- package/build/services/uniswap-v3-pool-discovery.d.ts +33 -0
- package/build/services/uniswap-v3-pool-discovery.js +141 -0
- package/build/services/uniswap-v3-pool-discovery.js.map +1 -0
- package/build/services/uniswap-venue-quote.d.ts +28 -0
- package/build/services/uniswap-venue-quote.js +114 -0
- package/build/services/uniswap-venue-quote.js.map +1 -0
- package/build/services/uniswap.service.d.ts +24 -9
- package/build/services/uniswap.service.js +86 -37
- package/build/services/uniswap.service.js.map +1 -1
- package/build/src/a2a/skills.js +1 -1
- package/build/src/a2a/skills.js.map +1 -1
- package/build/src/abis/uniswap-v3.d.ts +171 -0
- package/build/src/abis/uniswap-v3.js +120 -0
- package/build/src/abis/uniswap-v3.js.map +1 -0
- package/build/src/config/uniswap.d.ts +15 -0
- package/build/src/config/uniswap.js +9 -1
- package/build/src/config/uniswap.js.map +1 -1
- package/build/src/index.d.ts +3 -3
- package/build/src/services/swap-pairs.d.ts +18 -4
- package/build/src/services/swap-pairs.js +35 -2
- package/build/src/services/swap-pairs.js.map +1 -1
- package/build/src/services/uniswap-path-router.d.ts +11 -1
- package/build/src/services/uniswap-path-router.js +36 -10
- package/build/src/services/uniswap-path-router.js.map +1 -1
- package/build/src/services/uniswap-v3-path-router.d.ts +40 -0
- package/build/src/services/uniswap-v3-path-router.js +183 -0
- package/build/src/services/uniswap-v3-path-router.js.map +1 -0
- package/build/src/services/uniswap-v3-pool-discovery.d.ts +33 -0
- package/build/src/services/uniswap-v3-pool-discovery.js +141 -0
- package/build/src/services/uniswap-v3-pool-discovery.js.map +1 -0
- package/build/src/services/uniswap-venue-quote.d.ts +28 -0
- package/build/src/services/uniswap-venue-quote.js +114 -0
- package/build/src/services/uniswap-venue-quote.js.map +1 -0
- package/build/src/services/uniswap.service.d.ts +24 -9
- package/build/src/services/uniswap.service.js +86 -37
- package/build/src/services/uniswap.service.js.map +1 -1
- package/build/src/tools/domains/browser.js +5 -3
- package/build/src/tools/domains/browser.js.map +1 -1
- package/build/src/tools/domains/uniswap.js +5 -5
- package/build/src/tools/domains/uniswap.js.map +1 -1
- package/build/src/tools/swap-routing.d.ts +2 -2
- package/build/src/tools/swap-routing.js +21 -6
- package/build/src/tools/swap-routing.js.map +1 -1
- package/build/tests/catalog/domains/defi.js +8 -1
- package/build/tests/catalog/domains/defi.js.map +1 -1
- package/build/tools/domains/browser.js +5 -3
- package/build/tools/domains/browser.js.map +1 -1
- package/build/tools/domains/uniswap.js +5 -5
- package/build/tools/domains/uniswap.js.map +1 -1
- package/build/tools/swap-routing.d.ts +2 -2
- package/build/tools/swap-routing.js +21 -6
- package/build/tools/swap-routing.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uniswap v3 path routing: BFS over the hub-pool index and QuoterV2 selection.
|
|
3
|
+
*/
|
|
4
|
+
import { encodeFunctionData, encodePacked } from "viem";
|
|
5
|
+
import { swapRouter02Abi, v3QuoterAbi } from "../abis/uniswap-v3.js";
|
|
6
|
+
import { UNISWAP_V3 } from "../config/uniswap.js";
|
|
7
|
+
import { getUniswapV3PoolIndex, poolsBetweenV3, } from "./uniswap-v3-pool-discovery.js";
|
|
8
|
+
const MAX_POOLS = 3;
|
|
9
|
+
/** Token on the other side of a canonical v3 pool. */
|
|
10
|
+
export function otherV3Token(pool, token) {
|
|
11
|
+
const current = token.toLowerCase();
|
|
12
|
+
if (pool.token0.toLowerCase() === current)
|
|
13
|
+
return pool.token1;
|
|
14
|
+
if (pool.token1.toLowerCase() === current)
|
|
15
|
+
return pool.token0;
|
|
16
|
+
throw new Error("Token is not in the Uniswap v3 pool.");
|
|
17
|
+
}
|
|
18
|
+
/** Packed v3 path: token, fee, token, fee, token. */
|
|
19
|
+
export function encodeV3Path(tokenIn, pools) {
|
|
20
|
+
const types = ["address"];
|
|
21
|
+
const values = [tokenIn];
|
|
22
|
+
let current = tokenIn;
|
|
23
|
+
for (const pool of pools) {
|
|
24
|
+
const out = otherV3Token(pool, current);
|
|
25
|
+
types.push("uint24", "address");
|
|
26
|
+
values.push(pool.fee, out);
|
|
27
|
+
current = out;
|
|
28
|
+
}
|
|
29
|
+
return encodePacked(types, values);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* SwapRouter02 calldata. Single- and multi-hop swaps are wrapped in
|
|
33
|
+
* `multicall(deadline, bytes[])` because exactInput has no deadline field.
|
|
34
|
+
*/
|
|
35
|
+
export function buildSwapRouter02Calldata(args) {
|
|
36
|
+
const { pools, tokenIn, recipient, amountIn, amountOutMin, deadline } = args;
|
|
37
|
+
if (pools.length === 0) {
|
|
38
|
+
throw new Error("Uniswap v3 route has no pools.");
|
|
39
|
+
}
|
|
40
|
+
const inner = pools.length === 1
|
|
41
|
+
? encodeFunctionData({
|
|
42
|
+
abi: swapRouter02Abi,
|
|
43
|
+
functionName: "exactInputSingle",
|
|
44
|
+
args: [
|
|
45
|
+
{
|
|
46
|
+
tokenIn,
|
|
47
|
+
tokenOut: otherV3Token(pools[0], tokenIn),
|
|
48
|
+
fee: pools[0].fee,
|
|
49
|
+
recipient,
|
|
50
|
+
amountIn,
|
|
51
|
+
amountOutMinimum: amountOutMin,
|
|
52
|
+
sqrtPriceLimitX96: 0n,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
})
|
|
56
|
+
: encodeFunctionData({
|
|
57
|
+
abi: swapRouter02Abi,
|
|
58
|
+
functionName: "exactInput",
|
|
59
|
+
args: [
|
|
60
|
+
{
|
|
61
|
+
path: encodeV3Path(tokenIn, pools),
|
|
62
|
+
recipient,
|
|
63
|
+
amountIn,
|
|
64
|
+
amountOutMinimum: amountOutMin,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
return encodeFunctionData({
|
|
69
|
+
abi: swapRouter02Abi,
|
|
70
|
+
functionName: "multicall",
|
|
71
|
+
args: [deadline, [inner]],
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export function enumerateV3Paths(index, tokenIn, tokenOut) {
|
|
75
|
+
const start = tokenIn.toLowerCase();
|
|
76
|
+
const goal = tokenOut.toLowerCase();
|
|
77
|
+
if (start === goal) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const results = [];
|
|
81
|
+
const queue = [
|
|
82
|
+
{ token: start, pools: [], visited: new Set([start]) },
|
|
83
|
+
];
|
|
84
|
+
while (queue.length > 0) {
|
|
85
|
+
const node = queue.shift();
|
|
86
|
+
if (node.pools.length >= MAX_POOLS) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const neighbors = index.adjacency.get(node.token);
|
|
90
|
+
if (!neighbors) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
for (const neighbor of neighbors) {
|
|
94
|
+
if (node.visited.has(neighbor)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
for (const pool of poolsBetweenV3(index, node.token, neighbor)) {
|
|
98
|
+
const nextPools = [...node.pools, pool];
|
|
99
|
+
if (neighbor === goal) {
|
|
100
|
+
results.push(nextPools);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (nextPools.length < MAX_POOLS) {
|
|
104
|
+
queue.push({
|
|
105
|
+
token: neighbor,
|
|
106
|
+
pools: nextPools,
|
|
107
|
+
visited: new Set([...node.visited, neighbor]),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
export function countV3CandidatePaths(index, tokenIn, tokenOut) {
|
|
116
|
+
return enumerateV3Paths(index, tokenIn, tokenOut).length;
|
|
117
|
+
}
|
|
118
|
+
async function quoteV3Path(client, tokenIn, pools, amountIn) {
|
|
119
|
+
if (pools.length === 0) {
|
|
120
|
+
return 0n;
|
|
121
|
+
}
|
|
122
|
+
if (pools.length === 1) {
|
|
123
|
+
const pool = pools[0];
|
|
124
|
+
const { result } = await client.simulateContract({
|
|
125
|
+
address: UNISWAP_V3.quoterV2,
|
|
126
|
+
abi: v3QuoterAbi,
|
|
127
|
+
functionName: "quoteExactInputSingle",
|
|
128
|
+
args: [
|
|
129
|
+
{
|
|
130
|
+
tokenIn,
|
|
131
|
+
tokenOut: otherV3Token(pool, tokenIn),
|
|
132
|
+
amountIn,
|
|
133
|
+
fee: pool.fee,
|
|
134
|
+
sqrtPriceLimitX96: 0n,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
return result[0];
|
|
139
|
+
}
|
|
140
|
+
const { result } = await client.simulateContract({
|
|
141
|
+
address: UNISWAP_V3.quoterV2,
|
|
142
|
+
abi: v3QuoterAbi,
|
|
143
|
+
functionName: "quoteExactInput",
|
|
144
|
+
args: [encodeV3Path(tokenIn, pools), amountIn],
|
|
145
|
+
});
|
|
146
|
+
return result[0];
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Quote candidate v3 paths and return the highest output.
|
|
150
|
+
* @param maxPaths - Cap on simulateContract calls for this venue
|
|
151
|
+
*/
|
|
152
|
+
export async function findBestV3Route(client, tokenIn, tokenOut, amountIn, maxPaths, index) {
|
|
153
|
+
const poolIndex = index ?? (await getUniswapV3PoolIndex(client));
|
|
154
|
+
const candidatePaths = enumerateV3Paths(poolIndex, tokenIn, tokenOut)
|
|
155
|
+
.sort((a, b) => a.length - b.length)
|
|
156
|
+
.slice(0, Math.max(0, maxPaths));
|
|
157
|
+
const quotes = await Promise.allSettled(candidatePaths.map(async (pools) => ({
|
|
158
|
+
pools,
|
|
159
|
+
amountOut: await quoteV3Path(client, tokenIn, pools, amountIn),
|
|
160
|
+
})));
|
|
161
|
+
let best = null;
|
|
162
|
+
for (const quote of quotes) {
|
|
163
|
+
if (quote.status !== "fulfilled" || quote.value.amountOut <= 0n) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (!best || quote.value.amountOut > best.amountOut) {
|
|
167
|
+
best = {
|
|
168
|
+
amountOut: quote.value.amountOut,
|
|
169
|
+
route: {
|
|
170
|
+
tokenIn,
|
|
171
|
+
tokenOut,
|
|
172
|
+
pools: quote.value.pools,
|
|
173
|
+
hops: quote.value.pools.length,
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!best) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return { ...best, indexSource: poolIndex.source };
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=uniswap-v3-path-router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniswap-v3-path-router.js","sourceRoot":"","sources":["../../../src/services/uniswap-v3-path-router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uniswap v3 pool discovery: on-chain hub probing via the Celo v3 factory.
|
|
3
|
+
* Hub-to-hub pools only — there is no v3 subgraph in this SDK.
|
|
4
|
+
*/
|
|
5
|
+
import type { PublicClient } from "viem";
|
|
6
|
+
import { type UniswapV3Pool } from "../config/uniswap.js";
|
|
7
|
+
/** One edge in the v3 pool routing graph. */
|
|
8
|
+
export type UniswapV3PoolEdge = {
|
|
9
|
+
pool: UniswapV3Pool;
|
|
10
|
+
tokenA: `0x${string}`;
|
|
11
|
+
tokenB: `0x${string}`;
|
|
12
|
+
};
|
|
13
|
+
/** Cached routing graph of v3 hub pools on Celo mainnet. */
|
|
14
|
+
export type UniswapV3PoolIndex = {
|
|
15
|
+
edges: UniswapV3PoolEdge[];
|
|
16
|
+
adjacency: Map<string, Set<string>>;
|
|
17
|
+
/** Sorted pair key (`tokenLo|tokenHi`) → pools across fee tiers. */
|
|
18
|
+
poolsByPair: Map<string, UniswapV3Pool[]>;
|
|
19
|
+
source: "onchain";
|
|
20
|
+
fetchedAt: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Load or refresh the v3 hub-pool index.
|
|
24
|
+
* @param client - Celo public client for factory probing
|
|
25
|
+
* @param options.forceRefresh - Bypass TTL cache when true
|
|
26
|
+
*/
|
|
27
|
+
export declare function getUniswapV3PoolIndex(client: PublicClient, options?: {
|
|
28
|
+
forceRefresh?: boolean;
|
|
29
|
+
}): Promise<UniswapV3PoolIndex>;
|
|
30
|
+
/** All v3 pools connecting two tokens in a cached index. */
|
|
31
|
+
export declare function poolsBetweenV3(index: UniswapV3PoolIndex, tokenA: `0x${string}`, tokenB: `0x${string}`): UniswapV3Pool[];
|
|
32
|
+
/** Reset cached v3 index (for tests). */
|
|
33
|
+
export declare function resetUniswapV3PoolIndexCache(): void;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { uniswapV3FactoryAbi, uniswapV3PoolAbi } from "../abis/uniswap-v3.js";
|
|
2
|
+
import { UNISWAP_HUB_CURRENCIES, UNISWAP_POOL_CACHE_TTL_MS, UNISWAP_V3, UNISWAP_V3_FEE_TIERS, UNISWAP_V4, } from "../config/uniswap.js";
|
|
3
|
+
const ZERO_ADDRESS = UNISWAP_V4.nativeCurrency.toLowerCase();
|
|
4
|
+
const MULTICALL_CHUNK_SIZE = 100;
|
|
5
|
+
let cachedIndex = null;
|
|
6
|
+
function pairKey(a, b) {
|
|
7
|
+
const lo = a.toLowerCase();
|
|
8
|
+
const hi = b.toLowerCase();
|
|
9
|
+
return lo < hi ? `${lo}|${hi}` : `${hi}|${lo}`;
|
|
10
|
+
}
|
|
11
|
+
function chunk(items, size) {
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for (let i = 0; i < items.length; i += size) {
|
|
14
|
+
chunks.push(items.slice(i, i + size));
|
|
15
|
+
}
|
|
16
|
+
return chunks;
|
|
17
|
+
}
|
|
18
|
+
function sortTokens(a, b) {
|
|
19
|
+
return a.toLowerCase() < b.toLowerCase() ? [a, b] : [b, a];
|
|
20
|
+
}
|
|
21
|
+
function buildPoolIndex(pools) {
|
|
22
|
+
const edges = [];
|
|
23
|
+
const adjacency = new Map();
|
|
24
|
+
const poolsByPair = new Map();
|
|
25
|
+
for (const pool of pools) {
|
|
26
|
+
const tokenA = pool.token0.toLowerCase();
|
|
27
|
+
const tokenB = pool.token1.toLowerCase();
|
|
28
|
+
edges.push({ pool, tokenA, tokenB });
|
|
29
|
+
const key = pairKey(tokenA, tokenB);
|
|
30
|
+
const existing = poolsByPair.get(key) ?? [];
|
|
31
|
+
existing.push(pool);
|
|
32
|
+
poolsByPair.set(key, existing);
|
|
33
|
+
for (const [from, to] of [
|
|
34
|
+
[tokenA, tokenB],
|
|
35
|
+
[tokenB, tokenA],
|
|
36
|
+
]) {
|
|
37
|
+
const neighbors = adjacency.get(from) ?? new Set();
|
|
38
|
+
neighbors.add(to);
|
|
39
|
+
adjacency.set(from, neighbors);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
edges,
|
|
44
|
+
adjacency,
|
|
45
|
+
poolsByPair,
|
|
46
|
+
source: "onchain",
|
|
47
|
+
fetchedAt: Date.now(),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function probeHubPools(client) {
|
|
51
|
+
const hubs = UNISWAP_HUB_CURRENCIES.filter((currency) => currency.toLowerCase() !== ZERO_ADDRESS);
|
|
52
|
+
const candidates = [];
|
|
53
|
+
for (let i = 0; i < hubs.length; i++) {
|
|
54
|
+
for (let j = i + 1; j < hubs.length; j++) {
|
|
55
|
+
const [token0, token1] = sortTokens(hubs[i], hubs[j]);
|
|
56
|
+
for (const fee of UNISWAP_V3_FEE_TIERS) {
|
|
57
|
+
candidates.push({ token0, token1, fee });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const discovered = [];
|
|
62
|
+
for (const batch of chunk(candidates, MULTICALL_CHUNK_SIZE)) {
|
|
63
|
+
const results = await client.multicall({
|
|
64
|
+
allowFailure: true,
|
|
65
|
+
contracts: batch.map((candidate) => ({
|
|
66
|
+
address: UNISWAP_V3.factory,
|
|
67
|
+
abi: uniswapV3FactoryAbi,
|
|
68
|
+
functionName: "getPool",
|
|
69
|
+
args: [candidate.token0, candidate.token1, candidate.fee],
|
|
70
|
+
})),
|
|
71
|
+
});
|
|
72
|
+
for (let i = 0; i < batch.length; i++) {
|
|
73
|
+
const result = results[i];
|
|
74
|
+
const candidate = batch[i];
|
|
75
|
+
if (result?.status !== "success") {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const address = result.result;
|
|
79
|
+
if (address.toLowerCase() === ZERO_ADDRESS) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
discovered.push({
|
|
83
|
+
address,
|
|
84
|
+
pool: {
|
|
85
|
+
token0: candidate.token0,
|
|
86
|
+
token1: candidate.token1,
|
|
87
|
+
fee: candidate.fee,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (discovered.length === 0) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
const withLiquidity = [];
|
|
96
|
+
for (const batch of chunk(discovered, MULTICALL_CHUNK_SIZE)) {
|
|
97
|
+
const results = await client.multicall({
|
|
98
|
+
allowFailure: true,
|
|
99
|
+
contracts: batch.map((entry) => ({
|
|
100
|
+
address: entry.address,
|
|
101
|
+
abi: uniswapV3PoolAbi,
|
|
102
|
+
functionName: "liquidity",
|
|
103
|
+
})),
|
|
104
|
+
});
|
|
105
|
+
for (let i = 0; i < batch.length; i++) {
|
|
106
|
+
const result = results[i];
|
|
107
|
+
if (result?.status !== "success") {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const liquidity = result.result;
|
|
111
|
+
if (liquidity > 0n) {
|
|
112
|
+
withLiquidity.push(batch[i].pool);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return withLiquidity;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Load or refresh the v3 hub-pool index.
|
|
120
|
+
* @param client - Celo public client for factory probing
|
|
121
|
+
* @param options.forceRefresh - Bypass TTL cache when true
|
|
122
|
+
*/
|
|
123
|
+
export async function getUniswapV3PoolIndex(client, options) {
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
if (!options?.forceRefresh &&
|
|
126
|
+
cachedIndex &&
|
|
127
|
+
now - cachedIndex.fetchedAt < UNISWAP_POOL_CACHE_TTL_MS) {
|
|
128
|
+
return cachedIndex;
|
|
129
|
+
}
|
|
130
|
+
cachedIndex = buildPoolIndex(await probeHubPools(client));
|
|
131
|
+
return cachedIndex;
|
|
132
|
+
}
|
|
133
|
+
/** All v3 pools connecting two tokens in a cached index. */
|
|
134
|
+
export function poolsBetweenV3(index, tokenA, tokenB) {
|
|
135
|
+
return index.poolsByPair.get(pairKey(tokenA, tokenB)) ?? [];
|
|
136
|
+
}
|
|
137
|
+
/** Reset cached v3 index (for tests). */
|
|
138
|
+
export function resetUniswapV3PoolIndexCache() {
|
|
139
|
+
cachedIndex = null;
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=uniswap-v3-pool-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniswap-v3-pool-discovery.js","sourceRoot":"","sources":["../../../src/services/uniswap-v3-pool-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pick the better of the best Uniswap v3 path and the best Uniswap v4 path.
|
|
3
|
+
* A quote simulates at most MAX_QUOTE_PATHS paths across both venues.
|
|
4
|
+
*/
|
|
5
|
+
import type { PublicClient } from "viem";
|
|
6
|
+
import type { UniswapProtocol } from "../config/uniswap.js";
|
|
7
|
+
import { type UniswapV3SwapRoute } from "./uniswap-v3-path-router.js";
|
|
8
|
+
import { type UniswapSwapRoute } from "./uniswap-path-router.js";
|
|
9
|
+
export type UniswapQuotedRoute = {
|
|
10
|
+
protocol: "uniswap_v4";
|
|
11
|
+
amountOut: bigint;
|
|
12
|
+
indexSource: string;
|
|
13
|
+
hops: number;
|
|
14
|
+
route: UniswapSwapRoute;
|
|
15
|
+
} | {
|
|
16
|
+
protocol: "uniswap_v3";
|
|
17
|
+
amountOut: bigint;
|
|
18
|
+
indexSource: string;
|
|
19
|
+
hops: number;
|
|
20
|
+
route: UniswapV3SwapRoute;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Quote v3 and v4, or only the pinned venue.
|
|
24
|
+
* @returns The winning route, or null when every attempted venue has no liquidity
|
|
25
|
+
*/
|
|
26
|
+
export declare function findBestUniswapQuote(client: PublicClient, currencyIn: `0x${string}`, currencyOut: `0x${string}`, amountIn: bigint, options?: {
|
|
27
|
+
protocol?: UniswapProtocol;
|
|
28
|
+
}): Promise<UniswapQuotedRoute | null>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { getUniswapV3PoolIndex } from "./uniswap-v3-pool-discovery.js";
|
|
2
|
+
import { countV3CandidatePaths, findBestV3Route, } from "./uniswap-v3-path-router.js";
|
|
3
|
+
import { getUniswapPoolIndex } from "./uniswap-pool-discovery.js";
|
|
4
|
+
import { countV4CandidatePaths, findBestUniswapRoute, MAX_QUOTE_PATHS, splitQuoteBudget, } from "./uniswap-path-router.js";
|
|
5
|
+
function prefer(current, next) {
|
|
6
|
+
if (!next)
|
|
7
|
+
return current;
|
|
8
|
+
if (!current)
|
|
9
|
+
return next;
|
|
10
|
+
if (next.amountOut > current.amountOut)
|
|
11
|
+
return next;
|
|
12
|
+
if (next.amountOut < current.amountOut)
|
|
13
|
+
return current;
|
|
14
|
+
// Equal output: v3 needs at most one approval, v4 can need two.
|
|
15
|
+
if (next.protocol === "uniswap_v3" && current.protocol === "uniswap_v4") {
|
|
16
|
+
return next;
|
|
17
|
+
}
|
|
18
|
+
return current;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Quote v3 and v4, or only the pinned venue.
|
|
22
|
+
* @returns The winning route, or null when every attempted venue has no liquidity
|
|
23
|
+
*/
|
|
24
|
+
export async function findBestUniswapQuote(client, currencyIn, currencyOut, amountIn, options) {
|
|
25
|
+
const pinned = options?.protocol;
|
|
26
|
+
if (pinned === "uniswap_v4") {
|
|
27
|
+
const found = await findBestUniswapRoute(client, currencyIn, currencyOut, amountIn, MAX_QUOTE_PATHS);
|
|
28
|
+
if (!found)
|
|
29
|
+
return null;
|
|
30
|
+
return {
|
|
31
|
+
protocol: "uniswap_v4",
|
|
32
|
+
amountOut: found.amountOut,
|
|
33
|
+
indexSource: found.indexSource,
|
|
34
|
+
hops: found.route.hops,
|
|
35
|
+
route: found.route,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
if (pinned === "uniswap_v3") {
|
|
39
|
+
const found = await findBestV3Route(client, currencyIn, currencyOut, amountIn, MAX_QUOTE_PATHS);
|
|
40
|
+
if (!found)
|
|
41
|
+
return null;
|
|
42
|
+
return {
|
|
43
|
+
protocol: "uniswap_v3",
|
|
44
|
+
amountOut: found.amountOut,
|
|
45
|
+
indexSource: found.indexSource,
|
|
46
|
+
hops: found.route.hops,
|
|
47
|
+
route: found.route,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const [v4IndexResult, v3IndexResult] = await Promise.allSettled([
|
|
51
|
+
getUniswapPoolIndex(client),
|
|
52
|
+
getUniswapV3PoolIndex(client),
|
|
53
|
+
]);
|
|
54
|
+
const v4Index = v4IndexResult.status === "fulfilled" ? v4IndexResult.value : null;
|
|
55
|
+
const v3Index = v3IndexResult.status === "fulfilled" ? v3IndexResult.value : null;
|
|
56
|
+
if (!v4Index && !v3Index) {
|
|
57
|
+
throw v4IndexResult.status === "rejected"
|
|
58
|
+
? v4IndexResult.reason
|
|
59
|
+
: v3IndexResult.status === "rejected"
|
|
60
|
+
? v3IndexResult.reason
|
|
61
|
+
: new Error("Uniswap pool index unavailable.");
|
|
62
|
+
}
|
|
63
|
+
const caps = splitQuoteBudget(v4Index ? countV4CandidatePaths(v4Index, currencyIn, currencyOut) : 0, v3Index ? countV3CandidatePaths(v3Index, currencyIn, currencyOut) : 0);
|
|
64
|
+
const [v4Result, v3Result] = await Promise.allSettled([
|
|
65
|
+
v4Index
|
|
66
|
+
? findBestUniswapRoute(client, currencyIn, currencyOut, amountIn, caps.first, v4Index)
|
|
67
|
+
: Promise.reject(v4IndexResult.status === "rejected"
|
|
68
|
+
? v4IndexResult.reason
|
|
69
|
+
: new Error("Uniswap v4 pool index unavailable.")),
|
|
70
|
+
v3Index
|
|
71
|
+
? findBestV3Route(client, currencyIn, currencyOut, amountIn, caps.second, v3Index)
|
|
72
|
+
: Promise.reject(v3IndexResult.status === "rejected"
|
|
73
|
+
? v3IndexResult.reason
|
|
74
|
+
: new Error("Uniswap v3 pool index unavailable.")),
|
|
75
|
+
]);
|
|
76
|
+
let best = null;
|
|
77
|
+
const failures = [];
|
|
78
|
+
if (v4Result.status === "fulfilled") {
|
|
79
|
+
if (v4Result.value) {
|
|
80
|
+
best = prefer(best, {
|
|
81
|
+
protocol: "uniswap_v4",
|
|
82
|
+
amountOut: v4Result.value.amountOut,
|
|
83
|
+
indexSource: v4Result.value.indexSource,
|
|
84
|
+
hops: v4Result.value.route.hops,
|
|
85
|
+
route: v4Result.value.route,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
failures.push(v4Result.reason);
|
|
91
|
+
}
|
|
92
|
+
if (v3Result.status === "fulfilled") {
|
|
93
|
+
if (v3Result.value) {
|
|
94
|
+
best = prefer(best, {
|
|
95
|
+
protocol: "uniswap_v3",
|
|
96
|
+
amountOut: v3Result.value.amountOut,
|
|
97
|
+
indexSource: v3Result.value.indexSource,
|
|
98
|
+
hops: v3Result.value.route.hops,
|
|
99
|
+
route: v3Result.value.route,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
failures.push(v3Result.reason);
|
|
105
|
+
}
|
|
106
|
+
if (best) {
|
|
107
|
+
return best;
|
|
108
|
+
}
|
|
109
|
+
if (failures.length > 0) {
|
|
110
|
+
throw failures[0];
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=uniswap-venue-quote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniswap-venue-quote.js","sourceRoot":"","sources":["../../../src/services/uniswap-venue-quote.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { CeloClientFactory } from "../clients/celo-client.js";
|
|
2
|
+
import { type UniswapProtocol } from "../config/uniswap.js";
|
|
2
3
|
import { type SerializedPreparedFlow } from "../types/prepared.js";
|
|
3
4
|
import { type SwapPairsResult } from "./swap-pairs.js";
|
|
4
|
-
/** Optional parameters for Uniswap
|
|
5
|
+
/** Optional parameters for Uniswap swap estimates and prepares. */
|
|
5
6
|
export interface UniswapSwapParams {
|
|
6
7
|
/** Max slippage tolerance in percent (default `0.5`). */
|
|
7
8
|
slippageTolerance?: number;
|
|
@@ -9,8 +10,13 @@ export interface UniswapSwapParams {
|
|
|
9
10
|
deadlineMinutes?: number;
|
|
10
11
|
/** Address receiving output tokens (default: `from`). */
|
|
11
12
|
recipient?: `0x${string}`;
|
|
13
|
+
/**
|
|
14
|
+
* When set, quote and prepare only this venue.
|
|
15
|
+
* Omit to compare v3 and v4 and keep the higher output.
|
|
16
|
+
*/
|
|
17
|
+
protocol?: UniswapProtocol;
|
|
12
18
|
}
|
|
13
|
-
/** Uniswap v4 quotes, gas estimates, and `prepareSwap` flows on Celo mainnet. */
|
|
19
|
+
/** Uniswap v3 and v4 quotes, gas estimates, and `prepareSwap` flows on Celo mainnet. */
|
|
14
20
|
export declare class UniswapService {
|
|
15
21
|
private readonly clientFactory;
|
|
16
22
|
private readonly tokenService;
|
|
@@ -23,13 +29,18 @@ export declare class UniswapService {
|
|
|
23
29
|
private getApprovalSteps;
|
|
24
30
|
private estimateCallGas;
|
|
25
31
|
private estimateSwapGasWithAllowance;
|
|
32
|
+
private venueLabel;
|
|
33
|
+
private buildSwapCalldata;
|
|
26
34
|
/**
|
|
27
|
-
* Uniswap
|
|
35
|
+
* Uniswap registry-token pairs on Celo mainnet (v4 graph plus v3 hub pools).
|
|
36
|
+
* `protocol` stays `uniswap_v4` for existing consumers. Each pair's `venues`
|
|
37
|
+
* lists which versions can route it.
|
|
28
38
|
* @param token - Optional registry symbol; when set, only pairs involving that token
|
|
29
39
|
*/
|
|
30
40
|
listPairs(token?: string): Promise<SwapPairsResult>;
|
|
31
41
|
/**
|
|
32
|
-
* Expected Uniswap
|
|
42
|
+
* Expected Uniswap output for a token pair — no wallet required.
|
|
43
|
+
* Compares v3 and v4 and returns the higher output.
|
|
33
44
|
* @param tokenIn - Input token symbol or address
|
|
34
45
|
* @param tokenOut - Output token symbol or address
|
|
35
46
|
* @param amount - Human-readable input amount
|
|
@@ -37,9 +48,9 @@ export declare class UniswapService {
|
|
|
37
48
|
*/
|
|
38
49
|
getSwapQuote(tokenIn: string, tokenOut: string, amount: string, _from?: `0x${string}`): Promise<{
|
|
39
50
|
route: {
|
|
40
|
-
pools: import("../config/uniswap.js").UniswapPoolKey[];
|
|
51
|
+
pools: import("../config/uniswap.js").UniswapPoolKey[] | import("../config/uniswap.js").UniswapV3Pool[];
|
|
41
52
|
};
|
|
42
|
-
protocol:
|
|
53
|
+
protocol: UniswapProtocol;
|
|
43
54
|
network: "mainnet";
|
|
44
55
|
tokenIn: string;
|
|
45
56
|
tokenOut: string;
|
|
@@ -49,7 +60,8 @@ export declare class UniswapService {
|
|
|
49
60
|
indexSource: string | undefined;
|
|
50
61
|
}>;
|
|
51
62
|
/**
|
|
52
|
-
* Simulate gas for a Uniswap
|
|
63
|
+
* Simulate gas for a Uniswap swap from `from`.
|
|
64
|
+
* v4 includes Permit2 approvals when needed. v3 includes one ERC-20 approval when needed.
|
|
53
65
|
* @param from - Sender wallet address
|
|
54
66
|
* @param tokenIn - Input token symbol or address
|
|
55
67
|
* @param tokenOut - Output token symbol or address
|
|
@@ -67,7 +79,7 @@ export declare class UniswapService {
|
|
|
67
79
|
slippageTolerance: number;
|
|
68
80
|
deadline: string;
|
|
69
81
|
deadlineMinutes: number;
|
|
70
|
-
protocol:
|
|
82
|
+
protocol: UniswapProtocol;
|
|
71
83
|
network: "mainnet";
|
|
72
84
|
tokenIn: string;
|
|
73
85
|
tokenOut: string;
|
|
@@ -77,7 +89,10 @@ export declare class UniswapService {
|
|
|
77
89
|
indexSource: string | undefined;
|
|
78
90
|
}>;
|
|
79
91
|
/**
|
|
80
|
-
* Build unsigned Uniswap
|
|
92
|
+
* Build unsigned Uniswap steps.
|
|
93
|
+
* v4: ERC-20 approve → Permit2 approve → Universal Router swap.
|
|
94
|
+
* v3: ERC-20 approve → SwapRouter02 multicall swap.
|
|
95
|
+
* Pass `params.protocol` to pin the venue from a quote the caller already chose.
|
|
81
96
|
* @param from - Sender wallet address
|
|
82
97
|
* @param tokenIn - Input token symbol or address
|
|
83
98
|
* @param tokenOut - Output token symbol or address
|