@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.
Files changed (72) hide show
  1. package/README.md +2 -2
  2. package/build/a2a/skills.js +1 -1
  3. package/build/a2a/skills.js.map +1 -1
  4. package/build/abis/uniswap-v3.d.ts +171 -0
  5. package/build/abis/uniswap-v3.js +120 -0
  6. package/build/abis/uniswap-v3.js.map +1 -0
  7. package/build/config/uniswap.d.ts +15 -0
  8. package/build/config/uniswap.js +9 -1
  9. package/build/config/uniswap.js.map +1 -1
  10. package/build/index.d.ts +3 -3
  11. package/build/services/swap-pairs.d.ts +18 -4
  12. package/build/services/swap-pairs.js +35 -2
  13. package/build/services/swap-pairs.js.map +1 -1
  14. package/build/services/uniswap-path-router.d.ts +11 -1
  15. package/build/services/uniswap-path-router.js +36 -10
  16. package/build/services/uniswap-path-router.js.map +1 -1
  17. package/build/services/uniswap-v3-path-router.d.ts +40 -0
  18. package/build/services/uniswap-v3-path-router.js +183 -0
  19. package/build/services/uniswap-v3-path-router.js.map +1 -0
  20. package/build/services/uniswap-v3-pool-discovery.d.ts +33 -0
  21. package/build/services/uniswap-v3-pool-discovery.js +141 -0
  22. package/build/services/uniswap-v3-pool-discovery.js.map +1 -0
  23. package/build/services/uniswap-venue-quote.d.ts +28 -0
  24. package/build/services/uniswap-venue-quote.js +114 -0
  25. package/build/services/uniswap-venue-quote.js.map +1 -0
  26. package/build/services/uniswap.service.d.ts +24 -9
  27. package/build/services/uniswap.service.js +86 -37
  28. package/build/services/uniswap.service.js.map +1 -1
  29. package/build/src/a2a/skills.js +1 -1
  30. package/build/src/a2a/skills.js.map +1 -1
  31. package/build/src/abis/uniswap-v3.d.ts +171 -0
  32. package/build/src/abis/uniswap-v3.js +120 -0
  33. package/build/src/abis/uniswap-v3.js.map +1 -0
  34. package/build/src/config/uniswap.d.ts +15 -0
  35. package/build/src/config/uniswap.js +9 -1
  36. package/build/src/config/uniswap.js.map +1 -1
  37. package/build/src/index.d.ts +3 -3
  38. package/build/src/services/swap-pairs.d.ts +18 -4
  39. package/build/src/services/swap-pairs.js +35 -2
  40. package/build/src/services/swap-pairs.js.map +1 -1
  41. package/build/src/services/uniswap-path-router.d.ts +11 -1
  42. package/build/src/services/uniswap-path-router.js +36 -10
  43. package/build/src/services/uniswap-path-router.js.map +1 -1
  44. package/build/src/services/uniswap-v3-path-router.d.ts +40 -0
  45. package/build/src/services/uniswap-v3-path-router.js +183 -0
  46. package/build/src/services/uniswap-v3-path-router.js.map +1 -0
  47. package/build/src/services/uniswap-v3-pool-discovery.d.ts +33 -0
  48. package/build/src/services/uniswap-v3-pool-discovery.js +141 -0
  49. package/build/src/services/uniswap-v3-pool-discovery.js.map +1 -0
  50. package/build/src/services/uniswap-venue-quote.d.ts +28 -0
  51. package/build/src/services/uniswap-venue-quote.js +114 -0
  52. package/build/src/services/uniswap-venue-quote.js.map +1 -0
  53. package/build/src/services/uniswap.service.d.ts +24 -9
  54. package/build/src/services/uniswap.service.js +86 -37
  55. package/build/src/services/uniswap.service.js.map +1 -1
  56. package/build/src/tools/domains/browser.js +5 -3
  57. package/build/src/tools/domains/browser.js.map +1 -1
  58. package/build/src/tools/domains/uniswap.js +5 -5
  59. package/build/src/tools/domains/uniswap.js.map +1 -1
  60. package/build/src/tools/swap-routing.d.ts +2 -2
  61. package/build/src/tools/swap-routing.js +21 -6
  62. package/build/src/tools/swap-routing.js.map +1 -1
  63. package/build/tests/catalog/domains/defi.js +8 -1
  64. package/build/tests/catalog/domains/defi.js.map +1 -1
  65. package/build/tools/domains/browser.js +5 -3
  66. package/build/tools/domains/browser.js.map +1 -1
  67. package/build/tools/domains/uniswap.js +5 -5
  68. package/build/tools/domains/uniswap.js.map +1 -1
  69. package/build/tools/swap-routing.d.ts +2 -2
  70. package/build/tools/swap-routing.js +21 -6
  71. package/build/tools/swap-routing.js.map +1 -1
  72. 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":"AAAA;;GAEG;AACH,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAA+B,MAAM,MAAM,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,UAAU,EAAsB,MAAM,sBAAsB,CAAC;AACtE,OAAO,EACL,qBAAqB,EACrB,cAAc,GAEf,MAAM,gCAAgC,CAAC;AAUxC,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAC1B,IAAmB,EACnB,KAAoB;IAEpB,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACpC,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC;IAC9D,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC;IAC9D,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;AAC1D,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAAC,OAAsB,EAAE,KAAsB;IACzE,MAAM,KAAK,GAA6B,CAAC,SAAS,CAAC,CAAC;IACpD,MAAM,MAAM,GAA+B,CAAC,OAAO,CAAC,CAAC;IACrD,IAAI,OAAO,GAAG,OAAO,CAAC;IAEtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACxC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAC3B,OAAO,GAAG,GAAG,CAAC;IAChB,CAAC;IAED,OAAO,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAOzC;IACC,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAC7E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,KAAK,GACT,KAAK,CAAC,MAAM,KAAK,CAAC;QAChB,CAAC,CAAC,kBAAkB,CAAC;YACjB,GAAG,EAAE,eAAe;YACpB,YAAY,EAAE,kBAAkB;YAChC,IAAI,EAAE;gBACJ;oBACE,OAAO;oBACP,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC;oBAC1C,GAAG,EAAE,KAAK,CAAC,CAAC,CAAE,CAAC,GAAG;oBAClB,SAAS;oBACT,QAAQ;oBACR,gBAAgB,EAAE,YAAY;oBAC9B,iBAAiB,EAAE,EAAE;iBACtB;aACF;SACF,CAAC;QACJ,CAAC,CAAC,kBAAkB,CAAC;YACjB,GAAG,EAAE,eAAe;YACpB,YAAY,EAAE,YAAY;YAC1B,IAAI,EAAE;gBACJ;oBACE,IAAI,EAAE,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC;oBAClC,SAAS;oBACT,QAAQ;oBACR,gBAAgB,EAAE,YAAY;iBAC/B;aACF;SACF,CAAC,CAAC;IAET,OAAO,kBAAkB,CAAC;QACxB,GAAG,EAAE,eAAe;QACpB,YAAY,EAAE,WAAW;QACzB,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;KAC1B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAAyB,EACzB,OAAsB,EACtB,QAAuB;IAEvB,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACpC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,MAAM,KAAK,GAAsE;QAC/E,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE;KACvD,CAAC;IAEF,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;QAC5B,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;YACnC,SAAS;QACX,CAAC;QAED,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,SAAS;QACX,CAAC;QAED,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC/B,SAAS;YACX,CAAC;YAED,KAAK,MAAM,IAAI,IAAI,cAAc,CAC/B,KAAK,EACL,IAAI,CAAC,KAAsB,EAC3B,QAAyB,CAC1B,EAAE,CAAC;gBACF,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBACxC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;oBACtB,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACxB,SAAS;gBACX,CAAC;gBACD,IAAI,SAAS,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;oBACjC,KAAK,CAAC,IAAI,CAAC;wBACT,KAAK,EAAE,QAAQ;wBACf,KAAK,EAAE,SAAS;wBAChB,OAAO,EAAE,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;qBAC9C,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,KAAyB,EACzB,OAAsB,EACtB,QAAuB;IAEvB,OAAO,gBAAgB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC;AAC3D,CAAC;AAED,KAAK,UAAU,WAAW,CACxB,MAAoB,EACpB,OAAsB,EACtB,KAAsB,EACtB,QAAgB;IAEhB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACvB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC;YAC/C,OAAO,EAAE,UAAU,CAAC,QAAQ;YAC5B,GAAG,EAAE,WAAW;YAChB,YAAY,EAAE,uBAAuB;YACrC,IAAI,EAAE;gBACJ;oBACE,OAAO;oBACP,QAAQ,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC;oBACrC,QAAQ;oBACR,GAAG,EAAE,IAAI,CAAC,GAAG;oBACb,iBAAiB,EAAE,EAAE;iBACtB;aACF;SACF,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC;QAC/C,OAAO,EAAE,UAAU,CAAC,QAAQ;QAC5B,GAAG,EAAE,WAAW;QAChB,YAAY,EAAE,iBAAiB;QAC/B,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC;KAC/C,CAAC,CAAC;IACH,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAoB,EACpB,OAAsB,EACtB,QAAuB,EACvB,QAAgB,EAChB,QAAgB,EAChB,KAA0B;IAE1B,MAAM,SAAS,GAAG,KAAK,IAAI,CAAC,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IACjE,MAAM,cAAc,GAAG,gBAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC;SAClE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;SACnC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEnC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CACrC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACnC,KAAK;QACL,SAAS,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,CAAC;KAC/D,CAAC,CAAC,CACJ,CAAC;IAEF,IAAI,IAAI,GAA4D,IAAI,CAAC;IACzE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;YAChE,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YACpD,IAAI,GAAG;gBACL,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;gBAChC,KAAK,EAAE;oBACL,OAAO;oBACP,QAAQ;oBACR,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK;oBACxB,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM;iBAC/B;aACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,EAAE,GAAG,IAAI,EAAE,WAAW,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;AACpD,CAAC"}
@@ -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 v4 swap estimates and prepares. */
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 v4 registry-token pairs on Celo mainnet (direct pools and 2-hop routes).
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 v4 output for a token pair — no wallet required.
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: "uniswap_v4";
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 v4 swap from `from`, including Permit2 approvals when needed.
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: "uniswap_v4";
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 v4 steps (ERC-20 approve → Permit2 approve → swap when needed).
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