@t402/smart-router 1.0.0-beta.1
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 +388 -0
- package/dist/chunk-BUGQ6VOQ.js +662 -0
- package/dist/chunk-BUGQ6VOQ.js.map +1 -0
- package/dist/chunk-KJJ2L6TF.js +742 -0
- package/dist/chunk-KJJ2L6TF.js.map +1 -0
- package/dist/chunk-PCDWVENA.js +450 -0
- package/dist/chunk-PCDWVENA.js.map +1 -0
- package/dist/chunk-QIZPPHGB.js +169 -0
- package/dist/chunk-QIZPPHGB.js.map +1 -0
- package/dist/chunk-XKFKUWJY.js +504 -0
- package/dist/chunk-XKFKUWJY.js.map +1 -0
- package/dist/execution/index.d.ts +679 -0
- package/dist/execution/index.js +31 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/gas-CNpJzuy5.d.ts +454 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +119 -0
- package/dist/index.js.map +1 -0
- package/dist/liquidity/index.d.ts +418 -0
- package/dist/liquidity/index.js +24 -0
- package/dist/liquidity/index.js.map +1 -0
- package/dist/pricing/index.d.ts +74 -0
- package/dist/pricing/index.js +32 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/routing/index.d.ts +117 -0
- package/dist/routing/index.js +43 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/types-C0ey6WqI.d.ts +937 -0
- package/package.json +70 -0
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AssetId,
|
|
3
|
+
ChainId,
|
|
4
|
+
ProtocolId
|
|
5
|
+
} from "./chunk-QIZPPHGB.js";
|
|
6
|
+
|
|
7
|
+
// src/pricing/types.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
var GasPrice = z.object({
|
|
10
|
+
chain: ChainId,
|
|
11
|
+
// Price in native token (wei for EVM)
|
|
12
|
+
slow: z.string(),
|
|
13
|
+
standard: z.string(),
|
|
14
|
+
fast: z.string(),
|
|
15
|
+
instant: z.string(),
|
|
16
|
+
// EIP-1559 fields (for supported chains)
|
|
17
|
+
baseFee: z.string().optional(),
|
|
18
|
+
priorityFee: z.object({
|
|
19
|
+
slow: z.string(),
|
|
20
|
+
standard: z.string(),
|
|
21
|
+
fast: z.string(),
|
|
22
|
+
instant: z.string()
|
|
23
|
+
}).optional(),
|
|
24
|
+
// Native token price in USD
|
|
25
|
+
nativeTokenPriceUsd: z.string(),
|
|
26
|
+
// Block info
|
|
27
|
+
lastBlock: z.number(),
|
|
28
|
+
timestamp: z.number()
|
|
29
|
+
});
|
|
30
|
+
var TokenPrice = z.object({
|
|
31
|
+
chain: ChainId,
|
|
32
|
+
asset: AssetId,
|
|
33
|
+
priceUsd: z.string(),
|
|
34
|
+
price24hChange: z.string().optional(),
|
|
35
|
+
volume24h: z.string().optional(),
|
|
36
|
+
source: z.string(),
|
|
37
|
+
// Price oracle source
|
|
38
|
+
timestamp: z.number(),
|
|
39
|
+
confidence: z.number().min(0).max(100)
|
|
40
|
+
});
|
|
41
|
+
var ProtocolFees = z.object({
|
|
42
|
+
protocol: ProtocolId,
|
|
43
|
+
chain: ChainId,
|
|
44
|
+
// Fee types
|
|
45
|
+
swapFee: z.string().optional(),
|
|
46
|
+
// Percentage (e.g., "0.3" for 0.3%)
|
|
47
|
+
bridgeFee: z.object({
|
|
48
|
+
percentage: z.string(),
|
|
49
|
+
minimum: z.string(),
|
|
50
|
+
maximum: z.string().optional()
|
|
51
|
+
}).optional(),
|
|
52
|
+
// Gas estimates for different operations
|
|
53
|
+
gasEstimates: z.record(z.string(), z.string()),
|
|
54
|
+
// Operation -> gas units
|
|
55
|
+
timestamp: z.number()
|
|
56
|
+
});
|
|
57
|
+
var BridgeQuote = z.object({
|
|
58
|
+
bridge: ProtocolId,
|
|
59
|
+
sourceChain: ChainId,
|
|
60
|
+
destinationChain: ChainId,
|
|
61
|
+
sourceAsset: AssetId,
|
|
62
|
+
destinationAsset: AssetId,
|
|
63
|
+
inputAmount: z.string(),
|
|
64
|
+
outputAmount: z.string(),
|
|
65
|
+
fee: z.string(),
|
|
66
|
+
feeUsd: z.string(),
|
|
67
|
+
estimatedTime: z.number(),
|
|
68
|
+
// Seconds
|
|
69
|
+
confidence: z.number().min(0).max(100),
|
|
70
|
+
expiresAt: z.number()
|
|
71
|
+
});
|
|
72
|
+
var SwapQuote = z.object({
|
|
73
|
+
dex: ProtocolId,
|
|
74
|
+
chain: ChainId,
|
|
75
|
+
inputAsset: AssetId,
|
|
76
|
+
outputAsset: AssetId,
|
|
77
|
+
inputAmount: z.string(),
|
|
78
|
+
outputAmount: z.string(),
|
|
79
|
+
minOutputAmount: z.string(),
|
|
80
|
+
priceImpact: z.string(),
|
|
81
|
+
fee: z.string(),
|
|
82
|
+
feeUsd: z.string(),
|
|
83
|
+
route: z.array(AssetId),
|
|
84
|
+
// Token path
|
|
85
|
+
estimatedGas: z.string(),
|
|
86
|
+
expiresAt: z.number()
|
|
87
|
+
});
|
|
88
|
+
var FeeEstimateRequest = z.object({
|
|
89
|
+
chain: ChainId,
|
|
90
|
+
operation: z.enum(["transfer", "swap", "bridge", "approve", "wrap", "unwrap"]),
|
|
91
|
+
protocol: ProtocolId.optional(),
|
|
92
|
+
inputAsset: AssetId,
|
|
93
|
+
outputAsset: AssetId.optional(),
|
|
94
|
+
amount: z.string(),
|
|
95
|
+
gasSpeed: z.enum(["slow", "standard", "fast", "instant"]).default("standard")
|
|
96
|
+
});
|
|
97
|
+
var FeeEstimate = z.object({
|
|
98
|
+
// Gas
|
|
99
|
+
gasUnits: z.string(),
|
|
100
|
+
gasPriceWei: z.string(),
|
|
101
|
+
gasCostNative: z.string(),
|
|
102
|
+
gasCostUsd: z.string(),
|
|
103
|
+
// Protocol fees
|
|
104
|
+
protocolFee: z.string(),
|
|
105
|
+
protocolFeeUsd: z.string(),
|
|
106
|
+
// Total
|
|
107
|
+
totalFeeUsd: z.string(),
|
|
108
|
+
// Metadata
|
|
109
|
+
confidence: z.number().min(0).max(100),
|
|
110
|
+
breakdown: z.array(z.object({
|
|
111
|
+
type: z.string(),
|
|
112
|
+
amount: z.string(),
|
|
113
|
+
amountUsd: z.string()
|
|
114
|
+
}))
|
|
115
|
+
});
|
|
116
|
+
var PricePrediction = z.object({
|
|
117
|
+
chain: ChainId,
|
|
118
|
+
asset: AssetId.optional(),
|
|
119
|
+
// If not provided, predicts gas price
|
|
120
|
+
currentPrice: z.string(),
|
|
121
|
+
predictions: z.array(z.object({
|
|
122
|
+
timestamp: z.number(),
|
|
123
|
+
predictedPrice: z.string(),
|
|
124
|
+
confidence: z.number(),
|
|
125
|
+
lowerBound: z.string(),
|
|
126
|
+
upperBound: z.string()
|
|
127
|
+
})),
|
|
128
|
+
model: z.string(),
|
|
129
|
+
lastUpdated: z.number()
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// src/pricing/gas.ts
|
|
133
|
+
var GasOracle = class {
|
|
134
|
+
providers = /* @__PURE__ */ new Map();
|
|
135
|
+
cache = /* @__PURE__ */ new Map();
|
|
136
|
+
config;
|
|
137
|
+
history = /* @__PURE__ */ new Map();
|
|
138
|
+
maxHistorySize = 100;
|
|
139
|
+
constructor(config = {}) {
|
|
140
|
+
this.config = {
|
|
141
|
+
cacheTtl: config.cacheTtl ?? 15e3,
|
|
142
|
+
// 15 seconds
|
|
143
|
+
refreshInterval: config.refreshInterval ?? 12e3,
|
|
144
|
+
// 12 seconds (block time)
|
|
145
|
+
enablePrediction: config.enablePrediction ?? true
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Register a gas provider for a chain
|
|
150
|
+
*/
|
|
151
|
+
registerProvider(chain, provider) {
|
|
152
|
+
this.providers.set(chain, provider);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get current gas price for a chain
|
|
156
|
+
*/
|
|
157
|
+
async getGasPrice(chain) {
|
|
158
|
+
const cached = this.cache.get(chain);
|
|
159
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
160
|
+
return cached.data;
|
|
161
|
+
}
|
|
162
|
+
const provider = this.providers.get(chain);
|
|
163
|
+
let gasPrice;
|
|
164
|
+
if (provider) {
|
|
165
|
+
gasPrice = await provider.getGasPrice(chain);
|
|
166
|
+
} else {
|
|
167
|
+
gasPrice = this.getMockGasPrice(chain);
|
|
168
|
+
}
|
|
169
|
+
this.cache.set(chain, {
|
|
170
|
+
data: gasPrice,
|
|
171
|
+
expiresAt: Date.now() + this.config.cacheTtl
|
|
172
|
+
});
|
|
173
|
+
this.addToHistory(chain, gasPrice);
|
|
174
|
+
return gasPrice;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get gas prices for multiple chains
|
|
178
|
+
*/
|
|
179
|
+
async getMultiChainGasPrices(chains) {
|
|
180
|
+
const results = /* @__PURE__ */ new Map();
|
|
181
|
+
await Promise.all(
|
|
182
|
+
chains.map(async (chain) => {
|
|
183
|
+
try {
|
|
184
|
+
const price = await this.getGasPrice(chain);
|
|
185
|
+
results.set(chain, price);
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
return results;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Calculate gas cost in USD
|
|
194
|
+
*/
|
|
195
|
+
async calculateGasCostUsd(chain, gasUnits, speed = "standard") {
|
|
196
|
+
const gasPrice = await this.getGasPrice(chain);
|
|
197
|
+
const priceWei = BigInt(gasPrice[speed]);
|
|
198
|
+
const units = BigInt(gasUnits);
|
|
199
|
+
const costNative = (priceWei * units).toString();
|
|
200
|
+
const nativePrice = parseFloat(gasPrice.nativeTokenPriceUsd);
|
|
201
|
+
const costUsd = (Number(costNative) * nativePrice / 1e18).toFixed(6);
|
|
202
|
+
return { costNative, costUsd };
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Predict future gas prices (simple moving average)
|
|
206
|
+
*/
|
|
207
|
+
predictGasPrice(chain, horizonMinutes = 5) {
|
|
208
|
+
if (!this.config.enablePrediction) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
const history = this.history.get(chain);
|
|
212
|
+
if (!history || history.length < 5) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
const currentPrice = history[history.length - 1];
|
|
216
|
+
const recentPrices = history.slice(-20).map((h) => BigInt(h.standard));
|
|
217
|
+
const sum = recentPrices.reduce((a, b) => a + b, 0n);
|
|
218
|
+
const avg = sum / BigInt(recentPrices.length);
|
|
219
|
+
const variance = recentPrices.reduce(
|
|
220
|
+
(acc, p) => acc + (p - avg) * (p - avg),
|
|
221
|
+
0n
|
|
222
|
+
) / BigInt(recentPrices.length);
|
|
223
|
+
const stdDev = BigInt(Math.floor(Math.sqrt(Number(variance))));
|
|
224
|
+
const predictions = [];
|
|
225
|
+
const intervals = [1, 2, 5, 10, 15];
|
|
226
|
+
for (const mins of intervals) {
|
|
227
|
+
if (mins > horizonMinutes) break;
|
|
228
|
+
const decay = 0.9 ** mins;
|
|
229
|
+
const predicted = avg + BigInt(Math.floor(Number(BigInt(currentPrice.standard) - avg) * decay));
|
|
230
|
+
predictions.push({
|
|
231
|
+
timestamp: Date.now() + mins * 60 * 1e3,
|
|
232
|
+
predictedPrice: predicted.toString(),
|
|
233
|
+
confidence: Math.max(50, 95 - mins * 5),
|
|
234
|
+
lowerBound: (predicted - stdDev * 2n).toString(),
|
|
235
|
+
upperBound: (predicted + stdDev * 2n).toString()
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
return {
|
|
239
|
+
chain,
|
|
240
|
+
currentPrice: currentPrice.standard,
|
|
241
|
+
predictions,
|
|
242
|
+
model: "simple_moving_average",
|
|
243
|
+
lastUpdated: Date.now()
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get recommended gas price based on urgency
|
|
248
|
+
*/
|
|
249
|
+
async getRecommendedGasPrice(chain, urgency) {
|
|
250
|
+
const gasPrice = await this.getGasPrice(chain);
|
|
251
|
+
const mapping = {
|
|
252
|
+
low: { speed: "slow", time: 300 },
|
|
253
|
+
medium: { speed: "standard", time: 60 },
|
|
254
|
+
high: { speed: "fast", time: 15 },
|
|
255
|
+
critical: { speed: "instant", time: 5 }
|
|
256
|
+
};
|
|
257
|
+
const { speed, time } = mapping[urgency];
|
|
258
|
+
return {
|
|
259
|
+
price: gasPrice[speed],
|
|
260
|
+
estimatedTime: time
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Get gas history for a chain
|
|
265
|
+
*/
|
|
266
|
+
getHistory(chain) {
|
|
267
|
+
return this.history.get(chain) ?? [];
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Clear cache
|
|
271
|
+
*/
|
|
272
|
+
clearCache() {
|
|
273
|
+
this.cache.clear();
|
|
274
|
+
}
|
|
275
|
+
addToHistory(chain, price) {
|
|
276
|
+
if (!this.history.has(chain)) {
|
|
277
|
+
this.history.set(chain, []);
|
|
278
|
+
}
|
|
279
|
+
const history = this.history.get(chain);
|
|
280
|
+
history.push(price);
|
|
281
|
+
if (history.length > this.maxHistorySize) {
|
|
282
|
+
history.shift();
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
getMockGasPrice(chain) {
|
|
286
|
+
const defaults = {
|
|
287
|
+
"eip155:1": {
|
|
288
|
+
// Ethereum
|
|
289
|
+
slow: "15000000000",
|
|
290
|
+
// 15 gwei
|
|
291
|
+
standard: "25000000000",
|
|
292
|
+
// 25 gwei
|
|
293
|
+
fast: "40000000000",
|
|
294
|
+
// 40 gwei
|
|
295
|
+
instant: "60000000000",
|
|
296
|
+
// 60 gwei
|
|
297
|
+
nativeTokenPriceUsd: "2500",
|
|
298
|
+
baseFee: "20000000000",
|
|
299
|
+
priorityFee: {
|
|
300
|
+
slow: "1000000000",
|
|
301
|
+
standard: "2000000000",
|
|
302
|
+
fast: "5000000000",
|
|
303
|
+
instant: "10000000000"
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
"eip155:8453": {
|
|
307
|
+
// Base
|
|
308
|
+
slow: "100000000",
|
|
309
|
+
// 0.1 gwei
|
|
310
|
+
standard: "150000000",
|
|
311
|
+
// 0.15 gwei
|
|
312
|
+
fast: "250000000",
|
|
313
|
+
// 0.25 gwei
|
|
314
|
+
instant: "500000000",
|
|
315
|
+
// 0.5 gwei
|
|
316
|
+
nativeTokenPriceUsd: "2500"
|
|
317
|
+
},
|
|
318
|
+
"eip155:42161": {
|
|
319
|
+
// Arbitrum
|
|
320
|
+
slow: "10000000",
|
|
321
|
+
// 0.01 gwei
|
|
322
|
+
standard: "100000000",
|
|
323
|
+
// 0.1 gwei
|
|
324
|
+
fast: "200000000",
|
|
325
|
+
// 0.2 gwei
|
|
326
|
+
instant: "500000000",
|
|
327
|
+
// 0.5 gwei
|
|
328
|
+
nativeTokenPriceUsd: "2500"
|
|
329
|
+
},
|
|
330
|
+
"eip155:137": {
|
|
331
|
+
// Polygon
|
|
332
|
+
slow: "30000000000",
|
|
333
|
+
// 30 gwei
|
|
334
|
+
standard: "50000000000",
|
|
335
|
+
// 50 gwei
|
|
336
|
+
fast: "80000000000",
|
|
337
|
+
// 80 gwei
|
|
338
|
+
instant: "150000000000",
|
|
339
|
+
// 150 gwei
|
|
340
|
+
nativeTokenPriceUsd: "0.5"
|
|
341
|
+
},
|
|
342
|
+
"solana:mainnet": {
|
|
343
|
+
slow: "5000",
|
|
344
|
+
// 5000 lamports
|
|
345
|
+
standard: "10000",
|
|
346
|
+
// 10000 lamports
|
|
347
|
+
fast: "25000",
|
|
348
|
+
// 25000 lamports
|
|
349
|
+
instant: "50000",
|
|
350
|
+
// 50000 lamports
|
|
351
|
+
nativeTokenPriceUsd: "150"
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
const chainDefault = defaults[chain] ?? {
|
|
355
|
+
slow: "1000000000",
|
|
356
|
+
standard: "2000000000",
|
|
357
|
+
fast: "5000000000",
|
|
358
|
+
instant: "10000000000",
|
|
359
|
+
nativeTokenPriceUsd: "1"
|
|
360
|
+
};
|
|
361
|
+
return {
|
|
362
|
+
chain,
|
|
363
|
+
slow: chainDefault.slow,
|
|
364
|
+
standard: chainDefault.standard,
|
|
365
|
+
fast: chainDefault.fast,
|
|
366
|
+
instant: chainDefault.instant,
|
|
367
|
+
nativeTokenPriceUsd: chainDefault.nativeTokenPriceUsd,
|
|
368
|
+
baseFee: chainDefault.baseFee,
|
|
369
|
+
priorityFee: chainDefault.priorityFee,
|
|
370
|
+
lastBlock: 0,
|
|
371
|
+
timestamp: Date.now()
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
var MockGasProvider = class {
|
|
376
|
+
chains;
|
|
377
|
+
volatility;
|
|
378
|
+
constructor(chains, volatility = 0.1) {
|
|
379
|
+
this.chains = chains;
|
|
380
|
+
this.volatility = volatility;
|
|
381
|
+
}
|
|
382
|
+
async getGasPrice(chain) {
|
|
383
|
+
const variance = 1 + (Math.random() - 0.5) * this.volatility;
|
|
384
|
+
const basePrice = 1000000000n;
|
|
385
|
+
const adjusted = BigInt(Math.floor(Number(basePrice) * variance));
|
|
386
|
+
return {
|
|
387
|
+
chain,
|
|
388
|
+
slow: adjusted.toString(),
|
|
389
|
+
standard: (adjusted * 2n).toString(),
|
|
390
|
+
fast: (adjusted * 4n).toString(),
|
|
391
|
+
instant: (adjusted * 8n).toString(),
|
|
392
|
+
nativeTokenPriceUsd: "2500",
|
|
393
|
+
lastBlock: Math.floor(Math.random() * 1e6),
|
|
394
|
+
timestamp: Date.now()
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
supportedChains() {
|
|
398
|
+
return this.chains;
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/pricing/fees.ts
|
|
403
|
+
var FeeEstimator = class {
|
|
404
|
+
gasOracle;
|
|
405
|
+
protocolFees = /* @__PURE__ */ new Map();
|
|
406
|
+
tokenPrices = /* @__PURE__ */ new Map();
|
|
407
|
+
config;
|
|
408
|
+
// Default gas estimates for different operations
|
|
409
|
+
GAS_ESTIMATES = {
|
|
410
|
+
transfer: "65000",
|
|
411
|
+
approve: "46000",
|
|
412
|
+
swap_simple: "150000",
|
|
413
|
+
swap_complex: "300000",
|
|
414
|
+
bridge: "200000",
|
|
415
|
+
wrap: "45000",
|
|
416
|
+
unwrap: "45000",
|
|
417
|
+
deposit: "100000",
|
|
418
|
+
withdraw: "100000"
|
|
419
|
+
};
|
|
420
|
+
constructor(gasOracle, config = {}) {
|
|
421
|
+
this.gasOracle = gasOracle;
|
|
422
|
+
this.config = {
|
|
423
|
+
defaultSlippage: config.defaultSlippage ?? "0.5",
|
|
424
|
+
cacheTtl: config.cacheTtl ?? 6e4
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Estimate fees for an operation
|
|
429
|
+
*/
|
|
430
|
+
async estimateFees(request) {
|
|
431
|
+
const parsedRequest = FeeEstimateRequest.parse(request);
|
|
432
|
+
const gasUnits = this.getGasEstimate(
|
|
433
|
+
parsedRequest.operation,
|
|
434
|
+
parsedRequest.protocol
|
|
435
|
+
);
|
|
436
|
+
const { costNative, costUsd: gasCostUsd } = await this.gasOracle.calculateGasCostUsd(
|
|
437
|
+
parsedRequest.chain,
|
|
438
|
+
gasUnits,
|
|
439
|
+
parsedRequest.gasSpeed
|
|
440
|
+
);
|
|
441
|
+
const { fee: protocolFee, feeUsd: protocolFeeUsd } = await this.calculateProtocolFee(
|
|
442
|
+
parsedRequest.chain,
|
|
443
|
+
parsedRequest.protocol,
|
|
444
|
+
parsedRequest.operation,
|
|
445
|
+
parsedRequest.amount
|
|
446
|
+
);
|
|
447
|
+
const totalFeeUsd = (parseFloat(gasCostUsd) + parseFloat(protocolFeeUsd)).toFixed(6);
|
|
448
|
+
const breakdown = [
|
|
449
|
+
{ type: "gas", amount: costNative, amountUsd: gasCostUsd }
|
|
450
|
+
];
|
|
451
|
+
if (parseFloat(protocolFee) > 0) {
|
|
452
|
+
breakdown.push({ type: "protocol", amount: protocolFee, amountUsd: protocolFeeUsd });
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
gasUnits,
|
|
456
|
+
gasPriceWei: (await this.gasOracle.getGasPrice(parsedRequest.chain))[parsedRequest.gasSpeed],
|
|
457
|
+
gasCostNative: costNative,
|
|
458
|
+
gasCostUsd,
|
|
459
|
+
protocolFee,
|
|
460
|
+
protocolFeeUsd,
|
|
461
|
+
totalFeeUsd,
|
|
462
|
+
confidence: 85,
|
|
463
|
+
// Base confidence
|
|
464
|
+
breakdown
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Estimate total route fees
|
|
469
|
+
*/
|
|
470
|
+
async estimateRouteFees(steps, gasSpeed = "standard") {
|
|
471
|
+
const stepEstimates = [];
|
|
472
|
+
let totalGasUsd = 0;
|
|
473
|
+
let totalProtocolFeesUsd = 0;
|
|
474
|
+
for (const step of steps) {
|
|
475
|
+
const estimate = await this.estimateFees({
|
|
476
|
+
chain: step.chain,
|
|
477
|
+
operation: step.operation,
|
|
478
|
+
protocol: step.protocol,
|
|
479
|
+
inputAsset: step.inputAsset,
|
|
480
|
+
outputAsset: step.outputAsset,
|
|
481
|
+
amount: step.amount,
|
|
482
|
+
gasSpeed
|
|
483
|
+
});
|
|
484
|
+
stepEstimates.push(estimate);
|
|
485
|
+
totalGasUsd += parseFloat(estimate.gasCostUsd);
|
|
486
|
+
totalProtocolFeesUsd += parseFloat(estimate.protocolFeeUsd);
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
totalGasUsd: totalGasUsd.toFixed(6),
|
|
490
|
+
totalProtocolFeesUsd: totalProtocolFeesUsd.toFixed(6),
|
|
491
|
+
totalFeesUsd: (totalGasUsd + totalProtocolFeesUsd).toFixed(6),
|
|
492
|
+
stepEstimates
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Get a swap quote
|
|
497
|
+
*/
|
|
498
|
+
async getSwapQuote(chain, dex, inputAsset, outputAsset, inputAmount, slippage) {
|
|
499
|
+
const slippagePercent = parseFloat(slippage ?? this.config.defaultSlippage) / 100;
|
|
500
|
+
const inputPrice = await this.getTokenPrice(chain, inputAsset);
|
|
501
|
+
const outputPrice = await this.getTokenPrice(chain, outputAsset);
|
|
502
|
+
const inputValueUsd = parseFloat(inputAmount) * parseFloat(inputPrice.priceUsd);
|
|
503
|
+
const feePercent = this.getProtocolFeePercent(dex);
|
|
504
|
+
const outputValueUsd = inputValueUsd * (1 - feePercent);
|
|
505
|
+
const outputAmount = (outputValueUsd / parseFloat(outputPrice.priceUsd)).toString();
|
|
506
|
+
const minOutput = (parseFloat(outputAmount) * (1 - slippagePercent)).toFixed(0);
|
|
507
|
+
const priceImpact = (feePercent * 100).toFixed(4);
|
|
508
|
+
const gasEstimate = this.GAS_ESTIMATES.swap_simple;
|
|
509
|
+
await this.gasOracle.calculateGasCostUsd(chain, gasEstimate);
|
|
510
|
+
return {
|
|
511
|
+
dex,
|
|
512
|
+
chain,
|
|
513
|
+
inputAsset,
|
|
514
|
+
outputAsset,
|
|
515
|
+
inputAmount,
|
|
516
|
+
outputAmount,
|
|
517
|
+
minOutputAmount: minOutput,
|
|
518
|
+
priceImpact,
|
|
519
|
+
fee: (parseFloat(inputAmount) * feePercent).toFixed(0),
|
|
520
|
+
feeUsd: (inputValueUsd * feePercent).toFixed(6),
|
|
521
|
+
route: [inputAsset, outputAsset],
|
|
522
|
+
estimatedGas: gasEstimate,
|
|
523
|
+
expiresAt: Date.now() + 6e4
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Get a bridge quote
|
|
528
|
+
*/
|
|
529
|
+
async getBridgeQuote(bridge, sourceChain, destinationChain, sourceAsset, destinationAsset, inputAmount) {
|
|
530
|
+
const feePercent = 1e-3;
|
|
531
|
+
const minFee = 1e5;
|
|
532
|
+
const fee = Math.max(
|
|
533
|
+
minFee,
|
|
534
|
+
Math.floor(parseFloat(inputAmount) * feePercent)
|
|
535
|
+
);
|
|
536
|
+
const outputAmount = (parseFloat(inputAmount) - fee).toFixed(0);
|
|
537
|
+
const tokenPrice = await this.getTokenPrice(sourceChain, sourceAsset);
|
|
538
|
+
const feeUsd = (fee * parseFloat(tokenPrice.priceUsd) / 1e6).toFixed(6);
|
|
539
|
+
const estimatedTime = this.getBridgeEstimatedTime(bridge, sourceChain, destinationChain);
|
|
540
|
+
return {
|
|
541
|
+
bridge,
|
|
542
|
+
sourceChain,
|
|
543
|
+
destinationChain,
|
|
544
|
+
sourceAsset,
|
|
545
|
+
destinationAsset,
|
|
546
|
+
inputAmount,
|
|
547
|
+
outputAmount,
|
|
548
|
+
fee: fee.toString(),
|
|
549
|
+
feeUsd,
|
|
550
|
+
estimatedTime,
|
|
551
|
+
confidence: 90,
|
|
552
|
+
expiresAt: Date.now() + 12e4
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Register protocol fee structure
|
|
557
|
+
*/
|
|
558
|
+
registerProtocolFees(fees) {
|
|
559
|
+
const key = `${fees.protocol}:${fees.chain}`;
|
|
560
|
+
this.protocolFees.set(key, fees);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Update token price
|
|
564
|
+
*/
|
|
565
|
+
updateTokenPrice(price) {
|
|
566
|
+
const key = `${price.chain}:${price.asset}`;
|
|
567
|
+
this.tokenPrices.set(key, price);
|
|
568
|
+
}
|
|
569
|
+
getGasEstimate(operation, _protocol) {
|
|
570
|
+
const key = operation.toLowerCase();
|
|
571
|
+
return this.GAS_ESTIMATES[key] ?? this.GAS_ESTIMATES.transfer;
|
|
572
|
+
}
|
|
573
|
+
async calculateProtocolFee(_chain, protocol, operation, amount) {
|
|
574
|
+
if (!protocol) {
|
|
575
|
+
return { fee: "0", feeUsd: "0" };
|
|
576
|
+
}
|
|
577
|
+
const feePercent = this.getProtocolFeePercent(protocol);
|
|
578
|
+
if (operation === "transfer" || operation === "approve") {
|
|
579
|
+
return { fee: "0", feeUsd: "0" };
|
|
580
|
+
}
|
|
581
|
+
const fee = Math.floor(parseFloat(amount) * feePercent);
|
|
582
|
+
const feeUsd = (fee / 1e6).toFixed(6);
|
|
583
|
+
return { fee: fee.toString(), feeUsd };
|
|
584
|
+
}
|
|
585
|
+
getProtocolFeePercent(protocol) {
|
|
586
|
+
const fees = {
|
|
587
|
+
uniswap: 3e-3,
|
|
588
|
+
sushiswap: 3e-3,
|
|
589
|
+
curve: 4e-4,
|
|
590
|
+
balancer: 2e-3,
|
|
591
|
+
"1inch": 0,
|
|
592
|
+
paraswap: 0,
|
|
593
|
+
layerzero: 1e-3,
|
|
594
|
+
wormhole: 1e-3,
|
|
595
|
+
stargate: 6e-4
|
|
596
|
+
};
|
|
597
|
+
return fees[protocol.toLowerCase()] ?? 3e-3;
|
|
598
|
+
}
|
|
599
|
+
async getTokenPrice(chain, asset) {
|
|
600
|
+
const key = `${chain}:${asset}`;
|
|
601
|
+
const cached = this.tokenPrices.get(key);
|
|
602
|
+
if (cached && cached.timestamp > Date.now() - this.config.cacheTtl) {
|
|
603
|
+
return cached;
|
|
604
|
+
}
|
|
605
|
+
const isStablecoin = asset.toLowerCase().includes("usdt") || asset.toLowerCase().includes("usdc") || asset.toLowerCase().includes("dai");
|
|
606
|
+
const price = {
|
|
607
|
+
chain,
|
|
608
|
+
asset,
|
|
609
|
+
priceUsd: isStablecoin ? "1.0" : "1.0",
|
|
610
|
+
source: "mock",
|
|
611
|
+
timestamp: Date.now(),
|
|
612
|
+
confidence: 95
|
|
613
|
+
};
|
|
614
|
+
this.tokenPrices.set(key, price);
|
|
615
|
+
return price;
|
|
616
|
+
}
|
|
617
|
+
getBridgeEstimatedTime(bridge, sourceChain, destinationChain) {
|
|
618
|
+
const bridgeTimes = {
|
|
619
|
+
layerzero: 60,
|
|
620
|
+
wormhole: 120,
|
|
621
|
+
stargate: 30,
|
|
622
|
+
hop: 300,
|
|
623
|
+
across: 60,
|
|
624
|
+
celer: 600,
|
|
625
|
+
multichain: 600
|
|
626
|
+
};
|
|
627
|
+
let baseTime = bridgeTimes[bridge.toLowerCase()] ?? 300;
|
|
628
|
+
if (sourceChain.includes("eip155:1") || destinationChain.includes("eip155:1")) {
|
|
629
|
+
baseTime += 120;
|
|
630
|
+
}
|
|
631
|
+
return baseTime;
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
function calculateMinOutput(amount, slippagePercent) {
|
|
635
|
+
const slippage = parseFloat(slippagePercent) / 100;
|
|
636
|
+
const minOutput = BigInt(amount) * BigInt(Math.floor((1 - slippage) * 1e4)) / 10000n;
|
|
637
|
+
return minOutput.toString();
|
|
638
|
+
}
|
|
639
|
+
function calculatePriceImpact(amount, liquidity) {
|
|
640
|
+
if (BigInt(liquidity) === 0n) {
|
|
641
|
+
return "100";
|
|
642
|
+
}
|
|
643
|
+
const impact = Number(BigInt(amount) * 10000n / BigInt(liquidity)) / 100;
|
|
644
|
+
return impact.toFixed(4);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
export {
|
|
648
|
+
GasPrice,
|
|
649
|
+
TokenPrice,
|
|
650
|
+
ProtocolFees,
|
|
651
|
+
BridgeQuote,
|
|
652
|
+
SwapQuote,
|
|
653
|
+
FeeEstimateRequest,
|
|
654
|
+
FeeEstimate,
|
|
655
|
+
PricePrediction,
|
|
656
|
+
GasOracle,
|
|
657
|
+
MockGasProvider,
|
|
658
|
+
FeeEstimator,
|
|
659
|
+
calculateMinOutput,
|
|
660
|
+
calculatePriceImpact
|
|
661
|
+
};
|
|
662
|
+
//# sourceMappingURL=chunk-BUGQ6VOQ.js.map
|