@peachprojects/aggregator-sdk 0.1.2 → 0.1.3
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 +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.d.ts +44 -12
- package/dist/index.mjs +622 -518
- package/package.json +3 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import { ethers as
|
|
2
|
-
const q = "https://api.peach.ag",
|
|
3
|
-
function
|
|
4
|
-
return
|
|
1
|
+
import { ethers as f } from "ethers";
|
|
2
|
+
const q = "https://api.peach.ag", me = 50, k = 10000n, V = 500, Ae = 5e3, T = 1200, K = 6e4, U = [50, 100, 200, 400, 800, 1200], z = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
3
|
+
function y(h) {
|
|
4
|
+
return h.toLowerCase() === z.toLowerCase();
|
|
5
5
|
}
|
|
6
|
-
const
|
|
7
|
-
var
|
|
8
|
-
const
|
|
6
|
+
const ve = 4001;
|
|
7
|
+
var l = /* @__PURE__ */ ((h) => (h.PancakeV2 = "PancakeV2", h.PancakeV3 = "PancakeV3", h.PancakeInfinityCl = "Pancake_Infinity_Cl", h.PancakeInfinityLb = "Pancake_Infinity_Lb", h.UniswapV3 = "UniswapV3", h.UniswapV4 = "UniswapV4", h.Dodo = "Dodo", h.ThenaV3 = "ThenaV3", h.ThenaFusion = "Thena_Fusion", h.NomiswapStable = "Nomiswap_Stable", h.Biswap = "Biswap", h.Apeswap = "Apeswap", h.BabyDogeSwap = "BabyDogeSwap", h.BabySwap = "BabySwap", h.PancakeStable = "Pancake_Stable", h.ListaStable = "Lista_Stable", h.SquadSwapV3 = "SquadSwap_V3", h.SquadSwapV2 = "SquadSwap_V2", h.Wombat = "Wombat", h.SushiSwapV2 = "SushiSwap_V2", h.SushiSwapV3 = "SushiSwap_V3", h))(l || {});
|
|
8
|
+
const Se = {
|
|
9
9
|
chainId: 56,
|
|
10
10
|
rpcUrl: "https://bsc-dataseed.binance.org",
|
|
11
11
|
weth: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
|
|
12
12
|
// WBNB
|
|
13
13
|
adapters: []
|
|
14
|
-
},
|
|
14
|
+
}, ge = {
|
|
15
15
|
chainId: 97,
|
|
16
16
|
rpcUrl: "https://bsc-testnet-rpc.publicnode.com",
|
|
17
17
|
weth: "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd",
|
|
18
18
|
// WBNB Testnet
|
|
19
19
|
adapters: []
|
|
20
20
|
};
|
|
21
|
-
class
|
|
21
|
+
class B extends Error {
|
|
22
22
|
constructor(e) {
|
|
23
23
|
super(e), this.name = "CustomFeeError";
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
class
|
|
27
|
-
constructor(e, t,
|
|
28
|
-
super(e), this.name = "ExecuteTimeoutError", this.stage = t, this.txHash =
|
|
26
|
+
class Y extends Error {
|
|
27
|
+
constructor(e, t, n) {
|
|
28
|
+
super(e), this.name = "ExecuteTimeoutError", this.stage = t, this.txHash = n;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
const
|
|
31
|
+
const P = {
|
|
32
32
|
/** Default route search depth */
|
|
33
33
|
depth: 3,
|
|
34
34
|
/** Default trade split count */
|
|
35
35
|
splitCount: 20,
|
|
36
36
|
/** Default DEX providers */
|
|
37
|
-
providers: ["PANCAKEV2", "PANCAKEV3", "PANCAKE_INFINITY_CL", "UNISWAPV3", "UNISWAPV4", "DODO", "
|
|
37
|
+
providers: ["PANCAKEV2", "PANCAKEV3", "PANCAKE_INFINITY_CL", "PANCAKE_INFINITY_LB", "UNISWAPV3", "UNISWAPV4", "DODO", "THENAV3", "NOMISWAP_STABLE", "BISWAP", "APESWAP", "BABYDOGESWAP", "BABYSWAP", "PANCAKE_STABLE", "LISTA_STABLE", "SQUADSWAP_V3", "SQUADSWAP_V2", "WOMBAT", "THENA_FUSION", "SUSHISWAP_V2", "SUSHISWAP_V3"],
|
|
38
38
|
/** Default client version for V3 API */
|
|
39
39
|
clientVersion: 1001500
|
|
40
|
-
},
|
|
41
|
-
function
|
|
40
|
+
}, J = 1e4;
|
|
41
|
+
function X(h) {
|
|
42
42
|
const e = {};
|
|
43
|
-
return
|
|
44
|
-
e[
|
|
43
|
+
return h.forEach((t, n) => {
|
|
44
|
+
e[n] = t;
|
|
45
45
|
}), e;
|
|
46
46
|
}
|
|
47
|
-
class
|
|
47
|
+
class ee {
|
|
48
48
|
constructor(e = {}) {
|
|
49
|
-
this.baseUrl = e.baseUrl || q, this.timeout = e.timeout ||
|
|
49
|
+
this.baseUrl = e.baseUrl || q, this.timeout = e.timeout || J, this.extraHeaders = e.headers ? { ...e.headers } : {};
|
|
50
50
|
}
|
|
51
51
|
async findRoutes(e) {
|
|
52
52
|
const {
|
|
53
53
|
from: t,
|
|
54
|
-
target:
|
|
55
|
-
amount:
|
|
56
|
-
byAmountIn:
|
|
57
|
-
depth:
|
|
58
|
-
splitCount: a =
|
|
59
|
-
providers: i =
|
|
54
|
+
target: n,
|
|
55
|
+
amount: o,
|
|
56
|
+
byAmountIn: r = !0,
|
|
57
|
+
depth: s = P.depth,
|
|
58
|
+
splitCount: a = P.splitCount,
|
|
59
|
+
providers: i = P.providers,
|
|
60
60
|
includeResponseHeaders: c = !1
|
|
61
61
|
} = e, u = new URLSearchParams({
|
|
62
62
|
from: t,
|
|
63
|
-
target:
|
|
64
|
-
amount:
|
|
65
|
-
by_amount_in:
|
|
66
|
-
depth:
|
|
63
|
+
target: n,
|
|
64
|
+
amount: o.toString(),
|
|
65
|
+
by_amount_in: r.toString(),
|
|
66
|
+
depth: s.toString(),
|
|
67
67
|
split_count: a.toString(),
|
|
68
68
|
providers: i.join(","),
|
|
69
|
-
v:
|
|
70
|
-
}), d = `${this.baseUrl}/router/find_routes?${u}`, p = new AbortController(),
|
|
69
|
+
v: P.clientVersion.toString()
|
|
70
|
+
}), d = `${this.baseUrl}/router/find_routes?${u}`, p = new AbortController(), A = setTimeout(() => p.abort(), this.timeout);
|
|
71
71
|
try {
|
|
72
72
|
const m = await fetch(d, {
|
|
73
73
|
method: "GET",
|
|
@@ -77,31 +77,31 @@ class X {
|
|
|
77
77
|
},
|
|
78
78
|
signal: p.signal
|
|
79
79
|
});
|
|
80
|
-
if (clearTimeout(
|
|
81
|
-
throw new
|
|
80
|
+
if (clearTimeout(A), !m.ok)
|
|
81
|
+
throw new g(
|
|
82
82
|
`API request failed: ${m.status} ${m.statusText}`,
|
|
83
83
|
m.status
|
|
84
84
|
);
|
|
85
85
|
const v = await m.json();
|
|
86
86
|
if (v.code !== 200)
|
|
87
|
-
throw new
|
|
87
|
+
throw new g(v.msg || "Route not found", v.code);
|
|
88
88
|
if (!v.data || !v.data.paths || v.data.paths.length === 0)
|
|
89
|
-
throw new
|
|
89
|
+
throw new g("No routes found", 404);
|
|
90
90
|
return c ? {
|
|
91
91
|
data: v.data,
|
|
92
|
-
responseHeaders:
|
|
92
|
+
responseHeaders: X(m.headers)
|
|
93
93
|
} : v.data;
|
|
94
94
|
} catch (m) {
|
|
95
|
-
throw clearTimeout(
|
|
95
|
+
throw clearTimeout(A), m instanceof g ? m : m instanceof Error ? m.name === "AbortError" ? new g("API request timeout", 408) : new g(`API request failed: ${m.message}`, 0) : new g("Unknown API error", 0);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
99
|
* Get service status including available providers
|
|
100
100
|
*/
|
|
101
101
|
async getStatus() {
|
|
102
|
-
const e = `${this.baseUrl}/router/status`, t = new AbortController(),
|
|
102
|
+
const e = `${this.baseUrl}/router/status`, t = new AbortController(), n = setTimeout(() => t.abort(), this.timeout);
|
|
103
103
|
try {
|
|
104
|
-
const
|
|
104
|
+
const o = await fetch(e, {
|
|
105
105
|
method: "GET",
|
|
106
106
|
headers: {
|
|
107
107
|
Accept: "application/json",
|
|
@@ -109,17 +109,17 @@ class X {
|
|
|
109
109
|
},
|
|
110
110
|
signal: t.signal
|
|
111
111
|
});
|
|
112
|
-
if (clearTimeout(
|
|
113
|
-
throw new
|
|
114
|
-
`API request failed: ${
|
|
115
|
-
|
|
112
|
+
if (clearTimeout(n), !o.ok)
|
|
113
|
+
throw new g(
|
|
114
|
+
`API request failed: ${o.status} ${o.statusText}`,
|
|
115
|
+
o.status
|
|
116
116
|
);
|
|
117
|
-
const
|
|
118
|
-
if (
|
|
119
|
-
throw new
|
|
120
|
-
return
|
|
121
|
-
} catch (
|
|
122
|
-
throw clearTimeout(
|
|
117
|
+
const r = await o.json();
|
|
118
|
+
if (r.code !== 200)
|
|
119
|
+
throw new g(r.msg || "Failed to get status", r.code);
|
|
120
|
+
return r.data;
|
|
121
|
+
} catch (o) {
|
|
122
|
+
throw clearTimeout(n), o instanceof g ? o : o instanceof Error ? o.name === "AbortError" ? new g("API request timeout", 408) : new g(`API request failed: ${o.message}`, 0) : new g("Unknown API error", 0);
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
/**
|
|
@@ -141,22 +141,22 @@ class X {
|
|
|
141
141
|
return this.baseUrl;
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
|
-
class
|
|
144
|
+
class g extends Error {
|
|
145
145
|
constructor(e, t) {
|
|
146
146
|
super(e), this.name = "ApiError", this.code = t;
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
-
async function
|
|
149
|
+
async function $(h, e = K) {
|
|
150
150
|
if (e <= 0)
|
|
151
|
-
return
|
|
151
|
+
return h;
|
|
152
152
|
let t;
|
|
153
153
|
try {
|
|
154
154
|
return await Promise.race([
|
|
155
|
-
|
|
156
|
-
new Promise((
|
|
155
|
+
h,
|
|
156
|
+
new Promise((n, o) => {
|
|
157
157
|
t = setTimeout(() => {
|
|
158
|
-
|
|
159
|
-
new
|
|
158
|
+
o(
|
|
159
|
+
new Y(
|
|
160
160
|
`Wallet did not settle sendTransaction within ${e}ms.`,
|
|
161
161
|
"wallet_send"
|
|
162
162
|
)
|
|
@@ -168,7 +168,7 @@ async function U(w, e = W) {
|
|
|
168
168
|
t && clearTimeout(t);
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
|
-
const
|
|
171
|
+
const R = [
|
|
172
172
|
"function swap((address srcToken, address dstToken, uint256 amountIn, uint256 amountOutMin, (address adapter, address pool, address tokenIn, address tokenOut, uint256 amountIn, bytes extraData)[] steps, address[] intermediateTokens, uint256 deadline, bytes32 quoteId, uint256 expectAmountOut, address feeReceiver, uint16 feeBps) params) external returns (uint256 amountOut)",
|
|
173
173
|
"function swapETH((address srcToken, address dstToken, uint256 amountIn, uint256 amountOutMin, (address adapter, address pool, address tokenIn, address tokenOut, uint256 amountIn, bytes extraData)[] steps, address[] intermediateTokens, uint256 deadline, bytes32 quoteId, uint256 expectAmountOut, address feeReceiver, uint16 feeBps) params) external payable returns (uint256 amountOut)",
|
|
174
174
|
"function isAdapterRegistered(address adapter) external view returns (bool)",
|
|
@@ -181,40 +181,48 @@ const B = [
|
|
|
181
181
|
"function maxProtocolCutBps() external view returns (uint16)",
|
|
182
182
|
"function protocolFeeReceiver() external view returns (address)",
|
|
183
183
|
"function protocolCutBps() external view returns (uint16)"
|
|
184
|
-
],
|
|
184
|
+
], b = [
|
|
185
185
|
"function approve(address spender, uint256 amount) external returns (bool)",
|
|
186
186
|
"function allowance(address owner, address spender) external view returns (uint256)",
|
|
187
187
|
"function balanceOf(address account) external view returns (uint256)",
|
|
188
188
|
"function decimals() external view returns (uint8)",
|
|
189
189
|
"function symbol() external view returns (string)"
|
|
190
|
-
],
|
|
191
|
-
PANCAKEV3:
|
|
192
|
-
PANCAKEV2:
|
|
193
|
-
PANCAKE_INFINITY_CL:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
190
|
+
], te = "0xa0FfB9c1CE1Fe56963B0321B32E7A0302114058b", ne = "0xC697d2898e0D09264376196696c51D7aBbbAA4a9", oe = "0x28e2ea090877bf75740558f6bfb36a5ffee9e9df", M = {
|
|
191
|
+
PANCAKEV3: l.PancakeV3,
|
|
192
|
+
PANCAKEV2: l.PancakeV2,
|
|
193
|
+
PANCAKE_INFINITY_CL: l.PancakeInfinityCl,
|
|
194
|
+
PANCAKE_INFINITY_LB: l.PancakeInfinityLb,
|
|
195
|
+
UNISWAPV3: l.UniswapV3,
|
|
196
|
+
UNISWAPV4: l.UniswapV4,
|
|
197
|
+
DODO: l.Dodo,
|
|
198
|
+
THENAV3: l.ThenaV3,
|
|
199
|
+
NOMISWAP_STABLE: l.NomiswapStable,
|
|
200
|
+
BISWAP: l.Biswap,
|
|
201
|
+
APESWAP: l.Apeswap,
|
|
202
|
+
BABYDOGESWAP: l.BabyDogeSwap,
|
|
203
|
+
BABYSWAP: l.BabySwap,
|
|
204
|
+
PANCAKE_STABLE: l.PancakeStable,
|
|
205
|
+
LISTA_STABLE: l.ListaStable,
|
|
206
|
+
SQUADSWAP_V3: l.SquadSwapV3,
|
|
207
|
+
SQUADSWAP_V2: l.SquadSwapV2,
|
|
208
|
+
WOMBAT: l.Wombat,
|
|
209
|
+
THENA_FUSION: l.ThenaFusion,
|
|
210
|
+
SUSHISWAP_V2: l.SushiSwapV2,
|
|
211
|
+
SUSHISWAP_V3: l.SushiSwapV3
|
|
212
|
+
}, _ = class _ {
|
|
213
|
+
constructor(e, t, n) {
|
|
214
|
+
this.config = e, this.provider = t || new f.JsonRpcProvider(e.rpcUrl), this.routerContract = new f.Contract(
|
|
215
|
+
e.routerAddress || f.ZeroAddress,
|
|
216
|
+
R,
|
|
209
217
|
this.provider
|
|
210
|
-
), this.apiClient = new
|
|
218
|
+
), this.apiClient = new ee(n?.api);
|
|
211
219
|
}
|
|
212
220
|
/**
|
|
213
221
|
* Get the effective router address for a quote.
|
|
214
222
|
*/
|
|
215
223
|
getRouterAddress(e) {
|
|
216
224
|
const t = e.routerAddress || this.config.routerAddress;
|
|
217
|
-
if (!t || t ===
|
|
225
|
+
if (!t || t === f.ZeroAddress)
|
|
218
226
|
throw new Error("No router address available. Provide routerAddress in config or use API-based getQuote.");
|
|
219
227
|
return t;
|
|
220
228
|
}
|
|
@@ -224,25 +232,25 @@ class K {
|
|
|
224
232
|
applySlippage(e, t) {
|
|
225
233
|
if (t < 0 || t > 1e4)
|
|
226
234
|
throw new Error("slippageBps must be between 0 and 10000");
|
|
227
|
-
const
|
|
228
|
-
return { ...e, amountOutMin:
|
|
235
|
+
const n = e.amountOutMin * (k - BigInt(t)) / k;
|
|
236
|
+
return { ...e, amountOutMin: n };
|
|
229
237
|
}
|
|
230
238
|
/**
|
|
231
239
|
* Build transaction requests for an approval (if needed) and the swap itself.
|
|
232
240
|
*/
|
|
233
|
-
async swap(e, t,
|
|
234
|
-
const
|
|
241
|
+
async swap(e, t, n) {
|
|
242
|
+
const o = this.getRouterAddress(e), { tx: r, method: s } = this.buildSwapTransactionRequest(e, n);
|
|
235
243
|
let a;
|
|
236
244
|
return e.srcNative || (a = await this.buildApprovalRequest(
|
|
237
245
|
e.srcToken,
|
|
238
246
|
t,
|
|
239
247
|
e.amountIn,
|
|
240
|
-
|
|
241
|
-
|
|
248
|
+
o,
|
|
249
|
+
n
|
|
242
250
|
)), {
|
|
243
|
-
routerAddress:
|
|
244
|
-
method:
|
|
245
|
-
tx:
|
|
251
|
+
routerAddress: o,
|
|
252
|
+
method: s,
|
|
253
|
+
tx: r,
|
|
246
254
|
approval: a
|
|
247
255
|
};
|
|
248
256
|
}
|
|
@@ -251,21 +259,21 @@ class K {
|
|
|
251
259
|
*
|
|
252
260
|
* @deprecated Prefer swap(), then send the returned tx request with your wallet/client.
|
|
253
261
|
*/
|
|
254
|
-
async execute(e, t,
|
|
255
|
-
const
|
|
262
|
+
async execute(e, t, n) {
|
|
263
|
+
const o = await t.getAddress(), r = await this.swap(e, o, n);
|
|
256
264
|
try {
|
|
257
|
-
return
|
|
265
|
+
return r.approval && await (await this.sendTransactionWithTimeout(
|
|
258
266
|
t,
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
)).wait(), await this.sendTransactionWithTimeout(t,
|
|
262
|
-
} catch (
|
|
263
|
-
const a =
|
|
267
|
+
r.approval.tx,
|
|
268
|
+
n
|
|
269
|
+
)).wait(), await this.sendTransactionWithTimeout(t, r.tx, n);
|
|
270
|
+
} catch (s) {
|
|
271
|
+
const a = s instanceof Error ? s.message : String(s), i = /estimateGas/i.test(a), c = /missing revert data/i.test(a) || a.includes("reason=null") && a.includes("data=null");
|
|
264
272
|
if (i && (c || /reason=null|data=null/.test(a))) {
|
|
265
273
|
const u = "Transaction reverted during gas estimation and the RPC did not return a revert reason. Try: 1) Get a fresh quote and confirm immediately 2) Switch network or RPC 3) Increase slippage.", d = new Error(`${u} (estimateGas/missing revert data)`);
|
|
266
|
-
throw d.cause =
|
|
274
|
+
throw d.cause = s, d;
|
|
267
275
|
}
|
|
268
|
-
throw
|
|
276
|
+
throw s;
|
|
269
277
|
}
|
|
270
278
|
}
|
|
271
279
|
/**
|
|
@@ -294,8 +302,8 @@ class K {
|
|
|
294
302
|
};
|
|
295
303
|
}
|
|
296
304
|
getProtocolForProvider(e) {
|
|
297
|
-
if (e in
|
|
298
|
-
return
|
|
305
|
+
if (e in M)
|
|
306
|
+
return M[e];
|
|
299
307
|
throw new Error(`Unsupported provider: ${e}`);
|
|
300
308
|
}
|
|
301
309
|
/**
|
|
@@ -307,19 +315,19 @@ class K {
|
|
|
307
315
|
* @returns Encoded calldata and transaction info (to address, value)
|
|
308
316
|
*/
|
|
309
317
|
encodeSwapCalldata(e, t) {
|
|
310
|
-
const
|
|
311
|
-
if (
|
|
312
|
-
const a = this.routerContract.interface.encodeFunctionData("swapETH", [
|
|
318
|
+
const n = e.routerAddress ?? this.config.routerAddress ?? this.routerContract.target, o = this.applySlippage(e.params, t), r = e.srcNative === !0 || e.dstNative === !0, s = this.encodeParams(o);
|
|
319
|
+
if (r) {
|
|
320
|
+
const a = this.routerContract.interface.encodeFunctionData("swapETH", [s]);
|
|
313
321
|
return {
|
|
314
|
-
to:
|
|
322
|
+
to: n,
|
|
315
323
|
data: a,
|
|
316
324
|
value: e.srcNative ? e.amountIn : 0n,
|
|
317
325
|
method: "swapETH"
|
|
318
326
|
};
|
|
319
327
|
} else {
|
|
320
|
-
const a = this.routerContract.interface.encodeFunctionData("swap", [
|
|
328
|
+
const a = this.routerContract.interface.encodeFunctionData("swap", [s]);
|
|
321
329
|
return {
|
|
322
|
-
to:
|
|
330
|
+
to: n,
|
|
323
331
|
data: a,
|
|
324
332
|
value: 0n,
|
|
325
333
|
method: "swap"
|
|
@@ -327,66 +335,66 @@ class K {
|
|
|
327
335
|
}
|
|
328
336
|
}
|
|
329
337
|
buildSwapTransactionRequest(e, t) {
|
|
330
|
-
const { to:
|
|
338
|
+
const { to: n, data: o, value: r, method: s } = this.encodeSwapCalldata(e, t.slippageBps);
|
|
331
339
|
return {
|
|
332
|
-
method:
|
|
333
|
-
tx: this.applyTxOverrides({ to:
|
|
340
|
+
method: s,
|
|
341
|
+
tx: this.applyTxOverrides({ to: n, data: o, value: r }, t, !0)
|
|
334
342
|
};
|
|
335
343
|
}
|
|
336
|
-
async buildApprovalRequest(e, t,
|
|
337
|
-
const
|
|
338
|
-
if (!(
|
|
344
|
+
async buildApprovalRequest(e, t, n, o, r) {
|
|
345
|
+
const s = await this.getAllowance(e, t, o);
|
|
346
|
+
if (!(s >= n))
|
|
339
347
|
return {
|
|
340
348
|
token: e,
|
|
341
349
|
owner: t,
|
|
342
|
-
spender:
|
|
343
|
-
currentAllowance:
|
|
344
|
-
requiredAmount:
|
|
345
|
-
approveAmount:
|
|
346
|
-
tx: this.buildApprovalTransactionRequest(e,
|
|
350
|
+
spender: o,
|
|
351
|
+
currentAllowance: s,
|
|
352
|
+
requiredAmount: n,
|
|
353
|
+
approveAmount: f.MaxUint256,
|
|
354
|
+
tx: this.buildApprovalTransactionRequest(e, o, r)
|
|
347
355
|
};
|
|
348
356
|
}
|
|
349
|
-
buildApprovalTransactionRequest(e, t,
|
|
350
|
-
const
|
|
351
|
-
return this.applyTxOverrides({ to: e, data:
|
|
357
|
+
buildApprovalTransactionRequest(e, t, n) {
|
|
358
|
+
const r = new f.Interface(b).encodeFunctionData("approve", [t, f.MaxUint256]);
|
|
359
|
+
return this.applyTxOverrides({ to: e, data: r, value: 0n }, n, !1);
|
|
352
360
|
}
|
|
353
|
-
async getAllowance(e, t,
|
|
354
|
-
return new
|
|
361
|
+
async getAllowance(e, t, n) {
|
|
362
|
+
return new f.Contract(e, b, this.provider).allowance(t, n);
|
|
355
363
|
}
|
|
356
|
-
applyTxOverrides(e, t,
|
|
357
|
-
const
|
|
358
|
-
return t.gasPrice && (
|
|
364
|
+
applyTxOverrides(e, t, n) {
|
|
365
|
+
const o = { ...e };
|
|
366
|
+
return t.gasPrice && (o.gasPrice = t.gasPrice), n && t.gasLimit && (o.gasLimit = t.gasLimit), o;
|
|
359
367
|
}
|
|
360
|
-
async sendTransactionWithTimeout(e, t,
|
|
361
|
-
const
|
|
362
|
-
if (
|
|
368
|
+
async sendTransactionWithTimeout(e, t, n) {
|
|
369
|
+
const o = n.timeoutMs ?? K;
|
|
370
|
+
if (o <= 0)
|
|
363
371
|
return e.sendTransaction(t);
|
|
364
|
-
const
|
|
365
|
-
if (typeof
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
372
|
+
const r = e;
|
|
373
|
+
if (typeof r.sendUncheckedTransaction == "function" && r.provider) {
|
|
374
|
+
const s = await $(
|
|
375
|
+
r.sendUncheckedTransaction(t),
|
|
376
|
+
o
|
|
369
377
|
), a = await this.waitForTransactionResponse(
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
378
|
+
r.provider,
|
|
379
|
+
s,
|
|
380
|
+
o,
|
|
381
|
+
n.transactionResponsePollingIntervalsMs
|
|
374
382
|
);
|
|
375
383
|
if (a.response)
|
|
376
384
|
return a.response;
|
|
377
385
|
const i = a.rpcErrors > 0 ? `${a.rpcErrors} transient provider error(s) and ${a.nullResponses} null response(s)` : `${a.nullResponses} null response(s)`;
|
|
378
|
-
throw new
|
|
379
|
-
`Transaction was broadcast but provider did not return TransactionResponse within ${
|
|
386
|
+
throw new Y(
|
|
387
|
+
`Transaction was broadcast but provider did not return TransactionResponse within ${o}ms (${i}).`,
|
|
380
388
|
"provider_index",
|
|
381
|
-
|
|
389
|
+
s
|
|
382
390
|
);
|
|
383
391
|
}
|
|
384
|
-
return
|
|
392
|
+
return $(e.sendTransaction(t), o);
|
|
385
393
|
}
|
|
386
|
-
async waitForTransactionResponse(e, t,
|
|
387
|
-
const
|
|
388
|
-
let
|
|
389
|
-
for (; Date.now() <
|
|
394
|
+
async waitForTransactionResponse(e, t, n, o = U) {
|
|
395
|
+
const r = Date.now() + n;
|
|
396
|
+
let s = 0, a = 0, i = 0;
|
|
397
|
+
for (; Date.now() < r; ) {
|
|
390
398
|
try {
|
|
391
399
|
const u = await e.getTransaction(t);
|
|
392
400
|
if (u)
|
|
@@ -395,8 +403,8 @@ class K {
|
|
|
395
403
|
} catch {
|
|
396
404
|
i++;
|
|
397
405
|
}
|
|
398
|
-
const c = this.getNextPollingDelay(
|
|
399
|
-
|
|
406
|
+
const c = this.getNextPollingDelay(o, s);
|
|
407
|
+
s++, await this.delay(Math.min(c, Math.max(25, r - Date.now())));
|
|
400
408
|
}
|
|
401
409
|
return { response: null, nullResponses: a, rpcErrors: i };
|
|
402
410
|
}
|
|
@@ -406,24 +414,24 @@ class K {
|
|
|
406
414
|
});
|
|
407
415
|
}
|
|
408
416
|
getNextPollingDelay(e, t) {
|
|
409
|
-
return e.length === 0 ?
|
|
417
|
+
return e.length === 0 ? U.at(-1) ?? 1200 : e[Math.min(t, e.length - 1)] ?? 1200;
|
|
410
418
|
}
|
|
411
419
|
/**
|
|
412
420
|
* Get token metadata and, optionally, the balance for a specific owner.
|
|
413
421
|
*/
|
|
414
422
|
async getTokenInfo(e, t) {
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
t ?
|
|
423
|
+
const n = new f.Contract(e, b, this.provider), [o, r, s] = await Promise.all([
|
|
424
|
+
n.symbol(),
|
|
425
|
+
n.decimals(),
|
|
426
|
+
t ? n.balanceOf(t) : Promise.resolve(void 0)
|
|
419
427
|
]);
|
|
420
|
-
return
|
|
428
|
+
return s === void 0 ? { symbol: o, decimals: r } : { symbol: o, decimals: r, balance: s };
|
|
421
429
|
}
|
|
422
430
|
/**
|
|
423
431
|
* Get user token balance
|
|
424
432
|
*/
|
|
425
433
|
async getBalance(e, t) {
|
|
426
|
-
return new
|
|
434
|
+
return new f.Contract(e, b, this.provider).balanceOf(t);
|
|
427
435
|
}
|
|
428
436
|
/**
|
|
429
437
|
* Get quote via API
|
|
@@ -431,56 +439,56 @@ class K {
|
|
|
431
439
|
* @throws CustomFeeError if `options.customFee` is malformed
|
|
432
440
|
*/
|
|
433
441
|
async getQuote(e) {
|
|
434
|
-
const { srcToken: t, dstToken:
|
|
435
|
-
byAmountIn:
|
|
436
|
-
depth: a =
|
|
437
|
-
splitCount: i =
|
|
438
|
-
providers: c =
|
|
442
|
+
const { srcToken: t, dstToken: n, amountIn: o, options: r = {} } = e, {
|
|
443
|
+
byAmountIn: s = !0,
|
|
444
|
+
depth: a = P.depth,
|
|
445
|
+
splitCount: i = P.splitCount,
|
|
446
|
+
providers: c = P.providers,
|
|
439
447
|
deadlineSeconds: u = T,
|
|
440
448
|
includeResponseHeaders: d = !1,
|
|
441
449
|
customFee: p
|
|
442
|
-
} =
|
|
443
|
-
|
|
444
|
-
const
|
|
450
|
+
} = r;
|
|
451
|
+
_.validateCustomFee(p);
|
|
452
|
+
const A = y(t), m = y(n), v = A ? this.config.weth : t, S = m ? this.config.weth : n;
|
|
445
453
|
if (d) {
|
|
446
|
-
const { data:
|
|
454
|
+
const { data: E, responseHeaders: O } = await this.apiClient.findRoutes({
|
|
447
455
|
from: v,
|
|
448
|
-
target:
|
|
449
|
-
amount:
|
|
450
|
-
byAmountIn:
|
|
456
|
+
target: S,
|
|
457
|
+
amount: o,
|
|
458
|
+
byAmountIn: s,
|
|
451
459
|
depth: a,
|
|
452
460
|
splitCount: i,
|
|
453
461
|
providers: c,
|
|
454
462
|
includeResponseHeaders: !0
|
|
455
463
|
});
|
|
456
464
|
return this.buildQuoteFromApi(
|
|
457
|
-
|
|
465
|
+
E,
|
|
458
466
|
v,
|
|
459
|
-
|
|
467
|
+
S,
|
|
460
468
|
u,
|
|
461
469
|
c,
|
|
462
|
-
|
|
470
|
+
A,
|
|
463
471
|
m,
|
|
464
|
-
|
|
472
|
+
O,
|
|
465
473
|
p
|
|
466
474
|
);
|
|
467
475
|
}
|
|
468
|
-
const
|
|
476
|
+
const C = await this.apiClient.findRoutes({
|
|
469
477
|
from: v,
|
|
470
|
-
target:
|
|
471
|
-
amount:
|
|
472
|
-
byAmountIn:
|
|
478
|
+
target: S,
|
|
479
|
+
amount: o,
|
|
480
|
+
byAmountIn: s,
|
|
473
481
|
depth: a,
|
|
474
482
|
splitCount: i,
|
|
475
483
|
providers: c
|
|
476
484
|
});
|
|
477
485
|
return this.buildQuoteFromApi(
|
|
478
|
-
E,
|
|
479
|
-
v,
|
|
480
486
|
C,
|
|
487
|
+
v,
|
|
488
|
+
S,
|
|
481
489
|
u,
|
|
482
490
|
c,
|
|
483
|
-
|
|
491
|
+
A,
|
|
484
492
|
m,
|
|
485
493
|
void 0,
|
|
486
494
|
p
|
|
@@ -492,17 +500,17 @@ class K {
|
|
|
492
500
|
*/
|
|
493
501
|
static validateCustomFee(e) {
|
|
494
502
|
if (!e) return;
|
|
495
|
-
const { feeAccount: t, feeBps:
|
|
496
|
-
if (!Number.isInteger(
|
|
497
|
-
throw new
|
|
498
|
-
`customFee.feeBps must be an integer in [1, ${
|
|
503
|
+
const { feeAccount: t, feeBps: n } = e;
|
|
504
|
+
if (!Number.isInteger(n) || n < 1 || n > V)
|
|
505
|
+
throw new B(
|
|
506
|
+
`customFee.feeBps must be an integer in [1, ${V}] (5%), got ${n}`
|
|
499
507
|
);
|
|
500
|
-
if (!t || !
|
|
501
|
-
throw new
|
|
508
|
+
if (!t || !f.isAddress(t))
|
|
509
|
+
throw new B(
|
|
502
510
|
`customFee.feeAccount must be a valid address, got ${t}`
|
|
503
511
|
);
|
|
504
|
-
if (t ===
|
|
505
|
-
throw new
|
|
512
|
+
if (t === f.ZeroAddress)
|
|
513
|
+
throw new B("customFee.feeAccount must not be the zero address");
|
|
506
514
|
}
|
|
507
515
|
/**
|
|
508
516
|
* Resolve the router address for read methods: argument → quote.routerAddress →
|
|
@@ -511,7 +519,7 @@ class K {
|
|
|
511
519
|
*/
|
|
512
520
|
resolveRouterAddress(e) {
|
|
513
521
|
const t = e ?? this.config.routerAddress;
|
|
514
|
-
if (!t || t ===
|
|
522
|
+
if (!t || t === f.ZeroAddress)
|
|
515
523
|
throw new Error(
|
|
516
524
|
"No router address available. Pass one explicitly or set config.routerAddress."
|
|
517
525
|
);
|
|
@@ -529,13 +537,13 @@ class K {
|
|
|
529
537
|
* Pass an explicit address when reading from a different deployment.
|
|
530
538
|
*/
|
|
531
539
|
async getProtocolFeeConfig(e) {
|
|
532
|
-
const t = this.resolveRouterAddress(e),
|
|
533
|
-
|
|
534
|
-
|
|
540
|
+
const t = this.resolveRouterAddress(e), n = new f.Contract(t, R, this.provider), [o, r] = await Promise.all([
|
|
541
|
+
n.protocolFeeReceiver(),
|
|
542
|
+
n.protocolCutBps()
|
|
535
543
|
]);
|
|
536
544
|
return {
|
|
537
|
-
protocolFeeReceiver:
|
|
538
|
-
protocolCutBps: Number(
|
|
545
|
+
protocolFeeReceiver: o,
|
|
546
|
+
protocolCutBps: Number(r)
|
|
539
547
|
};
|
|
540
548
|
}
|
|
541
549
|
/**
|
|
@@ -564,30 +572,30 @@ class K {
|
|
|
564
572
|
* @param routerAddress Optional router address. Defaults to `config.routerAddress`.
|
|
565
573
|
*/
|
|
566
574
|
async getRouterConfig(e) {
|
|
567
|
-
const t = this.resolveRouterAddress(e),
|
|
568
|
-
|
|
569
|
-
s,
|
|
575
|
+
const t = this.resolveRouterAddress(e), n = new f.Contract(t, R, this.provider), [
|
|
576
|
+
o,
|
|
570
577
|
r,
|
|
578
|
+
s,
|
|
571
579
|
a,
|
|
572
580
|
i,
|
|
573
581
|
c,
|
|
574
582
|
u,
|
|
575
583
|
d
|
|
576
584
|
] = await Promise.all([
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
+
n.WETH(),
|
|
586
|
+
n.owner(),
|
|
587
|
+
n.pendingOwner(),
|
|
588
|
+
n.paused(),
|
|
589
|
+
n.maxCustomFeeBps(),
|
|
590
|
+
n.maxProtocolCutBps(),
|
|
591
|
+
n.protocolFeeReceiver(),
|
|
592
|
+
n.protocolCutBps()
|
|
585
593
|
]);
|
|
586
594
|
return {
|
|
587
595
|
address: t,
|
|
588
|
-
weth:
|
|
589
|
-
owner:
|
|
590
|
-
pendingOwner:
|
|
596
|
+
weth: o,
|
|
597
|
+
owner: r,
|
|
598
|
+
pendingOwner: s,
|
|
591
599
|
paused: !!a,
|
|
592
600
|
maxCustomFeeBps: Number(i),
|
|
593
601
|
maxProtocolCutBps: Number(c),
|
|
@@ -601,28 +609,28 @@ class K {
|
|
|
601
609
|
* Filter paths by allowed providers, removing paths with disallowed providers
|
|
602
610
|
* and cascade-removing orphaned paths that depend on removed paths.
|
|
603
611
|
*/
|
|
604
|
-
filterPathsByProviders(e, t,
|
|
605
|
-
const
|
|
606
|
-
let
|
|
607
|
-
if (
|
|
608
|
-
return
|
|
609
|
-
const a =
|
|
612
|
+
filterPathsByProviders(e, t, n, o) {
|
|
613
|
+
const r = new Set(t.map((p) => p.toUpperCase()));
|
|
614
|
+
let s = e.filter((p) => r.has(p.provider.toUpperCase()));
|
|
615
|
+
if (s.length === e.length)
|
|
616
|
+
return s;
|
|
617
|
+
const a = n.toLowerCase(), i = o.toLowerCase(), c = /* @__PURE__ */ new Set();
|
|
610
618
|
c.add(a);
|
|
611
619
|
let u = !0;
|
|
612
620
|
for (; u; ) {
|
|
613
621
|
u = !1;
|
|
614
|
-
for (const p of
|
|
622
|
+
for (const p of s)
|
|
615
623
|
c.has(p.token_in.toLowerCase()) && !c.has(p.token_out.toLowerCase()) && (c.add(p.token_out.toLowerCase()), u = !0);
|
|
616
624
|
}
|
|
617
625
|
const d = /* @__PURE__ */ new Set();
|
|
618
626
|
for (d.add(i), u = !0; u; ) {
|
|
619
627
|
u = !1;
|
|
620
|
-
for (const p of
|
|
628
|
+
for (const p of s)
|
|
621
629
|
d.has(p.token_out.toLowerCase()) && !d.has(p.token_in.toLowerCase()) && (d.add(p.token_in.toLowerCase()), u = !0);
|
|
622
630
|
}
|
|
623
|
-
return
|
|
631
|
+
return s.length, s = s.filter(
|
|
624
632
|
(p) => c.has(p.token_in.toLowerCase()) && d.has(p.token_out.toLowerCase())
|
|
625
|
-
),
|
|
633
|
+
), s;
|
|
626
634
|
}
|
|
627
635
|
/**
|
|
628
636
|
* Build Quote from route data (e.g. from JSON file or API response).
|
|
@@ -633,91 +641,91 @@ class K {
|
|
|
633
641
|
* @param dstToken - Destination token address (last path token_out)
|
|
634
642
|
* @param deadlineSeconds - Optional deadline in seconds from now (default: 20 min)
|
|
635
643
|
*/
|
|
636
|
-
buildQuoteFromRouteData(e, t,
|
|
637
|
-
const
|
|
644
|
+
buildQuoteFromRouteData(e, t, n, o, r) {
|
|
645
|
+
const s = this.buildQuoteFromRouteDataInternal(
|
|
638
646
|
e,
|
|
639
647
|
t,
|
|
640
|
-
|
|
641
|
-
|
|
648
|
+
n,
|
|
649
|
+
o ?? T,
|
|
642
650
|
void 0
|
|
643
651
|
);
|
|
644
|
-
return
|
|
652
|
+
return r?.srcNative && (s.srcNative = !0), r?.dstNative && (s.dstNative = !0), s;
|
|
645
653
|
}
|
|
646
654
|
/**
|
|
647
655
|
* Build Quote from API response
|
|
648
656
|
*/
|
|
649
|
-
buildQuoteFromApi(e, t, o,
|
|
657
|
+
buildQuoteFromApi(e, t, n, o, r, s, a, i, c) {
|
|
650
658
|
const u = this.buildQuoteFromRouteDataInternal(
|
|
651
659
|
e,
|
|
652
660
|
t,
|
|
653
|
-
o,
|
|
654
661
|
n,
|
|
655
|
-
|
|
662
|
+
o,
|
|
663
|
+
r,
|
|
656
664
|
c
|
|
657
665
|
);
|
|
658
|
-
return
|
|
666
|
+
return s && (u.srcNative = !0), a && (u.dstNative = !0), i && (u.responseHeaders = i), u;
|
|
659
667
|
}
|
|
660
|
-
buildQuoteFromRouteDataInternal(e, t, o,
|
|
668
|
+
buildQuoteFromRouteDataInternal(e, t, n, o, r, s) {
|
|
661
669
|
const a = t.toLowerCase();
|
|
662
670
|
e.paths.length;
|
|
663
|
-
let i = e.paths.filter((
|
|
671
|
+
let i = e.paths.filter((w) => !(!(w.token_in.toLowerCase() === a) && BigInt(w.amount_in) === 0n && BigInt(w.amount_out) === 0n));
|
|
664
672
|
if (i.length === 0)
|
|
665
|
-
throw new
|
|
666
|
-
if (
|
|
667
|
-
throw new
|
|
668
|
-
const c =
|
|
673
|
+
throw new g("All route paths have zero amounts", 4001);
|
|
674
|
+
if (r && r.length > 0 && (i = this.filterPathsByProviders(i, r, t, n), i.length === 0))
|
|
675
|
+
throw new g("No valid route paths remaining after provider filtering", 4001);
|
|
676
|
+
const c = n.toLowerCase();
|
|
669
677
|
let u = 0n, d = 0n;
|
|
670
|
-
for (const
|
|
671
|
-
|
|
678
|
+
for (const w of i)
|
|
679
|
+
w.token_in.toLowerCase() === a && (u += BigInt(w.amount_in)), w.token_out.toLowerCase() === c && (d += BigInt(w.amount_out));
|
|
672
680
|
const p = BigInt(
|
|
673
|
-
Math.floor(Date.now() / 1e3) +
|
|
674
|
-
),
|
|
675
|
-
for (const
|
|
676
|
-
const I =
|
|
677
|
-
|
|
681
|
+
Math.floor(Date.now() / 1e3) + o
|
|
682
|
+
), A = /* @__PURE__ */ new Map();
|
|
683
|
+
for (const w of i) {
|
|
684
|
+
const I = w.token_in.toLowerCase();
|
|
685
|
+
A.set(I, (A.get(I) ?? 0) + 1);
|
|
678
686
|
}
|
|
679
|
-
const m = /* @__PURE__ */ new Map(), v = i.map((
|
|
680
|
-
const I =
|
|
681
|
-
m.set(I,
|
|
682
|
-
const D =
|
|
687
|
+
const m = /* @__PURE__ */ new Map(), v = i.map((w) => {
|
|
688
|
+
const I = w.token_in.toLowerCase(), L = (m.get(I) ?? 0) + 1;
|
|
689
|
+
m.set(I, L);
|
|
690
|
+
const D = A.get(I), Z = D > 1 && L < D, j = I === a;
|
|
683
691
|
return {
|
|
684
|
-
adapter:
|
|
685
|
-
pool:
|
|
686
|
-
tokenIn:
|
|
687
|
-
tokenOut:
|
|
688
|
-
amountIn: j || Z ? BigInt(
|
|
689
|
-
extraData:
|
|
692
|
+
adapter: w.adapter,
|
|
693
|
+
pool: w.provider.toUpperCase() === "PANCAKE_INFINITY_CL" ? te : w.provider.toUpperCase() === "PANCAKE_INFINITY_LB" ? ne : w.provider.toUpperCase() === "UNISWAPV4" ? oe : w.pool,
|
|
694
|
+
tokenIn: w.token_in,
|
|
695
|
+
tokenOut: w.token_out,
|
|
696
|
+
amountIn: j || Z ? BigInt(w.amount_in) : 0n,
|
|
697
|
+
extraData: w.extra_data || "0x"
|
|
690
698
|
};
|
|
691
|
-
}),
|
|
692
|
-
for (const
|
|
693
|
-
|
|
694
|
-
const
|
|
699
|
+
}), S = /* @__PURE__ */ new Set();
|
|
700
|
+
for (const w of i)
|
|
701
|
+
w.token_out.toLowerCase() !== c && S.add(w.token_out);
|
|
702
|
+
const C = Array.from(S), E = s?.feeBps ?? 0, O = s?.feeAccount ?? f.ZeroAddress, N = d, F = E > 0 ? N * BigInt(E) / k : 0n, x = N - F, G = {
|
|
695
703
|
srcToken: t,
|
|
696
|
-
dstToken:
|
|
704
|
+
dstToken: n,
|
|
697
705
|
amountIn: u,
|
|
698
|
-
amountOutMin:
|
|
706
|
+
amountOutMin: x,
|
|
699
707
|
steps: v,
|
|
700
|
-
intermediateTokens:
|
|
708
|
+
intermediateTokens: C,
|
|
701
709
|
deadline: p,
|
|
702
|
-
quoteId: e.request_id ?
|
|
703
|
-
expectAmountOut:
|
|
704
|
-
feeReceiver:
|
|
705
|
-
feeBps:
|
|
706
|
-
},
|
|
710
|
+
quoteId: e.request_id ? f.id(e.request_id).slice(0, 66) : f.ZeroHash,
|
|
711
|
+
expectAmountOut: x,
|
|
712
|
+
feeReceiver: O,
|
|
713
|
+
feeBps: E
|
|
714
|
+
}, Q = {
|
|
707
715
|
routes: [
|
|
708
716
|
{
|
|
709
|
-
steps: i.map((
|
|
717
|
+
steps: i.map((w) => ({
|
|
710
718
|
pool: {
|
|
711
|
-
address:
|
|
712
|
-
token0:
|
|
713
|
-
token1:
|
|
714
|
-
protocol: this.getProtocolForProvider(
|
|
715
|
-
fee:
|
|
719
|
+
address: w.pool,
|
|
720
|
+
token0: w.token_in,
|
|
721
|
+
token1: w.token_out,
|
|
722
|
+
protocol: this.getProtocolForProvider(w.provider),
|
|
723
|
+
fee: w.fee_rate ? Math.round(parseFloat(w.fee_rate) * 1e6) : void 0
|
|
716
724
|
},
|
|
717
|
-
tokenIn:
|
|
718
|
-
tokenOut:
|
|
719
|
-
amountIn: BigInt(
|
|
720
|
-
amountOut: BigInt(
|
|
725
|
+
tokenIn: w.token_in,
|
|
726
|
+
tokenOut: w.token_out,
|
|
727
|
+
amountIn: BigInt(w.amount_in),
|
|
728
|
+
amountOut: BigInt(w.amount_out)
|
|
721
729
|
})),
|
|
722
730
|
amountIn: u,
|
|
723
731
|
amountOut: d,
|
|
@@ -730,22 +738,22 @@ class K {
|
|
|
730
738
|
totalGasEstimate: BigInt(e.gas)
|
|
731
739
|
};
|
|
732
740
|
if (!e.contracts?.router)
|
|
733
|
-
throw new
|
|
741
|
+
throw new g("API response missing contracts.router address", 4002);
|
|
734
742
|
return {
|
|
735
743
|
srcToken: t,
|
|
736
|
-
dstToken:
|
|
744
|
+
dstToken: n,
|
|
737
745
|
amountIn: u,
|
|
738
746
|
// Quote.amountOut reflects the user-perceived output (post custom fee).
|
|
739
747
|
// Wallets / UIs should display this number; the gross figure is at route.totalAmountOut.
|
|
740
|
-
amountOut:
|
|
748
|
+
amountOut: x,
|
|
741
749
|
priceImpact: parseFloat(e.deviation_ratio || "0"),
|
|
742
|
-
route:
|
|
750
|
+
route: Q,
|
|
743
751
|
params: G,
|
|
744
752
|
gasEstimate: BigInt(e.gas),
|
|
745
753
|
routerAddress: e.contracts?.router,
|
|
746
|
-
customFee:
|
|
747
|
-
feeAccount:
|
|
748
|
-
feeBps:
|
|
754
|
+
customFee: E > 0 ? {
|
|
755
|
+
feeAccount: O,
|
|
756
|
+
feeBps: E,
|
|
749
757
|
feeAmount: F
|
|
750
758
|
} : void 0
|
|
751
759
|
};
|
|
@@ -767,19 +775,19 @@ class K {
|
|
|
767
775
|
* @param stateOverrides - Optional state overrides for ERC20 balance/allowance
|
|
768
776
|
* @returns Simulated amountOut and method used
|
|
769
777
|
*/
|
|
770
|
-
async simulate(e, t,
|
|
771
|
-
const
|
|
772
|
-
if (
|
|
778
|
+
async simulate(e, t, n, o) {
|
|
779
|
+
const r = n || f.ZeroAddress, { to: s, data: a, value: i, method: c } = this.encodeSwapCalldata(e, t);
|
|
780
|
+
if (o) {
|
|
773
781
|
const u = this.getJsonRpcProviderForStateOverrides(), d = i > 0n ? "0x" + i.toString(16) : void 0, p = await u.send("eth_call", [
|
|
774
|
-
{ from:
|
|
782
|
+
{ from: r, to: s, data: a, value: d },
|
|
775
783
|
"latest",
|
|
776
|
-
|
|
777
|
-
]), [
|
|
778
|
-
return { amountOut:
|
|
784
|
+
o
|
|
785
|
+
]), [A] = this.routerContract.interface.decodeFunctionResult(c, p);
|
|
786
|
+
return { amountOut: A, method: c };
|
|
779
787
|
} else {
|
|
780
788
|
const u = await this.provider.call({
|
|
781
|
-
from:
|
|
782
|
-
to:
|
|
789
|
+
from: r,
|
|
790
|
+
to: s,
|
|
783
791
|
data: a,
|
|
784
792
|
value: i > 0n ? i : void 0
|
|
785
793
|
}), [d] = this.routerContract.interface.decodeFunctionResult(c, u);
|
|
@@ -796,92 +804,167 @@ class K {
|
|
|
796
804
|
/**
|
|
797
805
|
* Format simulate error with human-readable details
|
|
798
806
|
*/
|
|
799
|
-
formatSimulateError(e, t,
|
|
800
|
-
const
|
|
807
|
+
formatSimulateError(e, t, n, o) {
|
|
808
|
+
const r = e instanceof Error ? e : new Error(String(e)), s = e;
|
|
801
809
|
let a = "unknown";
|
|
802
|
-
if (
|
|
803
|
-
a =
|
|
804
|
-
else if (
|
|
805
|
-
const d =
|
|
810
|
+
if (s.reason && typeof s.reason == "string")
|
|
811
|
+
a = s.reason;
|
|
812
|
+
else if (s.revert && typeof s.revert == "object") {
|
|
813
|
+
const d = s.revert;
|
|
806
814
|
d.args && Array.isArray(d.args) && (a = d.args.join(", "));
|
|
807
|
-
} else
|
|
815
|
+
} else r.message && (a = r.message);
|
|
808
816
|
const i = t.params.steps.map(
|
|
809
817
|
(d, p) => ` Step ${p}: ${d.tokenIn.slice(0, 10)}→${d.tokenOut.slice(0, 10)} via adapter ${d.adapter.slice(0, 10)} pool ${d.pool.slice(0, 10)}`
|
|
810
818
|
).join(`
|
|
811
819
|
`), c = [
|
|
812
|
-
`Simulate ${
|
|
820
|
+
`Simulate ${n} failed: ${a}`,
|
|
813
821
|
` Route: ${t.srcToken} → ${t.dstToken}`,
|
|
814
822
|
` AmountIn: ${t.amountIn}`,
|
|
815
823
|
` AmountOutMin: ${t.params.amountOutMin}`,
|
|
816
824
|
` Router: ${t.routerAddress}`,
|
|
817
|
-
` Caller: ${
|
|
825
|
+
` Caller: ${o}`,
|
|
818
826
|
` Steps (${t.params.steps.length}):`,
|
|
819
827
|
i
|
|
820
828
|
].join(`
|
|
821
829
|
`), u = new Error(c);
|
|
822
|
-
return u.cause =
|
|
830
|
+
return u.cause = r, u.reason = a, u;
|
|
823
831
|
}
|
|
824
832
|
/**
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
833
|
+
* Pinpoint the first problematic step in a route.
|
|
834
|
+
*
|
|
835
|
+
* For each prefix [0..n), builds a rebased truncated quote (dstToken = last
|
|
836
|
+
* step's tokenOut, amountOutMin/expectAmountOut/customFee zeroed) and simulates it:
|
|
837
|
+
*
|
|
838
|
+
* - If the prefix REVERTS, that prefix's final step is the culprit. When a
|
|
839
|
+
* `fullRouteError` is supplied we only return when the prefix's revert
|
|
840
|
+
* matches the full-route reason; otherwise any revert qualifies.
|
|
841
|
+
* - If the prefix succeeds but its output is materially smaller than the
|
|
842
|
+
* aggregator's per-hop quoted amountOut, that step is flagged as the
|
|
843
|
+
* first deviator. This catches silent failures like transfer-tax tokens
|
|
844
|
+
* where simulation doesn't revert mid-route but cumulative shortfall
|
|
845
|
+
* eventually trips the final InsufficientOutput check.
|
|
828
846
|
*
|
|
829
847
|
* @param quote - Full quote from getQuote (the one that fails when simulated)
|
|
830
848
|
* @param slippageBps - Same as for simulate
|
|
831
849
|
* @param fromAddress - Same as for simulate
|
|
832
850
|
* @param stateOverrides - Same as for simulate (use when simulating ERC20 sell with arbitrary address)
|
|
833
|
-
* @param fullRouteError -
|
|
834
|
-
* @
|
|
851
|
+
* @param fullRouteError - Optional: the error from simulating the full route. If provided, prefix reverts whose reason differs are skipped (useful for MUL_ERROR-style targeted matching).
|
|
852
|
+
* @param deviationThresholdBps - Maximum tolerated per-step shortfall vs quoted amountOut before flagging the step (default 50 = 0.5%).
|
|
853
|
+
* @returns The first step matching the criteria above, or null if every prefix simulates within tolerance.
|
|
835
854
|
*/
|
|
836
|
-
async findFailingStep(e, t, o,
|
|
837
|
-
const
|
|
838
|
-
if (!
|
|
839
|
-
const
|
|
840
|
-
for (let
|
|
841
|
-
const
|
|
855
|
+
async findFailingStep(e, t, n, o, r, s = 50) {
|
|
856
|
+
const a = e.params.steps;
|
|
857
|
+
if (!a.length) return null;
|
|
858
|
+
const i = r != null ? this.normalizeRevertReason(r) : void 0;
|
|
859
|
+
for (let c = 1; c <= a.length; c++) {
|
|
860
|
+
const u = this.buildTruncatedQuote(e, c);
|
|
861
|
+
let d, p;
|
|
842
862
|
try {
|
|
843
|
-
await this.simulate(
|
|
844
|
-
} catch (
|
|
845
|
-
|
|
846
|
-
|
|
863
|
+
d = (await this.simulate(u, t, n, o)).amountOut;
|
|
864
|
+
} catch (S) {
|
|
865
|
+
p = S;
|
|
866
|
+
}
|
|
867
|
+
if (p !== void 0) {
|
|
868
|
+
const S = this.normalizeRevertReason(p), C = p?.shortMessage ?? p?.reason ?? p?.message;
|
|
869
|
+
if (i == null || S === i)
|
|
847
870
|
return {
|
|
848
|
-
stepIndex:
|
|
849
|
-
step:
|
|
850
|
-
error:
|
|
851
|
-
revertMessage: typeof
|
|
852
|
-
fullRouteRevertMessage:
|
|
871
|
+
stepIndex: c - 1,
|
|
872
|
+
step: a[c - 1],
|
|
873
|
+
error: p,
|
|
874
|
+
revertMessage: typeof C == "string" ? C : S,
|
|
875
|
+
fullRouteRevertMessage: i
|
|
853
876
|
};
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
if (d === void 0) continue;
|
|
880
|
+
const A = this.getQuotedStepAmountOut(e, c - 1);
|
|
881
|
+
if (A === void 0 || A === 0n) continue;
|
|
882
|
+
const m = A > d ? A - d : 0n;
|
|
883
|
+
if (m === 0n) continue;
|
|
884
|
+
const v = m * k / A;
|
|
885
|
+
if (v > BigInt(s)) {
|
|
886
|
+
const S = `Step ${c - 1} deviates: quoted=${A} simulated=${d} shortfall=${m} (${v}bps, threshold=${s}bps). Likely cause: transfer-tax on ${a[c - 1].tokenOut}, stale pool data, or dynamic-fee drift.`;
|
|
887
|
+
return {
|
|
888
|
+
stepIndex: c - 1,
|
|
889
|
+
step: a[c - 1],
|
|
890
|
+
error: new Error(S),
|
|
891
|
+
revertMessage: S,
|
|
892
|
+
fullRouteRevertMessage: i
|
|
893
|
+
};
|
|
854
894
|
}
|
|
855
895
|
}
|
|
856
896
|
return null;
|
|
857
897
|
}
|
|
858
|
-
/** Extract a comparable revert reason (e.g. "MUL_ERROR") from an error
|
|
898
|
+
/** Extract a comparable revert reason (e.g. "MUL_ERROR", "InsufficientOutput", "0xc956d868") from an error. */
|
|
859
899
|
normalizeRevertReason(e) {
|
|
860
900
|
if (e == null) return;
|
|
861
901
|
const t = e;
|
|
862
902
|
if (typeof t.reason == "string" && t.reason.length > 0) return t.reason;
|
|
903
|
+
const n = this.extractRevertData(e);
|
|
904
|
+
if (n && n.length >= 10) {
|
|
905
|
+
const c = n.slice(0, 10).toLowerCase();
|
|
906
|
+
return _.KNOWN_ERROR_SELECTORS[c] ?? c;
|
|
907
|
+
}
|
|
863
908
|
const o = t.shortMessage ?? t.message;
|
|
864
909
|
if (typeof o != "string") return;
|
|
865
|
-
const
|
|
866
|
-
if (
|
|
910
|
+
const r = o.match(/reason="([^"]+)"/);
|
|
911
|
+
if (r) return r[1];
|
|
867
912
|
const s = o.match(/reverted:\s*"([^"]+)"/);
|
|
868
913
|
if (s) return s[1];
|
|
869
|
-
const
|
|
870
|
-
if (
|
|
914
|
+
const a = o.match(/execution reverted:\s*"([^"]+)"/);
|
|
915
|
+
if (a) return a[1];
|
|
916
|
+
const i = o.match(/\b0x[0-9a-fA-F]{8}\b/);
|
|
917
|
+
if (i) {
|
|
918
|
+
const c = i[0].toLowerCase();
|
|
919
|
+
return _.KNOWN_ERROR_SELECTORS[c] ?? c;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
/** Walk an error object (including nested `info`/`error`/`cause`/`revert`) for the first hex revert-data blob. */
|
|
923
|
+
extractRevertData(e) {
|
|
924
|
+
const t = /* @__PURE__ */ new Set(), n = [e];
|
|
925
|
+
for (; n.length; ) {
|
|
926
|
+
const o = n.shift();
|
|
927
|
+
if (o == null || typeof o != "object" || t.has(o)) continue;
|
|
928
|
+
t.add(o);
|
|
929
|
+
const r = o, s = r.data;
|
|
930
|
+
if (typeof s == "string" && /^0x[0-9a-fA-F]{8,}$/.test(s)) return s;
|
|
931
|
+
for (const a of ["info", "error", "cause", "revert", "originalError"])
|
|
932
|
+
r[a] && n.push(r[a]);
|
|
933
|
+
}
|
|
871
934
|
}
|
|
872
|
-
/**
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
935
|
+
/** Read the aggregator's quoted per-step amountOut from the quote's flattened route. */
|
|
936
|
+
getQuotedStepAmountOut(e, t) {
|
|
937
|
+
return e.route?.routes?.[0]?.steps?.[t]?.amountOut;
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Build a quote that simulates only the first `stepCount` steps by rebasing
|
|
941
|
+
* dstToken to the last included step's tokenOut and zeroing all output
|
|
942
|
+
* constraints (amountOutMin/expectAmountOut/customFee). Without this rebase
|
|
943
|
+
* the truncated call would always revert — the router would expect the
|
|
944
|
+
* original dstToken to arrive, which never happens for an intermediate prefix.
|
|
945
|
+
*/
|
|
946
|
+
buildTruncatedQuote(e, t) {
|
|
947
|
+
const n = e.params.steps.slice(0, t), r = n[n.length - 1].tokenOut, s = r.toLowerCase(), a = e.srcToken.toLowerCase(), i = t === e.params.steps.length, c = /* @__PURE__ */ new Set(), u = [];
|
|
948
|
+
for (const d of n) {
|
|
949
|
+
const p = d.tokenOut.toLowerCase();
|
|
950
|
+
p === s || p === a || c.has(p) || (c.add(p), u.push(d.tokenOut));
|
|
878
951
|
}
|
|
879
952
|
return {
|
|
880
953
|
...e,
|
|
954
|
+
dstToken: r,
|
|
955
|
+
// Native unwrap only makes sense when we're simulating the true dstToken.
|
|
956
|
+
dstNative: i ? e.dstNative : !1,
|
|
957
|
+
amountOut: i ? e.amountOut : 0n,
|
|
958
|
+
customFee: i ? e.customFee : void 0,
|
|
881
959
|
params: {
|
|
882
960
|
...e.params,
|
|
883
|
-
|
|
884
|
-
|
|
961
|
+
dstToken: r,
|
|
962
|
+
amountOutMin: i ? e.params.amountOutMin : 0n,
|
|
963
|
+
expectAmountOut: i ? e.params.expectAmountOut : 0n,
|
|
964
|
+
feeReceiver: i ? e.params.feeReceiver : f.ZeroAddress,
|
|
965
|
+
feeBps: i ? e.params.feeBps : 0,
|
|
966
|
+
steps: n,
|
|
967
|
+
intermediateTokens: u
|
|
885
968
|
}
|
|
886
969
|
};
|
|
887
970
|
}
|
|
@@ -902,241 +985,245 @@ class K {
|
|
|
902
985
|
* @param balance - Balance to inject (default: 1M tokens with 18 decimals)
|
|
903
986
|
* @param spenderAddress - Spender to approve (default: routerAddress). Pass quote.routerAddress when simulating API quotes.
|
|
904
987
|
*/
|
|
905
|
-
buildStateOverrides(e, t, o,
|
|
906
|
-
if (
|
|
988
|
+
buildStateOverrides(e, t, n, o, r, s) {
|
|
989
|
+
if (s?.isNative || y(e))
|
|
907
990
|
return {};
|
|
908
|
-
if (!
|
|
991
|
+
if (!n || n === f.ZeroAddress)
|
|
909
992
|
throw new Error("buildStateOverrides requires a non-zero routerAddress.");
|
|
910
|
-
const a =
|
|
911
|
-
for (const m of
|
|
912
|
-
const v =
|
|
993
|
+
const a = f.AbiCoder.defaultAbiCoder(), i = o || f.parseUnits("1000000", 18), c = r ?? n, u = f.zeroPadValue(f.toBeHex(i), 32), d = f.zeroPadValue(f.toBeHex(f.MaxUint256), 32), p = {}, A = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 50, 51, 52, 100, 101, 102];
|
|
994
|
+
for (const m of A) {
|
|
995
|
+
const v = f.keccak256(a.encode(["address", "uint256"], [t, m]));
|
|
913
996
|
p[v] = u;
|
|
914
|
-
const
|
|
915
|
-
p[
|
|
997
|
+
const S = f.keccak256(a.encode(["address", "uint256"], [t, m])), C = f.keccak256(a.encode(["address", "bytes32"], [c, S]));
|
|
998
|
+
p[C] = d;
|
|
916
999
|
}
|
|
917
1000
|
return {
|
|
918
1001
|
[e.toLowerCase()]: { stateDiff: p }
|
|
919
1002
|
};
|
|
920
1003
|
}
|
|
921
|
-
}
|
|
922
|
-
|
|
1004
|
+
};
|
|
1005
|
+
_.KNOWN_ERROR_SELECTORS = {
|
|
1006
|
+
"0x2c19b8b8": "InsufficientOutput"
|
|
1007
|
+
};
|
|
1008
|
+
let W = _;
|
|
1009
|
+
const H = [
|
|
923
1010
|
"function token0() external view returns (address)",
|
|
924
1011
|
"function token1() external view returns (address)",
|
|
925
1012
|
"function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)"
|
|
926
|
-
],
|
|
1013
|
+
], re = [
|
|
927
1014
|
"function token0() external view returns (address)",
|
|
928
1015
|
"function token1() external view returns (address)",
|
|
929
1016
|
"function fee() external view returns (uint24)",
|
|
930
1017
|
"function liquidity() external view returns (uint128)",
|
|
931
1018
|
"function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint32 feeProtocol, bool unlocked)"
|
|
932
|
-
],
|
|
1019
|
+
], se = "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73", ae = [
|
|
933
1020
|
"function getPair(address tokenA, address tokenB) external view returns (address pair)"
|
|
934
|
-
],
|
|
1021
|
+
], ie = "0x86407bEa2078ea5f5EB5A52B2caA963bC1F889Da", ce = [
|
|
935
1022
|
"function getPair(address tokenA, address tokenB) external view returns (address pair)"
|
|
936
|
-
],
|
|
1023
|
+
], ue = "0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865", de = [
|
|
937
1024
|
"function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool)"
|
|
938
|
-
],
|
|
939
|
-
class
|
|
1025
|
+
], le = [100, 500, 2500, 1e4], pe = 60000n, fe = 120000n, he = 100000n;
|
|
1026
|
+
class Ie {
|
|
940
1027
|
constructor(e, t) {
|
|
941
|
-
this.poolCache = /* @__PURE__ */ new Map(), this.provider = e, this.v2Factory = new
|
|
942
|
-
|
|
943
|
-
ne,
|
|
944
|
-
e
|
|
945
|
-
), this.v3Factory = new l.Contract(
|
|
1028
|
+
this.poolCache = /* @__PURE__ */ new Map(), this.provider = e, this.v2Factory = new f.Contract(
|
|
1029
|
+
se,
|
|
946
1030
|
ae,
|
|
947
|
-
ie,
|
|
948
1031
|
e
|
|
949
|
-
), this.
|
|
950
|
-
|
|
951
|
-
|
|
1032
|
+
), this.v3Factory = new f.Contract(
|
|
1033
|
+
ue,
|
|
1034
|
+
de,
|
|
1035
|
+
e
|
|
1036
|
+
), this.babySwapFactory = new f.Contract(
|
|
1037
|
+
ie,
|
|
1038
|
+
ce,
|
|
952
1039
|
e
|
|
953
1040
|
);
|
|
954
1041
|
}
|
|
955
1042
|
/**
|
|
956
1043
|
* Discover optimal route
|
|
957
1044
|
*/
|
|
958
|
-
async findBestRoute(e, t,
|
|
959
|
-
const
|
|
960
|
-
if (
|
|
1045
|
+
async findBestRoute(e, t, n) {
|
|
1046
|
+
const o = await this.discoverPools(e, t);
|
|
1047
|
+
if (o.length === 0)
|
|
961
1048
|
throw new Error(`No pools found for ${e} -> ${t}`);
|
|
962
|
-
const
|
|
963
|
-
|
|
1049
|
+
const s = (await this.calculateRoutes(
|
|
1050
|
+
o,
|
|
964
1051
|
e,
|
|
965
1052
|
t,
|
|
966
|
-
|
|
1053
|
+
n
|
|
967
1054
|
)).reduce(
|
|
968
1055
|
(a, i) => i.amountOut > a.amountOut ? i : a
|
|
969
1056
|
);
|
|
970
1057
|
return {
|
|
971
|
-
routes: [
|
|
1058
|
+
routes: [s],
|
|
972
1059
|
percentages: [1e4],
|
|
973
1060
|
// 100%
|
|
974
|
-
totalAmountIn:
|
|
975
|
-
totalAmountOut:
|
|
976
|
-
totalGasEstimate:
|
|
1061
|
+
totalAmountIn: n,
|
|
1062
|
+
totalAmountOut: s.amountOut,
|
|
1063
|
+
totalGasEstimate: s.gasEstimate
|
|
977
1064
|
};
|
|
978
1065
|
}
|
|
979
1066
|
/**
|
|
980
1067
|
* Discover available pools (direct path)
|
|
981
1068
|
*/
|
|
982
1069
|
async discoverPools(e, t) {
|
|
983
|
-
const
|
|
1070
|
+
const n = [];
|
|
984
1071
|
try {
|
|
985
|
-
const
|
|
986
|
-
|
|
1072
|
+
const o = await this.findV2Pool(e, t);
|
|
1073
|
+
o && n.push(o);
|
|
987
1074
|
} catch {
|
|
988
1075
|
}
|
|
989
|
-
for (const
|
|
1076
|
+
for (const o of le)
|
|
990
1077
|
try {
|
|
991
|
-
const
|
|
992
|
-
|
|
1078
|
+
const r = await this.findV3Pool(e, t, o);
|
|
1079
|
+
r && n.push(r);
|
|
993
1080
|
} catch {
|
|
994
1081
|
}
|
|
995
1082
|
try {
|
|
996
|
-
const
|
|
997
|
-
|
|
1083
|
+
const o = await this.findBabySwapPool(e, t);
|
|
1084
|
+
o && n.push(o);
|
|
998
1085
|
} catch {
|
|
999
1086
|
}
|
|
1000
|
-
return
|
|
1087
|
+
return n;
|
|
1001
1088
|
}
|
|
1002
1089
|
/**
|
|
1003
1090
|
* Find V2 pool
|
|
1004
1091
|
*/
|
|
1005
1092
|
async findV2Pool(e, t) {
|
|
1006
|
-
const
|
|
1007
|
-
if (this.poolCache.has(
|
|
1008
|
-
return this.poolCache.get(
|
|
1009
|
-
const
|
|
1010
|
-
if (
|
|
1093
|
+
const n = `v2-${e}-${t}`;
|
|
1094
|
+
if (this.poolCache.has(n))
|
|
1095
|
+
return this.poolCache.get(n);
|
|
1096
|
+
const o = await this.v2Factory.getPair(e, t);
|
|
1097
|
+
if (o === f.ZeroAddress)
|
|
1011
1098
|
return null;
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1099
|
+
const r = new f.Contract(o, H, this.provider), [s, a, i] = await Promise.all([
|
|
1100
|
+
r.token0(),
|
|
1101
|
+
r.token1(),
|
|
1102
|
+
r.getReserves()
|
|
1016
1103
|
]), c = {
|
|
1017
|
-
address:
|
|
1018
|
-
token0:
|
|
1104
|
+
address: o,
|
|
1105
|
+
token0: s,
|
|
1019
1106
|
token1: a,
|
|
1020
|
-
protocol:
|
|
1107
|
+
protocol: l.PancakeV2,
|
|
1021
1108
|
reserve0: i.reserve0,
|
|
1022
1109
|
reserve1: i.reserve1
|
|
1023
1110
|
};
|
|
1024
|
-
return this.poolCache.set(
|
|
1111
|
+
return this.poolCache.set(n, c), c;
|
|
1025
1112
|
}
|
|
1026
1113
|
/**
|
|
1027
1114
|
* Find V3 pool
|
|
1028
1115
|
*/
|
|
1029
|
-
async findV3Pool(e, t,
|
|
1030
|
-
const
|
|
1031
|
-
if (this.poolCache.has(
|
|
1032
|
-
return this.poolCache.get(
|
|
1033
|
-
const
|
|
1034
|
-
if (
|
|
1116
|
+
async findV3Pool(e, t, n) {
|
|
1117
|
+
const o = `v3-${e}-${t}-${n}`;
|
|
1118
|
+
if (this.poolCache.has(o))
|
|
1119
|
+
return this.poolCache.get(o);
|
|
1120
|
+
const r = await this.v3Factory.getPool(e, t, n);
|
|
1121
|
+
if (r === f.ZeroAddress)
|
|
1035
1122
|
return null;
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1123
|
+
const s = new f.Contract(r, re, this.provider), [a, i, c] = await Promise.all([
|
|
1124
|
+
s.token0(),
|
|
1125
|
+
s.token1(),
|
|
1126
|
+
s.liquidity()
|
|
1040
1127
|
]);
|
|
1041
1128
|
if (c === 0n)
|
|
1042
1129
|
return null;
|
|
1043
1130
|
const u = {
|
|
1044
|
-
address:
|
|
1131
|
+
address: r,
|
|
1045
1132
|
token0: a,
|
|
1046
1133
|
token1: i,
|
|
1047
|
-
protocol:
|
|
1048
|
-
fee:
|
|
1134
|
+
protocol: l.PancakeV3,
|
|
1135
|
+
fee: n,
|
|
1049
1136
|
liquidity: c
|
|
1050
1137
|
};
|
|
1051
|
-
return this.poolCache.set(
|
|
1138
|
+
return this.poolCache.set(o, u), u;
|
|
1052
1139
|
}
|
|
1053
1140
|
/**
|
|
1054
1141
|
* Find BabySwap pool (V2 fork, 0.2% fee)
|
|
1055
1142
|
*/
|
|
1056
1143
|
async findBabySwapPool(e, t) {
|
|
1057
|
-
const
|
|
1058
|
-
if (this.poolCache.has(
|
|
1059
|
-
return this.poolCache.get(
|
|
1060
|
-
const
|
|
1061
|
-
if (
|
|
1144
|
+
const n = `babyswap-${e}-${t}`;
|
|
1145
|
+
if (this.poolCache.has(n))
|
|
1146
|
+
return this.poolCache.get(n);
|
|
1147
|
+
const o = await this.babySwapFactory.getPair(e, t);
|
|
1148
|
+
if (o === f.ZeroAddress)
|
|
1062
1149
|
return null;
|
|
1063
|
-
const
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1150
|
+
const r = new f.Contract(o, H, this.provider), [s, a, i] = await Promise.all([
|
|
1151
|
+
r.token0(),
|
|
1152
|
+
r.token1(),
|
|
1153
|
+
r.getReserves()
|
|
1067
1154
|
]), c = {
|
|
1068
|
-
address:
|
|
1069
|
-
token0:
|
|
1155
|
+
address: o,
|
|
1156
|
+
token0: s,
|
|
1070
1157
|
token1: a,
|
|
1071
|
-
protocol:
|
|
1158
|
+
protocol: l.BabySwap,
|
|
1072
1159
|
reserve0: i.reserve0,
|
|
1073
1160
|
reserve1: i.reserve1
|
|
1074
1161
|
};
|
|
1075
|
-
return this.poolCache.set(
|
|
1162
|
+
return this.poolCache.set(n, c), c;
|
|
1076
1163
|
}
|
|
1077
1164
|
/**
|
|
1078
1165
|
* Calculate route quotes
|
|
1079
1166
|
*/
|
|
1080
|
-
async calculateRoutes(e, t,
|
|
1081
|
-
const
|
|
1082
|
-
for (const
|
|
1167
|
+
async calculateRoutes(e, t, n, o) {
|
|
1168
|
+
const r = [];
|
|
1169
|
+
for (const s of e)
|
|
1083
1170
|
try {
|
|
1084
1171
|
const a = await this.getAmountOut(
|
|
1085
|
-
|
|
1172
|
+
s,
|
|
1086
1173
|
t,
|
|
1087
|
-
|
|
1088
|
-
|
|
1174
|
+
n,
|
|
1175
|
+
o
|
|
1089
1176
|
);
|
|
1090
|
-
a > 0n &&
|
|
1177
|
+
a > 0n && r.push({
|
|
1091
1178
|
steps: [
|
|
1092
1179
|
{
|
|
1093
|
-
pool:
|
|
1180
|
+
pool: s,
|
|
1094
1181
|
tokenIn: t,
|
|
1095
|
-
tokenOut:
|
|
1096
|
-
amountIn:
|
|
1182
|
+
tokenOut: n,
|
|
1183
|
+
amountIn: o,
|
|
1097
1184
|
amountOut: a
|
|
1098
1185
|
}
|
|
1099
1186
|
],
|
|
1100
|
-
amountIn:
|
|
1187
|
+
amountIn: o,
|
|
1101
1188
|
amountOut: a,
|
|
1102
|
-
gasEstimate:
|
|
1189
|
+
gasEstimate: s.protocol === l.PancakeV2 || s.protocol === l.BabySwap ? pe : s.protocol === l.Dodo ? he : fe
|
|
1103
1190
|
});
|
|
1104
1191
|
} catch {
|
|
1105
1192
|
}
|
|
1106
|
-
return
|
|
1193
|
+
return r;
|
|
1107
1194
|
}
|
|
1108
1195
|
/**
|
|
1109
1196
|
* Get output for a single pool
|
|
1110
1197
|
*/
|
|
1111
|
-
async getAmountOut(e, t,
|
|
1112
|
-
if (e.protocol ===
|
|
1113
|
-
return this.getV2AmountOut(e, t,
|
|
1114
|
-
if (e.protocol ===
|
|
1115
|
-
return this.getBabySwapAmountOut(e, t,
|
|
1116
|
-
if (e.protocol ===
|
|
1198
|
+
async getAmountOut(e, t, n, o) {
|
|
1199
|
+
if (e.protocol === l.PancakeV2)
|
|
1200
|
+
return this.getV2AmountOut(e, t, o);
|
|
1201
|
+
if (e.protocol === l.BabySwap)
|
|
1202
|
+
return this.getBabySwapAmountOut(e, t, o);
|
|
1203
|
+
if (e.protocol === l.Dodo)
|
|
1117
1204
|
throw new Error("DODO amount out not supported in local route discovery");
|
|
1118
|
-
return this.estimateV3AmountOut(e, t,
|
|
1205
|
+
return this.estimateV3AmountOut(e, t, o);
|
|
1119
1206
|
}
|
|
1120
1207
|
/**
|
|
1121
1208
|
* V2 output calculation
|
|
1122
1209
|
*/
|
|
1123
|
-
getV2AmountOut(e, t,
|
|
1124
|
-
const
|
|
1210
|
+
getV2AmountOut(e, t, n) {
|
|
1211
|
+
const o = t.toLowerCase() === e.token0.toLowerCase(), [r, s] = o ? [e.reserve0, e.reserve1] : [e.reserve1, e.reserve0], a = n * 9975n, i = a * s, c = r * 10000n + a;
|
|
1125
1212
|
return i / c;
|
|
1126
1213
|
}
|
|
1127
1214
|
/**
|
|
1128
1215
|
* BabySwap output calculation (V2 fork, 0.2% fee — coefficient 2 in 1000-scale)
|
|
1129
1216
|
*/
|
|
1130
|
-
getBabySwapAmountOut(e, t,
|
|
1131
|
-
const
|
|
1217
|
+
getBabySwapAmountOut(e, t, n) {
|
|
1218
|
+
const o = t.toLowerCase() === e.token0.toLowerCase(), [r, s] = o ? [e.reserve0, e.reserve1] : [e.reserve1, e.reserve0], a = n * 998n, i = a * s, c = r * 1000n + a;
|
|
1132
1219
|
return i / c;
|
|
1133
1220
|
}
|
|
1134
1221
|
/**
|
|
1135
1222
|
* V3 output estimation (simplified)
|
|
1136
1223
|
*/
|
|
1137
|
-
estimateV3AmountOut(e, t,
|
|
1138
|
-
const
|
|
1139
|
-
return
|
|
1224
|
+
estimateV3AmountOut(e, t, n) {
|
|
1225
|
+
const r = 1000000n - BigInt(e.fee || 2500);
|
|
1226
|
+
return n * r / 1000000n;
|
|
1140
1227
|
}
|
|
1141
1228
|
/**
|
|
1142
1229
|
* Clear cache
|
|
@@ -1145,33 +1232,33 @@ class Ae {
|
|
|
1145
1232
|
this.poolCache.clear();
|
|
1146
1233
|
}
|
|
1147
1234
|
}
|
|
1148
|
-
class
|
|
1235
|
+
class Ce {
|
|
1149
1236
|
constructor(e) {
|
|
1150
1237
|
this.adapters = e;
|
|
1151
1238
|
}
|
|
1152
1239
|
/**
|
|
1153
1240
|
* Build SwapParams from split route
|
|
1154
1241
|
*/
|
|
1155
|
-
build(e, t,
|
|
1156
|
-
const
|
|
1242
|
+
build(e, t, n, o, r = T) {
|
|
1243
|
+
const s = this.flattenRoutes(e), a = this.mergeIdenticalPools(s), i = this.topologicalSort(a, t), c = this.convertToSwapSteps(i), u = this.extractIntermediates(
|
|
1157
1244
|
i,
|
|
1158
1245
|
t,
|
|
1159
|
-
|
|
1160
|
-
), d = BigInt(Math.floor(Date.now() / 1e3) +
|
|
1246
|
+
n
|
|
1247
|
+
), d = BigInt(Math.floor(Date.now() / 1e3) + r);
|
|
1161
1248
|
return {
|
|
1162
1249
|
srcToken: t,
|
|
1163
|
-
dstToken:
|
|
1250
|
+
dstToken: n,
|
|
1164
1251
|
amountIn: e.totalAmountIn,
|
|
1165
|
-
amountOutMin:
|
|
1252
|
+
amountOutMin: o,
|
|
1166
1253
|
steps: c,
|
|
1167
1254
|
intermediateTokens: u,
|
|
1168
1255
|
deadline: d,
|
|
1169
|
-
quoteId:
|
|
1256
|
+
quoteId: f.ZeroHash,
|
|
1170
1257
|
expectAmountOut: e.totalAmountOut,
|
|
1171
1258
|
// SwapBuilder is the legacy direct-build path that does not know about custom fees.
|
|
1172
1259
|
// Integrators that want custom fees should use PeachClient.getQuote with options.customFee
|
|
1173
1260
|
// instead, which sets these fields and adjusts amountOutMin accordingly.
|
|
1174
|
-
feeReceiver:
|
|
1261
|
+
feeReceiver: f.ZeroAddress,
|
|
1175
1262
|
feeBps: 0
|
|
1176
1263
|
};
|
|
1177
1264
|
}
|
|
@@ -1180,17 +1267,17 @@ class ge {
|
|
|
1180
1267
|
*/
|
|
1181
1268
|
flattenRoutes(e) {
|
|
1182
1269
|
const t = [];
|
|
1183
|
-
for (let
|
|
1184
|
-
const
|
|
1185
|
-
for (let a = 0; a <
|
|
1186
|
-
const i =
|
|
1270
|
+
for (let n = 0; n < e.routes.length; n++) {
|
|
1271
|
+
const o = e.routes[n], r = e.percentages[n], s = e.totalAmountIn * BigInt(r) / k;
|
|
1272
|
+
for (let a = 0; a < o.steps.length; a++) {
|
|
1273
|
+
const i = o.steps[a];
|
|
1187
1274
|
t.push({
|
|
1188
1275
|
pool: i.pool,
|
|
1189
1276
|
tokenIn: i.tokenIn,
|
|
1190
1277
|
tokenOut: i.tokenOut,
|
|
1191
1278
|
// Only first step has fixed amount, subsequent steps depend on previous output
|
|
1192
|
-
amountIn: a === 0 ?
|
|
1193
|
-
routeIndex:
|
|
1279
|
+
amountIn: a === 0 ? s : 0n,
|
|
1280
|
+
routeIndex: n,
|
|
1194
1281
|
stepIndex: a
|
|
1195
1282
|
});
|
|
1196
1283
|
}
|
|
@@ -1202,33 +1289,33 @@ class ge {
|
|
|
1202
1289
|
* Key: pool + tokenIn + tokenOut
|
|
1203
1290
|
*/
|
|
1204
1291
|
mergeIdenticalPools(e) {
|
|
1205
|
-
const t = /* @__PURE__ */ new Map(),
|
|
1206
|
-
for (const
|
|
1207
|
-
const
|
|
1208
|
-
if (t.has(
|
|
1209
|
-
const
|
|
1210
|
-
|
|
1292
|
+
const t = /* @__PURE__ */ new Map(), n = [];
|
|
1293
|
+
for (const o of e) {
|
|
1294
|
+
const r = `${o.pool.address}-${o.tokenIn}-${o.tokenOut}`;
|
|
1295
|
+
if (t.has(r)) {
|
|
1296
|
+
const s = t.get(r);
|
|
1297
|
+
o.amountIn > 0n && s.amountIn > 0n && (s.amountIn = 0n);
|
|
1211
1298
|
} else {
|
|
1212
|
-
const
|
|
1213
|
-
t.set(
|
|
1299
|
+
const s = { ...o };
|
|
1300
|
+
t.set(r, s), n.push(s);
|
|
1214
1301
|
}
|
|
1215
1302
|
}
|
|
1216
|
-
for (const
|
|
1217
|
-
const
|
|
1303
|
+
for (const o of n) {
|
|
1304
|
+
const r = `${o.pool.address}-${o.tokenIn}-${o.tokenOut}`;
|
|
1218
1305
|
e.filter(
|
|
1219
|
-
(a) => `${a.pool.address}-${a.tokenIn}-${a.tokenOut}` ===
|
|
1220
|
-
).length > 1 && (
|
|
1306
|
+
(a) => `${a.pool.address}-${a.tokenIn}-${a.tokenOut}` === r
|
|
1307
|
+
).length > 1 && (o.amountIn = 0n);
|
|
1221
1308
|
}
|
|
1222
|
-
return
|
|
1309
|
+
return n;
|
|
1223
1310
|
}
|
|
1224
1311
|
/**
|
|
1225
1312
|
* Topological sort (Kahn's Algorithm)
|
|
1226
1313
|
*/
|
|
1227
1314
|
topologicalSort(e, t) {
|
|
1228
|
-
const
|
|
1315
|
+
const n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map();
|
|
1229
1316
|
for (const i of e) {
|
|
1230
1317
|
const c = this.stepKey(i);
|
|
1231
|
-
|
|
1318
|
+
o.set(c, i), n.set(c, 0), r.set(c, []);
|
|
1232
1319
|
}
|
|
1233
1320
|
for (const i of e) {
|
|
1234
1321
|
const c = this.stepKey(i);
|
|
@@ -1236,20 +1323,20 @@ class ge {
|
|
|
1236
1323
|
for (const u of e)
|
|
1237
1324
|
if (u.tokenOut === i.tokenIn) {
|
|
1238
1325
|
const d = this.stepKey(u);
|
|
1239
|
-
d !== c && (
|
|
1326
|
+
d !== c && (r.get(d).push(c), n.set(c, (n.get(c) || 0) + 1));
|
|
1240
1327
|
}
|
|
1241
1328
|
}
|
|
1242
1329
|
}
|
|
1243
|
-
const
|
|
1244
|
-
for (const [i, c] of
|
|
1245
|
-
c === 0 &&
|
|
1330
|
+
const s = [];
|
|
1331
|
+
for (const [i, c] of n)
|
|
1332
|
+
c === 0 && s.push(i);
|
|
1246
1333
|
const a = [];
|
|
1247
|
-
for (;
|
|
1248
|
-
const i =
|
|
1334
|
+
for (; s.length > 0; ) {
|
|
1335
|
+
const i = s.shift(), c = o.get(i);
|
|
1249
1336
|
a.push(c);
|
|
1250
|
-
for (const u of
|
|
1251
|
-
const d = (
|
|
1252
|
-
|
|
1337
|
+
for (const u of r.get(i) || []) {
|
|
1338
|
+
const d = (n.get(u) || 0) - 1;
|
|
1339
|
+
n.set(u, d), d === 0 && s.push(u);
|
|
1253
1340
|
}
|
|
1254
1341
|
}
|
|
1255
1342
|
if (a.length !== e.length)
|
|
@@ -1261,11 +1348,11 @@ class ge {
|
|
|
1261
1348
|
*/
|
|
1262
1349
|
convertToSwapSteps(e) {
|
|
1263
1350
|
return e.map((t) => {
|
|
1264
|
-
const
|
|
1265
|
-
if (!
|
|
1351
|
+
const n = this.adapters.get(t.pool.protocol);
|
|
1352
|
+
if (!n)
|
|
1266
1353
|
throw new Error(`No adapter for protocol: ${t.pool.protocol}`);
|
|
1267
1354
|
return {
|
|
1268
|
-
adapter:
|
|
1355
|
+
adapter: n,
|
|
1269
1356
|
pool: t.pool.address,
|
|
1270
1357
|
tokenIn: t.tokenIn,
|
|
1271
1358
|
tokenOut: t.tokenOut,
|
|
@@ -1279,24 +1366,41 @@ class ge {
|
|
|
1279
1366
|
*/
|
|
1280
1367
|
encodeExtraData(e) {
|
|
1281
1368
|
switch (e.protocol) {
|
|
1282
|
-
case
|
|
1369
|
+
case l.PancakeV2:
|
|
1283
1370
|
return "0x";
|
|
1284
1371
|
// V2 doesn't need extra parameters
|
|
1285
|
-
case
|
|
1286
|
-
case
|
|
1287
|
-
case
|
|
1372
|
+
case l.PancakeV3:
|
|
1373
|
+
case l.UniswapV3:
|
|
1374
|
+
case l.UniswapV4:
|
|
1375
|
+
case l.SquadSwapV3:
|
|
1376
|
+
case l.ThenaFusion:
|
|
1377
|
+
return "0x";
|
|
1378
|
+
case l.PancakeInfinityCl:
|
|
1379
|
+
case l.PancakeInfinityLb:
|
|
1380
|
+
return "0x";
|
|
1381
|
+
case l.Dodo:
|
|
1382
|
+
return "0x";
|
|
1383
|
+
case l.NomiswapStable:
|
|
1384
|
+
return "0x";
|
|
1385
|
+
case l.Biswap:
|
|
1288
1386
|
return "0x";
|
|
1289
|
-
case
|
|
1387
|
+
case l.Apeswap:
|
|
1290
1388
|
return "0x";
|
|
1291
|
-
case
|
|
1389
|
+
case l.BabyDogeSwap:
|
|
1292
1390
|
return "0x";
|
|
1293
|
-
case
|
|
1391
|
+
case l.BabySwap:
|
|
1294
1392
|
return "0x";
|
|
1295
|
-
case
|
|
1393
|
+
case l.SquadSwapV2:
|
|
1296
1394
|
return "0x";
|
|
1297
|
-
case
|
|
1395
|
+
case l.SushiSwapV2:
|
|
1298
1396
|
return "0x";
|
|
1299
|
-
case
|
|
1397
|
+
case l.SushiSwapV3:
|
|
1398
|
+
return "0x";
|
|
1399
|
+
case l.PancakeStable:
|
|
1400
|
+
return "0x";
|
|
1401
|
+
case l.ListaStable:
|
|
1402
|
+
return "0x";
|
|
1403
|
+
case l.Wombat:
|
|
1300
1404
|
return "0x";
|
|
1301
1405
|
default:
|
|
1302
1406
|
return "0x";
|
|
@@ -1305,11 +1409,11 @@ class ge {
|
|
|
1305
1409
|
/**
|
|
1306
1410
|
* Extract intermediate tokens
|
|
1307
1411
|
*/
|
|
1308
|
-
extractIntermediates(e, t,
|
|
1309
|
-
const
|
|
1310
|
-
for (const
|
|
1311
|
-
|
|
1312
|
-
return
|
|
1412
|
+
extractIntermediates(e, t, n) {
|
|
1413
|
+
const o = /* @__PURE__ */ new Set();
|
|
1414
|
+
for (const r of e)
|
|
1415
|
+
r.tokenOut !== n && o.add(r.tokenOut);
|
|
1416
|
+
return o.delete(t), o.delete(n), Array.from(o);
|
|
1313
1417
|
}
|
|
1314
1418
|
/**
|
|
1315
1419
|
* Generate unique step key
|
|
@@ -1319,28 +1423,28 @@ class ge {
|
|
|
1319
1423
|
}
|
|
1320
1424
|
}
|
|
1321
1425
|
export {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1426
|
+
P as API_DEFAULTS,
|
|
1427
|
+
ee as ApiClient,
|
|
1428
|
+
g as ApiError,
|
|
1429
|
+
k as BPS_DENOMINATOR,
|
|
1430
|
+
Se as BSC_MAINNET_CONFIG,
|
|
1431
|
+
ge as BSC_TESTNET_CONFIG,
|
|
1432
|
+
B as CustomFeeError,
|
|
1329
1433
|
q as DEFAULT_API_URL,
|
|
1330
1434
|
T as DEFAULT_DEADLINE_SECONDS,
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1435
|
+
K as DEFAULT_EXECUTE_TIMEOUT_MS,
|
|
1436
|
+
me as DEFAULT_SLIPPAGE_BPS,
|
|
1437
|
+
U as DEFAULT_TRANSACTION_RESPONSE_POLL_INTERVALS_MS,
|
|
1438
|
+
ve as ERR_ZERO_AMOUNT_PATHS,
|
|
1439
|
+
Y as ExecuteTimeoutError,
|
|
1440
|
+
V as MAX_FEE_BPS,
|
|
1441
|
+
Ae as MAX_PLATFORM_CUT_BPS,
|
|
1442
|
+
z as NATIVE_TOKEN_ADDRESS,
|
|
1443
|
+
W as PeachClient,
|
|
1444
|
+
l as ProtocolType,
|
|
1445
|
+
Ie as RouteDiscovery,
|
|
1446
|
+
Ce as SwapBuilder,
|
|
1447
|
+
y as isNativeTokenAddress,
|
|
1448
|
+
$ as withWalletSendTimeout
|
|
1345
1449
|
};
|
|
1346
1450
|
//# sourceMappingURL=index.mjs.map
|