@pafi-dev/trading 0.4.2 → 0.5.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 +111 -50
- package/dist/index.cjs +179 -261
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -128
- package/dist/index.d.ts +105 -128
- package/dist/index.js +176 -251
- package/dist/index.js.map +1 -1
- package/package.json +13 -2
package/dist/index.cjs
CHANGED
|
@@ -21,19 +21,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
PAFI_SUBGRAPH_URL: () => import_core10.PAFI_SUBGRAPH_URL,
|
|
24
|
-
SWAP_EXACT_OUT: () => SWAP_EXACT_OUT,
|
|
25
|
-
SWAP_EXACT_OUT_SINGLE: () => SWAP_EXACT_OUT_SINGLE,
|
|
26
24
|
TradingHandlers: () => TradingHandlers,
|
|
25
|
+
V3_SWAP_EXACT_IN: () => V3_SWAP_EXACT_IN,
|
|
26
|
+
V3_SWAP_EXACT_OUT: () => V3_SWAP_EXACT_OUT,
|
|
27
27
|
buildAllPaths: () => buildAllPaths,
|
|
28
28
|
buildErc20ApprovalCalldata: () => buildErc20ApprovalCalldata,
|
|
29
29
|
buildPermit2ApprovalCalldata: () => buildPermit2ApprovalCalldata,
|
|
30
|
-
buildSwapFromQuote: () => buildSwapFromQuote,
|
|
31
30
|
buildSwapUserOp: () => buildSwapUserOp,
|
|
32
31
|
buildSwapUserOpExactOut: () => buildSwapUserOpExactOut,
|
|
33
32
|
buildUniversalRouterExecuteArgs: () => buildUniversalRouterExecuteArgs,
|
|
34
33
|
buildUniversalRouterExecuteArgsExactOut: () => buildUniversalRouterExecuteArgsExactOut,
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
buildV3SwapInputExactIn: () => buildV3SwapInputExactIn,
|
|
35
|
+
buildV3SwapInputExactOut: () => buildV3SwapInputExactOut,
|
|
37
36
|
checkAllowance: () => checkAllowance,
|
|
38
37
|
combineRoutes: () => combineRoutes,
|
|
39
38
|
fetchPafiPools: () => import_core10.fetchPafiPools,
|
|
@@ -58,7 +57,6 @@ var import_core9 = require("@pafi-dev/core");
|
|
|
58
57
|
|
|
59
58
|
// src/quoting/routes.ts
|
|
60
59
|
var import_core = require("@pafi-dev/core");
|
|
61
|
-
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
62
60
|
function combineRoutes(chainId, pointTokenAddress) {
|
|
63
61
|
const commonPools = import_core.COMMON_POOLS[chainId] ?? [];
|
|
64
62
|
const pointPools = import_core.POINT_TOKEN_POOLS[chainId]?.[pointTokenAddress] ?? [];
|
|
@@ -66,84 +64,77 @@ function combineRoutes(chainId, pointTokenAddress) {
|
|
|
66
64
|
}
|
|
67
65
|
function buildAllPaths(pools, tokenIn, tokenOut, maxHops = 3) {
|
|
68
66
|
const results = [];
|
|
69
|
-
function dfs(currentToken,
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
|
|
67
|
+
function dfs(currentToken, tokens, fees, usedPoolIndices) {
|
|
68
|
+
const hopsSoFar = tokens.length - 1;
|
|
69
|
+
if (hopsSoFar > maxHops) return;
|
|
70
|
+
if (hopsSoFar > 0 && currentToken.toLowerCase() === tokenOut.toLowerCase()) {
|
|
71
|
+
results.push({ tokens: [...tokens], fees: [...fees] });
|
|
73
72
|
return;
|
|
74
73
|
}
|
|
75
74
|
for (let i = 0; i < pools.length; i++) {
|
|
76
75
|
if (usedPoolIndices.has(i)) continue;
|
|
77
76
|
const pool = pools[i];
|
|
78
|
-
const
|
|
79
|
-
const
|
|
77
|
+
const t0 = pool.token0.toLowerCase();
|
|
78
|
+
const t1 = pool.token1.toLowerCase();
|
|
80
79
|
const curr = currentToken.toLowerCase();
|
|
81
80
|
let nextToken = null;
|
|
82
|
-
if (curr ===
|
|
83
|
-
nextToken = pool.
|
|
84
|
-
} else if (curr ===
|
|
85
|
-
nextToken = pool.
|
|
81
|
+
if (curr === t0) {
|
|
82
|
+
nextToken = pool.token1;
|
|
83
|
+
} else if (curr === t1) {
|
|
84
|
+
nextToken = pool.token0;
|
|
86
85
|
}
|
|
87
86
|
if (!nextToken) continue;
|
|
88
|
-
const hop = {
|
|
89
|
-
intermediateCurrency: nextToken,
|
|
90
|
-
fee: pool.fee,
|
|
91
|
-
tickSpacing: pool.tickSpacing,
|
|
92
|
-
hooks: pool.hooks ?? ZERO_ADDRESS,
|
|
93
|
-
hookData: "0x"
|
|
94
|
-
};
|
|
95
87
|
usedPoolIndices.add(i);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
88
|
+
tokens.push(nextToken);
|
|
89
|
+
fees.push(pool.fee);
|
|
90
|
+
dfs(nextToken, tokens, fees, usedPoolIndices);
|
|
91
|
+
tokens.pop();
|
|
92
|
+
fees.pop();
|
|
99
93
|
usedPoolIndices.delete(i);
|
|
100
94
|
}
|
|
101
95
|
}
|
|
102
|
-
dfs(tokenIn, [], /* @__PURE__ */ new Set());
|
|
96
|
+
dfs(tokenIn, [tokenIn], [], /* @__PURE__ */ new Set());
|
|
103
97
|
return results;
|
|
104
98
|
}
|
|
105
99
|
|
|
106
100
|
// src/quoting/quote.ts
|
|
107
101
|
var import_core2 = require("@pafi-dev/core");
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
102
|
+
async function quoteExactInput(client, quoterAddress, path, exactAmount) {
|
|
103
|
+
const pathBytes = (0, import_core2.encodeV3Path)(path);
|
|
104
|
+
const result = await client.readContract({
|
|
111
105
|
address: quoterAddress,
|
|
112
|
-
abi: import_core2.
|
|
106
|
+
abi: import_core2.v3QuoterV2Abi,
|
|
113
107
|
functionName: "quoteExactInput",
|
|
114
|
-
args: [
|
|
108
|
+
args: [pathBytes, exactAmount]
|
|
115
109
|
});
|
|
110
|
+
const [amountOut, , , gasEstimate] = result;
|
|
116
111
|
return { amountOut, gasEstimate, path };
|
|
117
112
|
}
|
|
118
|
-
async function quoteExactInputSingle(client, quoterAddress,
|
|
119
|
-
const
|
|
113
|
+
async function quoteExactInputSingle(client, quoterAddress, tokenIn, tokenOut, fee, exactAmount, sqrtPriceLimitX96 = 0n) {
|
|
114
|
+
const result = await client.readContract({
|
|
120
115
|
address: quoterAddress,
|
|
121
|
-
abi: import_core2.
|
|
116
|
+
abi: import_core2.v3QuoterV2Abi,
|
|
122
117
|
functionName: "quoteExactInputSingle",
|
|
123
118
|
args: [
|
|
124
119
|
{
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
exactAmount,
|
|
128
|
-
|
|
120
|
+
tokenIn,
|
|
121
|
+
tokenOut,
|
|
122
|
+
amountIn: exactAmount,
|
|
123
|
+
fee,
|
|
124
|
+
sqrtPriceLimitX96
|
|
129
125
|
}
|
|
130
126
|
]
|
|
131
127
|
});
|
|
128
|
+
const [amountOut, , , gasEstimate] = result;
|
|
132
129
|
return { amountOut, gasEstimate };
|
|
133
130
|
}
|
|
134
|
-
async function quoteBestRoute(client, quoterAddress,
|
|
131
|
+
async function quoteBestRoute(client, quoterAddress, routes, exactAmount) {
|
|
135
132
|
const results = await client.multicall({
|
|
136
133
|
contracts: routes.map((path) => ({
|
|
137
134
|
address: quoterAddress,
|
|
138
|
-
abi: import_core2.
|
|
135
|
+
abi: import_core2.v3QuoterV2Abi,
|
|
139
136
|
functionName: "quoteExactInput",
|
|
140
|
-
args: [
|
|
141
|
-
{
|
|
142
|
-
exactCurrency,
|
|
143
|
-
path,
|
|
144
|
-
exactAmount
|
|
145
|
-
}
|
|
146
|
-
]
|
|
137
|
+
args: [(0, import_core2.encodeV3Path)(path), exactAmount]
|
|
147
138
|
})),
|
|
148
139
|
allowFailure: true
|
|
149
140
|
});
|
|
@@ -152,7 +143,15 @@ async function quoteBestRoute(client, quoterAddress, exactCurrency, routes, exac
|
|
|
152
143
|
for (let i = 0; i < results.length; i++) {
|
|
153
144
|
const r = results[i];
|
|
154
145
|
if (r.status === "success") {
|
|
155
|
-
const
|
|
146
|
+
const tuple = r.result;
|
|
147
|
+
if (!Array.isArray(tuple) || tuple.length < 4) {
|
|
148
|
+
if (firstFailure === void 0) {
|
|
149
|
+
const len = Array.isArray(tuple) ? tuple.length : "n/a";
|
|
150
|
+
firstFailure = `unexpected QuoterV2 return shape (length=${len})`;
|
|
151
|
+
}
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const [amountOut, , , gasEstimate] = tuple;
|
|
156
155
|
allRoutes.push({ amountOut, gasEstimate, path: routes[i] });
|
|
157
156
|
} else if (firstFailure === void 0) {
|
|
158
157
|
const errMsg = r.error instanceof Error ? r.error.message : String(r.error ?? "unknown");
|
|
@@ -161,7 +160,7 @@ async function quoteBestRoute(client, quoterAddress, exactCurrency, routes, exac
|
|
|
161
160
|
}
|
|
162
161
|
if (allRoutes.length === 0) {
|
|
163
162
|
throw new Error(
|
|
164
|
-
`No valid routes found
|
|
163
|
+
`No valid routes found (${routes.length} candidates probed)` + (firstFailure ? `; first failure: ${firstFailure}` : "")
|
|
165
164
|
);
|
|
166
165
|
}
|
|
167
166
|
const bestRoute = allRoutes.reduce(
|
|
@@ -170,56 +169,54 @@ async function quoteBestRoute(client, quoterAddress, exactCurrency, routes, exac
|
|
|
170
169
|
return { bestRoute, allRoutes };
|
|
171
170
|
}
|
|
172
171
|
async function findBestQuote(client, chainId, tokenIn, tokenOut, exactAmount, pools = [], quoterAddress, maxHops = 3) {
|
|
173
|
-
const quoter = quoterAddress ??
|
|
172
|
+
const quoter = quoterAddress ?? import_core2.QUOTER_V2_ADDRESSES[chainId];
|
|
174
173
|
if (!quoter) {
|
|
175
|
-
throw new Error(`No
|
|
174
|
+
throw new Error(`No V3 QuoterV2 address configured for chain ${chainId}`);
|
|
176
175
|
}
|
|
177
|
-
const commonPools =
|
|
176
|
+
const commonPools = import_core2.COMMON_POOLS[chainId] ?? [];
|
|
178
177
|
const allPools = [...pools, ...commonPools];
|
|
179
178
|
const paths = buildAllPaths(allPools, tokenIn, tokenOut, maxHops);
|
|
180
179
|
if (paths.length === 0) {
|
|
181
180
|
throw new Error(`No paths found from ${tokenIn} to ${tokenOut}`);
|
|
182
181
|
}
|
|
183
|
-
return quoteBestRoute(client, quoter,
|
|
182
|
+
return quoteBestRoute(client, quoter, paths, exactAmount);
|
|
184
183
|
}
|
|
185
|
-
async function quoteExactOutput(client, quoterAddress,
|
|
186
|
-
const
|
|
184
|
+
async function quoteExactOutput(client, quoterAddress, path, exactAmount) {
|
|
185
|
+
const pathBytes = (0, import_core2.encodeV3PathReversed)(path);
|
|
186
|
+
const result = await client.readContract({
|
|
187
187
|
address: quoterAddress,
|
|
188
|
-
abi: import_core2.
|
|
188
|
+
abi: import_core2.v3QuoterV2Abi,
|
|
189
189
|
functionName: "quoteExactOutput",
|
|
190
|
-
args: [
|
|
190
|
+
args: [pathBytes, exactAmount]
|
|
191
191
|
});
|
|
192
|
+
const [amountIn, , , gasEstimate] = result;
|
|
192
193
|
return { amountIn, gasEstimate, path };
|
|
193
194
|
}
|
|
194
|
-
async function quoteExactOutputSingle(client, quoterAddress,
|
|
195
|
-
const
|
|
195
|
+
async function quoteExactOutputSingle(client, quoterAddress, tokenIn, tokenOut, fee, exactAmount, sqrtPriceLimitX96 = 0n) {
|
|
196
|
+
const result = await client.readContract({
|
|
196
197
|
address: quoterAddress,
|
|
197
|
-
abi: import_core2.
|
|
198
|
+
abi: import_core2.v3QuoterV2Abi,
|
|
198
199
|
functionName: "quoteExactOutputSingle",
|
|
199
200
|
args: [
|
|
200
201
|
{
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
exactAmount,
|
|
204
|
-
|
|
202
|
+
tokenIn,
|
|
203
|
+
tokenOut,
|
|
204
|
+
amount: exactAmount,
|
|
205
|
+
fee,
|
|
206
|
+
sqrtPriceLimitX96
|
|
205
207
|
}
|
|
206
208
|
]
|
|
207
209
|
});
|
|
210
|
+
const [amountIn, , , gasEstimate] = result;
|
|
208
211
|
return { amountIn, gasEstimate };
|
|
209
212
|
}
|
|
210
|
-
async function quoteBestRouteExactOut(client, quoterAddress,
|
|
213
|
+
async function quoteBestRouteExactOut(client, quoterAddress, routes, exactAmount) {
|
|
211
214
|
const results = await client.multicall({
|
|
212
215
|
contracts: routes.map((path) => ({
|
|
213
216
|
address: quoterAddress,
|
|
214
|
-
abi: import_core2.
|
|
217
|
+
abi: import_core2.v3QuoterV2Abi,
|
|
215
218
|
functionName: "quoteExactOutput",
|
|
216
|
-
args: [
|
|
217
|
-
{
|
|
218
|
-
exactCurrency,
|
|
219
|
-
path,
|
|
220
|
-
exactAmount
|
|
221
|
-
}
|
|
222
|
-
]
|
|
219
|
+
args: [(0, import_core2.encodeV3PathReversed)(path), exactAmount]
|
|
223
220
|
})),
|
|
224
221
|
allowFailure: true
|
|
225
222
|
});
|
|
@@ -228,7 +225,15 @@ async function quoteBestRouteExactOut(client, quoterAddress, exactCurrency, rout
|
|
|
228
225
|
for (let i = 0; i < results.length; i++) {
|
|
229
226
|
const r = results[i];
|
|
230
227
|
if (r.status === "success") {
|
|
231
|
-
const
|
|
228
|
+
const tuple = r.result;
|
|
229
|
+
if (!Array.isArray(tuple) || tuple.length < 4) {
|
|
230
|
+
if (firstFailure === void 0) {
|
|
231
|
+
const len = Array.isArray(tuple) ? tuple.length : "n/a";
|
|
232
|
+
firstFailure = `unexpected QuoterV2 return shape (length=${len})`;
|
|
233
|
+
}
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const [amountIn, , , gasEstimate] = tuple;
|
|
232
237
|
allRoutes.push({ amountIn, gasEstimate, path: routes[i] });
|
|
233
238
|
} else if (firstFailure === void 0) {
|
|
234
239
|
const errMsg = r.error instanceof Error ? r.error.message : String(r.error ?? "unknown");
|
|
@@ -237,7 +242,7 @@ async function quoteBestRouteExactOut(client, quoterAddress, exactCurrency, rout
|
|
|
237
242
|
}
|
|
238
243
|
if (allRoutes.length === 0) {
|
|
239
244
|
throw new Error(
|
|
240
|
-
`No valid exact-output routes found
|
|
245
|
+
`No valid exact-output routes found (${routes.length} candidates probed)` + (firstFailure ? `; first failure: ${firstFailure}` : "")
|
|
241
246
|
);
|
|
242
247
|
}
|
|
243
248
|
const bestRoute = allRoutes.reduce(
|
|
@@ -246,43 +251,43 @@ async function quoteBestRouteExactOut(client, quoterAddress, exactCurrency, rout
|
|
|
246
251
|
return { bestRoute, allRoutes };
|
|
247
252
|
}
|
|
248
253
|
async function findBestQuoteExactOut(client, chainId, tokenIn, tokenOut, exactAmount, pools = [], quoterAddress, maxHops = 3) {
|
|
249
|
-
const quoter = quoterAddress ??
|
|
254
|
+
const quoter = quoterAddress ?? import_core2.QUOTER_V2_ADDRESSES[chainId];
|
|
250
255
|
if (!quoter) {
|
|
251
|
-
throw new Error(`No
|
|
256
|
+
throw new Error(`No V3 QuoterV2 address configured for chain ${chainId}`);
|
|
252
257
|
}
|
|
253
|
-
const commonPools =
|
|
258
|
+
const commonPools = import_core2.COMMON_POOLS[chainId] ?? [];
|
|
254
259
|
const allPools = [...pools, ...commonPools];
|
|
255
|
-
const paths = buildAllPaths(allPools,
|
|
260
|
+
const paths = buildAllPaths(allPools, tokenIn, tokenOut, maxHops);
|
|
256
261
|
if (paths.length === 0) {
|
|
257
262
|
throw new Error(
|
|
258
|
-
`No exact-output paths found
|
|
263
|
+
`No exact-output paths found from ${tokenIn} to ${tokenOut}`
|
|
259
264
|
);
|
|
260
265
|
}
|
|
261
|
-
return quoteBestRouteExactOut(client, quoter,
|
|
266
|
+
return quoteBestRouteExactOut(client, quoter, paths, exactAmount);
|
|
262
267
|
}
|
|
263
268
|
|
|
264
269
|
// src/swap/approval.ts
|
|
265
270
|
var import_viem = require("viem");
|
|
271
|
+
var import_core3 = require("@pafi-dev/core");
|
|
266
272
|
var import_core4 = require("@pafi-dev/core");
|
|
267
|
-
var import_core5 = require("@pafi-dev/core");
|
|
268
273
|
async function checkAllowance(client, token, owner, spender) {
|
|
269
274
|
return client.readContract({
|
|
270
275
|
address: token,
|
|
271
|
-
abi:
|
|
276
|
+
abi: import_core3.erc20Abi,
|
|
272
277
|
functionName: "allowance",
|
|
273
278
|
args: [owner, spender]
|
|
274
279
|
});
|
|
275
280
|
}
|
|
276
281
|
function buildErc20ApprovalCalldata(spender, amount) {
|
|
277
282
|
return (0, import_viem.encodeFunctionData)({
|
|
278
|
-
abi:
|
|
283
|
+
abi: import_core3.erc20Abi,
|
|
279
284
|
functionName: "approve",
|
|
280
285
|
args: [spender, amount]
|
|
281
286
|
});
|
|
282
287
|
}
|
|
283
288
|
function buildPermit2ApprovalCalldata(token, spender, amount, expiration) {
|
|
284
289
|
return (0, import_viem.encodeFunctionData)({
|
|
285
|
-
abi:
|
|
290
|
+
abi: import_core4.permit2Abi,
|
|
286
291
|
functionName: "approve",
|
|
287
292
|
args: [token, spender, amount, expiration]
|
|
288
293
|
});
|
|
@@ -290,173 +295,79 @@ function buildPermit2ApprovalCalldata(token, spender, amount, expiration) {
|
|
|
290
295
|
|
|
291
296
|
// src/swap/universalRouter.ts
|
|
292
297
|
var import_viem2 = require("viem");
|
|
293
|
-
var
|
|
294
|
-
var
|
|
295
|
-
var
|
|
296
|
-
var SWAP_EXACT_OUT = 9;
|
|
297
|
-
var SETTLE_ALL = 12;
|
|
298
|
-
var TAKE_ALL = 15;
|
|
298
|
+
var import_core5 = require("@pafi-dev/core");
|
|
299
|
+
var V3_SWAP_EXACT_IN = 0;
|
|
300
|
+
var V3_SWAP_EXACT_OUT = 1;
|
|
299
301
|
var UINT128_MAX = 2n ** 128n - 1n;
|
|
300
|
-
var
|
|
301
|
-
{ name: "
|
|
302
|
-
{ name: "
|
|
303
|
-
{ name: "
|
|
304
|
-
{ name: "
|
|
305
|
-
{ name: "
|
|
302
|
+
var V3_EXACT_IN_INPUT_ABI = [
|
|
303
|
+
{ name: "recipient", type: "address" },
|
|
304
|
+
{ name: "amountIn", type: "uint256" },
|
|
305
|
+
{ name: "amountOutMinimum", type: "uint256" },
|
|
306
|
+
{ name: "path", type: "bytes" },
|
|
307
|
+
{ name: "payerIsUser", type: "bool" }
|
|
306
308
|
];
|
|
307
|
-
var
|
|
308
|
-
{ name: "
|
|
309
|
-
{
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
},
|
|
314
|
-
{ name: "amountIn", type: "uint128" },
|
|
315
|
-
{ name: "amountOutMinimum", type: "uint128" }
|
|
309
|
+
var V3_EXACT_OUT_INPUT_ABI = [
|
|
310
|
+
{ name: "recipient", type: "address" },
|
|
311
|
+
{ name: "amountOut", type: "uint256" },
|
|
312
|
+
{ name: "amountInMaximum", type: "uint256" },
|
|
313
|
+
{ name: "path", type: "bytes" },
|
|
314
|
+
{ name: "payerIsUser", type: "bool" }
|
|
316
315
|
];
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
path: path.map((p) => ({
|
|
338
|
-
intermediateCurrency: p.intermediateCurrency,
|
|
339
|
-
fee: BigInt(p.fee),
|
|
340
|
-
tickSpacing: p.tickSpacing,
|
|
341
|
-
hooks: p.hooks,
|
|
342
|
-
hookData: p.hookData
|
|
343
|
-
})),
|
|
344
|
-
amountIn,
|
|
345
|
-
amountOutMinimum: minAmountOut
|
|
346
|
-
}
|
|
347
|
-
]
|
|
348
|
-
);
|
|
349
|
-
const settleParam = (0, import_viem2.encodeAbiParameters)(
|
|
350
|
-
[
|
|
351
|
-
{ name: "currency", type: "address" },
|
|
352
|
-
{ name: "maxAmount", type: "uint256" }
|
|
353
|
-
],
|
|
354
|
-
[currencyIn, amountIn]
|
|
355
|
-
);
|
|
356
|
-
const takeParam = (0, import_viem2.encodeAbiParameters)(
|
|
357
|
-
[
|
|
358
|
-
{ name: "currency", type: "address" },
|
|
359
|
-
{ name: "minAmount", type: "uint256" }
|
|
360
|
-
],
|
|
361
|
-
[outputCurrency, minAmountOut]
|
|
362
|
-
);
|
|
363
|
-
return (0, import_viem2.encodeAbiParameters)(
|
|
364
|
-
[
|
|
365
|
-
{ name: "actions", type: "bytes" },
|
|
366
|
-
{ name: "params", type: "bytes[]" }
|
|
367
|
-
],
|
|
368
|
-
[actions, [swapParam, settleParam, takeParam]]
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
function buildUniversalRouterExecuteArgs(currencyIn, path, amountIn, minAmountOut, outputCurrency) {
|
|
372
|
-
const commands = (0, import_viem2.encodePacked)(["uint8"], [V4_SWAP]);
|
|
373
|
-
const inputs = [
|
|
374
|
-
buildV4SwapInput(currencyIn, path, amountIn, minAmountOut, outputCurrency)
|
|
375
|
-
];
|
|
376
|
-
return { commands, inputs };
|
|
316
|
+
function buildV3SwapInputExactIn(recipient, path, amountIn, minAmountOut) {
|
|
317
|
+
if (amountIn <= 0n || amountIn > UINT128_MAX) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`buildV3SwapInputExactIn: amountIn (${amountIn}) must be in (0, 2^128-1]`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
if (minAmountOut < 0n) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`buildV3SwapInputExactIn: minAmountOut (${minAmountOut}) must be non-negative`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
const pathBytes = (0, import_core5.encodeV3Path)(path);
|
|
328
|
+
return (0, import_viem2.encodeAbiParameters)(V3_EXACT_IN_INPUT_ABI, [
|
|
329
|
+
recipient,
|
|
330
|
+
amountIn,
|
|
331
|
+
minAmountOut,
|
|
332
|
+
pathBytes,
|
|
333
|
+
true
|
|
334
|
+
// payerIsUser — router pulls via Permit2 (the SDK's only flow)
|
|
335
|
+
]);
|
|
377
336
|
}
|
|
378
|
-
function
|
|
337
|
+
function buildV3SwapInputExactOut(recipient, path, amountOut, maxAmountIn) {
|
|
379
338
|
if (amountOut <= 0n || amountOut > UINT128_MAX) {
|
|
380
339
|
throw new Error(
|
|
381
|
-
`
|
|
340
|
+
`buildV3SwapInputExactOut: amountOut (${amountOut}) must be in (0, 2^128-1]`
|
|
382
341
|
);
|
|
383
342
|
}
|
|
384
343
|
if (maxAmountIn <= 0n || maxAmountIn > UINT128_MAX) {
|
|
385
344
|
throw new Error(
|
|
386
|
-
`
|
|
345
|
+
`buildV3SwapInputExactOut: maxAmountIn (${maxAmountIn}) must be in (0, 2^128-1]`
|
|
387
346
|
);
|
|
388
347
|
}
|
|
389
|
-
const
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
path: path.map((p) => ({
|
|
399
|
-
intermediateCurrency: p.intermediateCurrency,
|
|
400
|
-
fee: BigInt(p.fee),
|
|
401
|
-
tickSpacing: p.tickSpacing,
|
|
402
|
-
hooks: p.hooks,
|
|
403
|
-
hookData: p.hookData
|
|
404
|
-
})),
|
|
405
|
-
amountOut,
|
|
406
|
-
amountInMaximum: maxAmountIn
|
|
407
|
-
}
|
|
408
|
-
]
|
|
409
|
-
);
|
|
410
|
-
const settleParam = (0, import_viem2.encodeAbiParameters)(
|
|
411
|
-
[
|
|
412
|
-
{ name: "currency", type: "address" },
|
|
413
|
-
{ name: "maxAmount", type: "uint256" }
|
|
414
|
-
],
|
|
415
|
-
[inputCurrency, maxAmountIn]
|
|
416
|
-
);
|
|
417
|
-
const takeParam = (0, import_viem2.encodeAbiParameters)(
|
|
418
|
-
[
|
|
419
|
-
{ name: "currency", type: "address" },
|
|
420
|
-
{ name: "minAmount", type: "uint256" }
|
|
421
|
-
],
|
|
422
|
-
[currencyOut, amountOut]
|
|
423
|
-
);
|
|
424
|
-
return (0, import_viem2.encodeAbiParameters)(
|
|
425
|
-
[
|
|
426
|
-
{ name: "actions", type: "bytes" },
|
|
427
|
-
{ name: "params", type: "bytes[]" }
|
|
428
|
-
],
|
|
429
|
-
[actions, [swapParam, settleParam, takeParam]]
|
|
430
|
-
);
|
|
348
|
+
const pathBytes = (0, import_core5.encodeV3PathReversed)(path);
|
|
349
|
+
return (0, import_viem2.encodeAbiParameters)(V3_EXACT_OUT_INPUT_ABI, [
|
|
350
|
+
recipient,
|
|
351
|
+
amountOut,
|
|
352
|
+
maxAmountIn,
|
|
353
|
+
pathBytes,
|
|
354
|
+
true
|
|
355
|
+
// payerIsUser
|
|
356
|
+
]);
|
|
431
357
|
}
|
|
432
|
-
function
|
|
433
|
-
const commands = (0, import_viem2.encodePacked)(["uint8"], [
|
|
358
|
+
function buildUniversalRouterExecuteArgs(recipient, path, amountIn, minAmountOut) {
|
|
359
|
+
const commands = (0, import_viem2.encodePacked)(["uint8"], [V3_SWAP_EXACT_IN]);
|
|
434
360
|
const inputs = [
|
|
435
|
-
|
|
436
|
-
currencyOut,
|
|
437
|
-
path,
|
|
438
|
-
amountOut,
|
|
439
|
-
maxAmountIn,
|
|
440
|
-
inputCurrency
|
|
441
|
-
)
|
|
361
|
+
buildV3SwapInputExactIn(recipient, path, amountIn, minAmountOut)
|
|
442
362
|
];
|
|
443
363
|
return { commands, inputs };
|
|
444
364
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
return buildUniversalRouterExecuteArgs(
|
|
454
|
-
params.currencyIn,
|
|
455
|
-
params.quote.path,
|
|
456
|
-
params.amountIn,
|
|
457
|
-
params.minAmountOut,
|
|
458
|
-
params.currencyOut
|
|
459
|
-
);
|
|
365
|
+
function buildUniversalRouterExecuteArgsExactOut(recipient, path, amountOut, maxAmountIn) {
|
|
366
|
+
const commands = (0, import_viem2.encodePacked)(["uint8"], [V3_SWAP_EXACT_OUT]);
|
|
367
|
+
const inputs = [
|
|
368
|
+
buildV3SwapInputExactOut(recipient, path, amountOut, maxAmountIn)
|
|
369
|
+
];
|
|
370
|
+
return { commands, inputs };
|
|
460
371
|
}
|
|
461
372
|
|
|
462
373
|
// src/swap/simulate.ts
|
|
@@ -496,9 +407,14 @@ function buildSwapUserOp(params) {
|
|
|
496
407
|
"buildSwapUserOp: minAmountOut must be >= gasFeeAmountOutput so the post-swap fee transfer cannot revert"
|
|
497
408
|
);
|
|
498
409
|
}
|
|
499
|
-
if (params.swapPath.length
|
|
410
|
+
if (params.swapPath.tokens.length < 2 || params.swapPath.fees.length < 1) {
|
|
500
411
|
throw new Error(
|
|
501
|
-
"buildSwapUserOp: swapPath must contain at least one
|
|
412
|
+
"buildSwapUserOp: swapPath must contain at least one hop (>=2 tokens, >=1 fee)"
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
if (params.swapPath.tokens.length !== params.swapPath.fees.length + 1) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
"buildSwapUserOp: swapPath.tokens.length must equal swapPath.fees.length + 1"
|
|
502
418
|
);
|
|
503
419
|
}
|
|
504
420
|
const PERMIT2_EXPIRATION_MAX = 2n ** 48n - 1n;
|
|
@@ -508,11 +424,10 @@ function buildSwapUserOp(params) {
|
|
|
508
424
|
);
|
|
509
425
|
}
|
|
510
426
|
const { commands, inputs } = buildUniversalRouterExecuteArgs(
|
|
511
|
-
params.
|
|
427
|
+
params.userAddress,
|
|
512
428
|
params.swapPath,
|
|
513
429
|
params.amountIn,
|
|
514
|
-
params.minAmountOut
|
|
515
|
-
params.outputTokenAddress
|
|
430
|
+
params.minAmountOut
|
|
516
431
|
);
|
|
517
432
|
const swapCallData = (0, import_viem3.encodeFunctionData)({
|
|
518
433
|
abi: import_core8.universalRouterAbi,
|
|
@@ -562,9 +477,14 @@ function buildSwapUserOpExactOut(params) {
|
|
|
562
477
|
"buildSwapUserOpExactOut: gasFeeAmountInput must be non-negative"
|
|
563
478
|
);
|
|
564
479
|
}
|
|
565
|
-
if (params.swapPath.length
|
|
480
|
+
if (params.swapPath.tokens.length < 2 || params.swapPath.fees.length < 1) {
|
|
566
481
|
throw new Error(
|
|
567
|
-
"buildSwapUserOpExactOut: swapPath must contain at least one
|
|
482
|
+
"buildSwapUserOpExactOut: swapPath must contain at least one hop (>=2 tokens, >=1 fee)"
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
if (params.swapPath.tokens.length !== params.swapPath.fees.length + 1) {
|
|
486
|
+
throw new Error(
|
|
487
|
+
"buildSwapUserOpExactOut: swapPath.tokens.length must equal swapPath.fees.length + 1"
|
|
568
488
|
);
|
|
569
489
|
}
|
|
570
490
|
const PERMIT2_EXPIRATION_MAX = 2n ** 48n - 1n;
|
|
@@ -574,11 +494,10 @@ function buildSwapUserOpExactOut(params) {
|
|
|
574
494
|
);
|
|
575
495
|
}
|
|
576
496
|
const { commands, inputs } = buildUniversalRouterExecuteArgsExactOut(
|
|
577
|
-
params.
|
|
497
|
+
params.userAddress,
|
|
578
498
|
params.swapPath,
|
|
579
499
|
params.amountOut,
|
|
580
|
-
params.maxAmountIn
|
|
581
|
-
params.inputTokenAddress
|
|
500
|
+
params.maxAmountIn
|
|
582
501
|
);
|
|
583
502
|
const swapCallData = (0, import_viem3.encodeFunctionData)({
|
|
584
503
|
abi: import_core8.universalRouterAbi,
|
|
@@ -639,7 +558,7 @@ var TradingHandlers = class {
|
|
|
639
558
|
// GET /quote
|
|
640
559
|
// =========================================================================
|
|
641
560
|
/**
|
|
642
|
-
* Quote exact-input PT → USDT via Uniswap
|
|
561
|
+
* Quote exact-input PT → USDT via Uniswap V3 QuoterV2.
|
|
643
562
|
*
|
|
644
563
|
* Uses multicall to batch all candidate routes into a single RPC call.
|
|
645
564
|
* Returns `quoteError: "QUOTE_UNAVAILABLE"` when no pool/path exists
|
|
@@ -779,7 +698,7 @@ var TradingHandlers = class {
|
|
|
779
698
|
request.chainId,
|
|
780
699
|
outputTokenAddress
|
|
781
700
|
).catch(() => 0n);
|
|
782
|
-
const hops = quoteResult.bestRoute.path.length;
|
|
701
|
+
const hops = quoteResult.bestRoute.path.fees.length;
|
|
783
702
|
const slippageBps = request.slippageBps ?? (hops > 1 ? 100 : 50);
|
|
784
703
|
const estimatedOutputAmount = quoteResult.bestRoute.amountOut;
|
|
785
704
|
const minAmountOut = estimatedOutputAmount * BigInt(1e4 - slippageBps) / 10000n;
|
|
@@ -832,11 +751,11 @@ var TradingHandlers = class {
|
|
|
832
751
|
};
|
|
833
752
|
}
|
|
834
753
|
// =========================================================================
|
|
835
|
-
// GET /quote/exact-out —
|
|
754
|
+
// GET /quote/exact-out — V3 exact-output quote
|
|
836
755
|
// =========================================================================
|
|
837
756
|
/**
|
|
838
757
|
* Quote the input required to receive `request.amount` of the output
|
|
839
|
-
* token via Uniswap
|
|
758
|
+
* token via Uniswap V3 QuoterV2. Input-side operator fee is auto-quoted, so
|
|
840
759
|
* `inputGross = estimatedInputAmount + feeAmountInput` is the total
|
|
841
760
|
* the user must hold.
|
|
842
761
|
*
|
|
@@ -897,10 +816,10 @@ var TradingHandlers = class {
|
|
|
897
816
|
}
|
|
898
817
|
}
|
|
899
818
|
// =========================================================================
|
|
900
|
-
// POST /swap/exact-out —
|
|
819
|
+
// POST /swap/exact-out — V3 exact-output swap UserOp
|
|
901
820
|
// =========================================================================
|
|
902
821
|
/**
|
|
903
|
-
* Build a
|
|
822
|
+
* Build a V3 exact-output swap UserOp.
|
|
904
823
|
*
|
|
905
824
|
* Quotes the best exact-output route, applies slippage as a CEILING on
|
|
906
825
|
* `maxAmountIn` (so the cap is never silently tightened by floor
|
|
@@ -909,7 +828,7 @@ var TradingHandlers = class {
|
|
|
909
828
|
* inputToken.transfer(feeRecipient, gasFeeAmountInput) [if > 0]
|
|
910
829
|
* → input.approve(Permit2, maxAmountIn)
|
|
911
830
|
* → Permit2.approve(router, maxAmountIn)
|
|
912
|
-
* → UniversalRouter.execute (
|
|
831
|
+
* → UniversalRouter.execute (V3_SWAP_EXACT_OUT)
|
|
913
832
|
*
|
|
914
833
|
* Operator fee is INPUT-token-side (charged before swap) so the user
|
|
915
834
|
* receives exactly `request.amount` of output.
|
|
@@ -979,7 +898,7 @@ var TradingHandlers = class {
|
|
|
979
898
|
request.chainId,
|
|
980
899
|
inputTokenAddress
|
|
981
900
|
).catch(() => 0n);
|
|
982
|
-
const hops = quoteResult.bestRoute.path.length;
|
|
901
|
+
const hops = quoteResult.bestRoute.path.fees.length;
|
|
983
902
|
const slippageBps = request.slippageBps ?? (hops > 1 ? 100 : 50);
|
|
984
903
|
const estimatedInputAmount = quoteResult.bestRoute.amountIn;
|
|
985
904
|
const slippageNumerator = estimatedInputAmount * BigInt(1e4 + slippageBps);
|
|
@@ -1302,7 +1221,7 @@ async function swapDirect(params) {
|
|
|
1302
1221
|
`swapDirect: no swap path found from ${params.inputTokenAddress} to ${params.outputTokenAddress} (cause: ${cause})`
|
|
1303
1222
|
);
|
|
1304
1223
|
}
|
|
1305
|
-
const hops = quoteResult.bestRoute.path.length;
|
|
1224
|
+
const hops = quoteResult.bestRoute.path.fees.length;
|
|
1306
1225
|
const slippageBps = params.slippageBps ?? (hops > 1 ? 100 : 50);
|
|
1307
1226
|
const estimatedOutputAmount = quoteResult.bestRoute.amountOut;
|
|
1308
1227
|
const minAmountOut = estimatedOutputAmount * BigInt(1e4 - slippageBps) / 10000n;
|
|
@@ -1417,7 +1336,7 @@ async function swapDirectExactOut(params) {
|
|
|
1417
1336
|
`swapDirectExactOut: no swap path found from ${params.inputTokenAddress} to ${params.outputTokenAddress} (cause: ${cause})`
|
|
1418
1337
|
);
|
|
1419
1338
|
}
|
|
1420
|
-
const hops = quoteResult.bestRoute.path.length;
|
|
1339
|
+
const hops = quoteResult.bestRoute.path.fees.length;
|
|
1421
1340
|
const slippageBps = params.slippageBps ?? (hops > 1 ? 100 : 50);
|
|
1422
1341
|
const estimatedInputAmount = quoteResult.bestRoute.amountIn;
|
|
1423
1342
|
const slippageNumerator = estimatedInputAmount * BigInt(1e4 + slippageBps);
|
|
@@ -1604,19 +1523,18 @@ async function perpDepositDirect(params) {
|
|
|
1604
1523
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1605
1524
|
0 && (module.exports = {
|
|
1606
1525
|
PAFI_SUBGRAPH_URL,
|
|
1607
|
-
SWAP_EXACT_OUT,
|
|
1608
|
-
SWAP_EXACT_OUT_SINGLE,
|
|
1609
1526
|
TradingHandlers,
|
|
1527
|
+
V3_SWAP_EXACT_IN,
|
|
1528
|
+
V3_SWAP_EXACT_OUT,
|
|
1610
1529
|
buildAllPaths,
|
|
1611
1530
|
buildErc20ApprovalCalldata,
|
|
1612
1531
|
buildPermit2ApprovalCalldata,
|
|
1613
|
-
buildSwapFromQuote,
|
|
1614
1532
|
buildSwapUserOp,
|
|
1615
1533
|
buildSwapUserOpExactOut,
|
|
1616
1534
|
buildUniversalRouterExecuteArgs,
|
|
1617
1535
|
buildUniversalRouterExecuteArgsExactOut,
|
|
1618
|
-
|
|
1619
|
-
|
|
1536
|
+
buildV3SwapInputExactIn,
|
|
1537
|
+
buildV3SwapInputExactOut,
|
|
1620
1538
|
checkAllowance,
|
|
1621
1539
|
combineRoutes,
|
|
1622
1540
|
fetchPafiPools,
|