@megaflow-labs/sdk 0.1.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/CHANGELOG.md +47 -0
- package/README.md +259 -0
- package/dist/index.d.mts +1641 -0
- package/dist/index.d.ts +1641 -0
- package/dist/index.js +1375 -0
- package/dist/index.mjs +1326 -0
- package/package.json +66 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1375 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
ERC20_ABI: () => ERC20_ABI,
|
|
24
|
+
ERC721_ABI: () => ERC721_ABI,
|
|
25
|
+
KYBERSWAP_API_BASE: () => KYBERSWAP_API_BASE,
|
|
26
|
+
MEGAETH_BASE_FEE: () => MEGAETH_BASE_FEE,
|
|
27
|
+
MEGAETH_GAS_LIMITS: () => MEGAETH_GAS_LIMITS,
|
|
28
|
+
MEGAETH_L1_BRIDGE: () => MEGAETH_L1_BRIDGE,
|
|
29
|
+
MEGAETH_TOKENS: () => MEGAETH_TOKENS,
|
|
30
|
+
MEGA_ROUTER_ABI: () => MEGA_ROUTER_ABI,
|
|
31
|
+
MegaFlowBuilder: () => MegaFlowBuilder,
|
|
32
|
+
MegaFlowClient: () => MegaFlowClient,
|
|
33
|
+
MegaFlowError: () => MegaFlowError,
|
|
34
|
+
UNISWAP_V2_ROUTER_ABI: () => UNISWAP_V2_ROUTER_ABI,
|
|
35
|
+
WETH_ABI: () => WETH_ABI,
|
|
36
|
+
applyMegaethGasBuffer: () => applyMegaethGasBuffer,
|
|
37
|
+
buildKyberSwap: () => buildKyberSwap,
|
|
38
|
+
buildSafeApproveCalls: () => buildSafeApproveCalls,
|
|
39
|
+
calculateTotalValue: () => calculateTotalValue,
|
|
40
|
+
chainById: () => chainById,
|
|
41
|
+
chunkCalls: () => chunkCalls,
|
|
42
|
+
createMegaFlow: () => createMegaFlow,
|
|
43
|
+
createMegaFlowClient: () => createMegaFlowClient,
|
|
44
|
+
createMegaFlowClientMainnet: () => createMegaFlowClientMainnet,
|
|
45
|
+
createMegaFlowClientTestnet: () => createMegaFlowClientTestnet,
|
|
46
|
+
createMegaFlowMainnet: () => createMegaFlowMainnet,
|
|
47
|
+
createMegaFlowTestnet: () => createMegaFlowTestnet,
|
|
48
|
+
deadlineInMinutes: () => deadlineInMinutes,
|
|
49
|
+
getKyberQuote: () => getKyberQuote,
|
|
50
|
+
isUserRejection: () => isUserRejection,
|
|
51
|
+
isValidAddress: () => isValidAddress,
|
|
52
|
+
isValidHex: () => isValidHex,
|
|
53
|
+
megaethChains: () => megaethChains,
|
|
54
|
+
megaethMainnet: () => megaethMainnet,
|
|
55
|
+
megaethTestnet: () => megaethTestnet,
|
|
56
|
+
parseBatchExecutedEvent: () => parseBatchExecutedEvent,
|
|
57
|
+
parseCallExecutedEvents: () => parseCallExecutedEvents,
|
|
58
|
+
parseCallResults: () => parseCallResults
|
|
59
|
+
});
|
|
60
|
+
module.exports = __toCommonJS(index_exports);
|
|
61
|
+
|
|
62
|
+
// src/chains.ts
|
|
63
|
+
var megaethMainnet = {
|
|
64
|
+
id: 4326,
|
|
65
|
+
name: "MegaETH Mainnet",
|
|
66
|
+
nativeCurrency: {
|
|
67
|
+
decimals: 18,
|
|
68
|
+
name: "Ether",
|
|
69
|
+
symbol: "ETH"
|
|
70
|
+
},
|
|
71
|
+
rpcUrls: {
|
|
72
|
+
default: {
|
|
73
|
+
http: ["https://mainnet.megaeth.com/rpc"],
|
|
74
|
+
webSocket: ["wss://mainnet.megaeth.com/ws"]
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
blockExplorers: {
|
|
78
|
+
default: {
|
|
79
|
+
name: "MegaETH Explorer",
|
|
80
|
+
url: "https://mega.etherscan.io"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var megaethTestnet = {
|
|
85
|
+
id: 6343,
|
|
86
|
+
name: "MegaETH Testnet",
|
|
87
|
+
nativeCurrency: {
|
|
88
|
+
decimals: 18,
|
|
89
|
+
name: "Ether",
|
|
90
|
+
symbol: "ETH"
|
|
91
|
+
},
|
|
92
|
+
rpcUrls: {
|
|
93
|
+
default: {
|
|
94
|
+
http: ["https://carrot.megaeth.com/rpc"],
|
|
95
|
+
webSocket: ["wss://carrot.megaeth.com/ws"]
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
blockExplorers: {
|
|
99
|
+
default: {
|
|
100
|
+
name: "MegaETH Testnet Explorer",
|
|
101
|
+
url: "https://megaeth-testnet-v2.blockscout.com"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
testnet: true
|
|
105
|
+
};
|
|
106
|
+
var megaethChains = [megaethMainnet, megaethTestnet];
|
|
107
|
+
var chainById = {
|
|
108
|
+
[megaethMainnet.id]: megaethMainnet,
|
|
109
|
+
[megaethTestnet.id]: megaethTestnet
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/constants.ts
|
|
113
|
+
var import_viem = require("viem");
|
|
114
|
+
var MEGA_ROUTER_ABI = (0, import_viem.parseAbi)([
|
|
115
|
+
// Core batch execution
|
|
116
|
+
"function executeBatch((address target, uint256 value, bytes callData)[] calls) payable returns ((bool success, bytes returnData)[] results)",
|
|
117
|
+
"function executeBatchWithOutput((address target, uint256 value, bytes callData)[] calls, uint256 outputCallIndex, uint256 outputOffset, uint256 outputLength) payable returns ((bool success, bytes returnData)[] results, bytes extractedOutput)",
|
|
118
|
+
// View helpers
|
|
119
|
+
"function calculateRequiredETH((address target, uint256 value, bytes callData)[] calls) view returns (uint256)",
|
|
120
|
+
"function flatFee() view returns (uint256)",
|
|
121
|
+
"function feeCollector() view returns (address)",
|
|
122
|
+
// Events
|
|
123
|
+
"event BatchExecuted(address indexed sender, uint256 callCount, uint256 totalValue)",
|
|
124
|
+
"event CallExecuted(uint256 indexed callIndex, address indexed target, uint256 value, bool success)",
|
|
125
|
+
// Errors (for decoding reverts)
|
|
126
|
+
"error ReentrancyGuard()",
|
|
127
|
+
"error InsufficientETH(uint256 required, uint256 provided)",
|
|
128
|
+
"error InvalidTarget(uint256 callIndex)",
|
|
129
|
+
"error EmptyCalldata(uint256 callIndex)",
|
|
130
|
+
"error CallFailed(uint256 callIndex, bytes returnData)",
|
|
131
|
+
"error FeeTransferFailed()",
|
|
132
|
+
"error SweepFailed()"
|
|
133
|
+
]);
|
|
134
|
+
var ERC20_ABI = (0, import_viem.parseAbi)([
|
|
135
|
+
"function name() view returns (string)",
|
|
136
|
+
"function symbol() view returns (string)",
|
|
137
|
+
"function decimals() view returns (uint8)",
|
|
138
|
+
"function totalSupply() view returns (uint256)",
|
|
139
|
+
"function balanceOf(address account) view returns (uint256)",
|
|
140
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
141
|
+
"function transfer(address to, uint256 amount) returns (bool)",
|
|
142
|
+
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
143
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
144
|
+
"event Transfer(address indexed from, address indexed to, uint256 value)",
|
|
145
|
+
"event Approval(address indexed owner, address indexed spender, uint256 value)"
|
|
146
|
+
]);
|
|
147
|
+
var ERC721_ABI = (0, import_viem.parseAbi)([
|
|
148
|
+
"function name() view returns (string)",
|
|
149
|
+
"function symbol() view returns (string)",
|
|
150
|
+
"function tokenURI(uint256 tokenId) view returns (string)",
|
|
151
|
+
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
152
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
153
|
+
"function isApprovedForAll(address owner, address operator) view returns (bool)",
|
|
154
|
+
"function getApproved(uint256 tokenId) view returns (address)",
|
|
155
|
+
"function transferFrom(address from, address to, uint256 tokenId)",
|
|
156
|
+
"function safeTransferFrom(address from, address to, uint256 tokenId)",
|
|
157
|
+
"function approve(address to, uint256 tokenId)",
|
|
158
|
+
"function setApprovalForAll(address operator, bool approved)"
|
|
159
|
+
]);
|
|
160
|
+
var UNISWAP_V2_ROUTER_ABI = (0, import_viem.parseAbi)([
|
|
161
|
+
"function factory() pure returns (address)",
|
|
162
|
+
"function WETH() pure returns (address)",
|
|
163
|
+
"function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] path, address to, uint deadline) returns (uint[] amounts)",
|
|
164
|
+
"function swapTokensForExactTokens(uint amountOut, uint amountInMax, address[] path, address to, uint deadline) returns (uint[] amounts)",
|
|
165
|
+
"function swapExactETHForTokens(uint amountOutMin, address[] path, address to, uint deadline) payable returns (uint[] amounts)",
|
|
166
|
+
"function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] path, address to, uint deadline) returns (uint[] amounts)",
|
|
167
|
+
"function swapETHForExactTokens(uint amountOut, address[] path, address to, uint deadline) payable returns (uint[] amounts)",
|
|
168
|
+
"function getAmountsOut(uint amountIn, address[] path) view returns (uint[] amounts)",
|
|
169
|
+
"function getAmountsIn(uint amountOut, address[] path) view returns (uint[] amounts)",
|
|
170
|
+
"function addLiquidity(address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline) returns (uint amountA, uint amountB, uint liquidity)",
|
|
171
|
+
"function removeLiquidity(address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline) returns (uint amountA, uint amountB)"
|
|
172
|
+
]);
|
|
173
|
+
var WETH_ABI = (0, import_viem.parseAbi)([
|
|
174
|
+
"function name() view returns (string)",
|
|
175
|
+
"function symbol() view returns (string)",
|
|
176
|
+
"function decimals() view returns (uint8)",
|
|
177
|
+
"function totalSupply() view returns (uint256)",
|
|
178
|
+
"function balanceOf(address account) view returns (uint256)",
|
|
179
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
180
|
+
"function transfer(address to, uint256 amount) returns (bool)",
|
|
181
|
+
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
182
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
183
|
+
"function deposit() payable",
|
|
184
|
+
"function withdraw(uint256 wad)",
|
|
185
|
+
"event Transfer(address indexed from, address indexed to, uint256 value)",
|
|
186
|
+
"event Approval(address indexed owner, address indexed spender, uint256 value)"
|
|
187
|
+
]);
|
|
188
|
+
var MEGAETH_GAS_LIMITS = {
|
|
189
|
+
/** Simple ETH transfer — 60k on MegaETH, not 21k */
|
|
190
|
+
TRANSFER: 60000n,
|
|
191
|
+
/** ERC20 transfer */
|
|
192
|
+
ERC20_TRANSFER: 100000n,
|
|
193
|
+
/** ERC20 approve */
|
|
194
|
+
ERC20_APPROVE: 80000n,
|
|
195
|
+
/** DEX swap */
|
|
196
|
+
SWAP: 350000n
|
|
197
|
+
};
|
|
198
|
+
var MEGAETH_BASE_FEE = 1000000n;
|
|
199
|
+
var MAINNET_ROUTER_ADDRESS = "0x9c1528e7688ee7122781dc8ed05686383b763ccb";
|
|
200
|
+
var MEGAETH_TOKENS = {
|
|
201
|
+
/** Wrapped ETH */
|
|
202
|
+
WETH: "0x4200000000000000000000000000000000000006",
|
|
203
|
+
/** MEGA governance token */
|
|
204
|
+
MEGA: "0x28B7E77f82B25B95953825F1E3eA0E36c1c29861",
|
|
205
|
+
/** USDM stablecoin */
|
|
206
|
+
USDM: "0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7"
|
|
207
|
+
};
|
|
208
|
+
var MEGAETH_L1_BRIDGE = "0x0CA3A2FBC3D770b578223FBB6b062fa875a2eE75";
|
|
209
|
+
var KYBERSWAP_API_BASE = "https://aggregator-api.kyberswap.com/megaeth/api/v1";
|
|
210
|
+
|
|
211
|
+
// src/utils.ts
|
|
212
|
+
var import_viem2 = require("viem");
|
|
213
|
+
function decodeRevertError(returnData) {
|
|
214
|
+
if (!returnData || returnData === "0x" || returnData.length < 10) {
|
|
215
|
+
return "Execution reverted with no data";
|
|
216
|
+
}
|
|
217
|
+
const selector = returnData.slice(0, 10).toLowerCase();
|
|
218
|
+
if (selector === "0x08c379a0") {
|
|
219
|
+
try {
|
|
220
|
+
const [message] = (0, import_viem2.decodeAbiParameters)(
|
|
221
|
+
[{ type: "string" }],
|
|
222
|
+
`0x${returnData.slice(10)}`
|
|
223
|
+
);
|
|
224
|
+
return `Reverted: ${message}`;
|
|
225
|
+
} catch {
|
|
226
|
+
return `Reverted (Error selector found but decode failed)`;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (selector === "0x4e487b71") {
|
|
230
|
+
try {
|
|
231
|
+
const [code] = (0, import_viem2.decodeAbiParameters)(
|
|
232
|
+
[{ type: "uint256" }],
|
|
233
|
+
`0x${returnData.slice(10)}`
|
|
234
|
+
);
|
|
235
|
+
return `Panic: ${decodePanicCode(Number(code))}`;
|
|
236
|
+
} catch {
|
|
237
|
+
return "Panic: unknown code";
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const customErrors = {
|
|
241
|
+
"0x3e3f8f73": "ReentrancyGuard: re-entrant call",
|
|
242
|
+
"0xfe9ba5cd": "InsufficientETH: not enough ETH sent",
|
|
243
|
+
"0xd4a5e2bf": "InsufficientFee: flat fee not covered",
|
|
244
|
+
"0x1425f8f8": "InvalidTarget: call target is address(0)",
|
|
245
|
+
"0x4a3f0f2d": "EmptyCalldata: callData must not be empty",
|
|
246
|
+
"0x85d9d0b8": "CallFailed: one of the batch calls reverted",
|
|
247
|
+
"0xb5e9e6d2": "FeeTransferFailed: could not send fee to collector",
|
|
248
|
+
"0x750b219c": "SweepFailed: could not return leftover ETH"
|
|
249
|
+
};
|
|
250
|
+
if (customErrors[selector]) {
|
|
251
|
+
if (selector === "0x85d9d0b8") {
|
|
252
|
+
try {
|
|
253
|
+
const [callIndex, innerData] = (0, import_viem2.decodeAbiParameters)(
|
|
254
|
+
[{ type: "uint256" }, { type: "bytes" }],
|
|
255
|
+
`0x${returnData.slice(10)}`
|
|
256
|
+
);
|
|
257
|
+
const inner = decodeRevertError(innerData);
|
|
258
|
+
return `Call #${callIndex} failed \u2192 ${inner}`;
|
|
259
|
+
} catch {
|
|
260
|
+
return customErrors[selector];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return customErrors[selector];
|
|
264
|
+
}
|
|
265
|
+
return `Reverted with unknown selector ${selector}`;
|
|
266
|
+
}
|
|
267
|
+
function decodePanicCode(code) {
|
|
268
|
+
const codes = {
|
|
269
|
+
0: "generic panic",
|
|
270
|
+
1: "assert failed",
|
|
271
|
+
17: "arithmetic overflow/underflow",
|
|
272
|
+
18: "division by zero",
|
|
273
|
+
33: "invalid enum value",
|
|
274
|
+
34: "storage access out of bounds",
|
|
275
|
+
49: "pop on empty array",
|
|
276
|
+
50: "array index out of bounds",
|
|
277
|
+
65: "out of memory",
|
|
278
|
+
81: "uninitialized function pointer"
|
|
279
|
+
};
|
|
280
|
+
return codes[code] ?? `code 0x${code.toString(16)}`;
|
|
281
|
+
}
|
|
282
|
+
function parseCallResults(receipt, callCount, rawReturnData) {
|
|
283
|
+
if (rawReturnData && rawReturnData !== "0x" && rawReturnData.length > 2) {
|
|
284
|
+
try {
|
|
285
|
+
const decoded = (0, import_viem2.decodeAbiParameters)(
|
|
286
|
+
[
|
|
287
|
+
{
|
|
288
|
+
type: "tuple[]",
|
|
289
|
+
components: [
|
|
290
|
+
{ name: "success", type: "bool" },
|
|
291
|
+
{ name: "returnData", type: "bytes" }
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
],
|
|
295
|
+
rawReturnData
|
|
296
|
+
);
|
|
297
|
+
const results = decoded[0];
|
|
298
|
+
return results.map((r) => ({
|
|
299
|
+
success: r.success,
|
|
300
|
+
returnData: r.returnData
|
|
301
|
+
}));
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const resultMap = /* @__PURE__ */ new Map();
|
|
306
|
+
for (const log of receipt.logs) {
|
|
307
|
+
try {
|
|
308
|
+
const decoded = (0, import_viem2.decodeEventLog)({
|
|
309
|
+
abi: MEGA_ROUTER_ABI,
|
|
310
|
+
data: log.data,
|
|
311
|
+
topics: log.topics,
|
|
312
|
+
strict: false
|
|
313
|
+
});
|
|
314
|
+
if (decoded.eventName === "CallExecuted") {
|
|
315
|
+
const { callIndex, success } = decoded.args;
|
|
316
|
+
resultMap.set(Number(callIndex), {
|
|
317
|
+
success,
|
|
318
|
+
returnData: "0x"
|
|
319
|
+
// events don't carry returnData
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (resultMap.size > 0) {
|
|
326
|
+
return Array.from(
|
|
327
|
+
{ length: callCount },
|
|
328
|
+
(_, i) => resultMap.get(i) ?? { success: true, returnData: "0x" }
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
return Array.from({ length: callCount }, () => ({
|
|
332
|
+
success: true,
|
|
333
|
+
returnData: "0x"
|
|
334
|
+
}));
|
|
335
|
+
}
|
|
336
|
+
function parseBatchExecutedEvent(receipt) {
|
|
337
|
+
for (const log of receipt.logs) {
|
|
338
|
+
try {
|
|
339
|
+
const decoded = (0, import_viem2.decodeEventLog)({
|
|
340
|
+
abi: MEGA_ROUTER_ABI,
|
|
341
|
+
data: log.data,
|
|
342
|
+
topics: log.topics,
|
|
343
|
+
strict: false
|
|
344
|
+
});
|
|
345
|
+
if (decoded.eventName === "BatchExecuted") {
|
|
346
|
+
const args = decoded.args;
|
|
347
|
+
return {
|
|
348
|
+
sender: args.sender,
|
|
349
|
+
callCount: args.callCount,
|
|
350
|
+
totalValue: args.totalValue,
|
|
351
|
+
blockNumber: receipt.blockNumber,
|
|
352
|
+
transactionHash: receipt.transactionHash
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
function parseCallExecutedEvents(receipt) {
|
|
361
|
+
const events = [];
|
|
362
|
+
for (const log of receipt.logs) {
|
|
363
|
+
try {
|
|
364
|
+
const decoded = (0, import_viem2.decodeEventLog)({
|
|
365
|
+
abi: MEGA_ROUTER_ABI,
|
|
366
|
+
data: log.data,
|
|
367
|
+
topics: log.topics,
|
|
368
|
+
strict: false
|
|
369
|
+
});
|
|
370
|
+
if (decoded.eventName === "CallExecuted") {
|
|
371
|
+
const args = decoded.args;
|
|
372
|
+
events.push({
|
|
373
|
+
callIndex: args.callIndex,
|
|
374
|
+
target: args.target,
|
|
375
|
+
value: args.value,
|
|
376
|
+
success: args.success,
|
|
377
|
+
blockNumber: receipt.blockNumber,
|
|
378
|
+
transactionHash: receipt.transactionHash
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
} catch {
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return events.sort((a, b) => Number(a.callIndex - b.callIndex));
|
|
385
|
+
}
|
|
386
|
+
function applyMegaethGasBuffer(estimate, bufferPct = 10) {
|
|
387
|
+
return estimate * BigInt(100 + bufferPct) / 100n;
|
|
388
|
+
}
|
|
389
|
+
function deadlineInMinutes(minutes) {
|
|
390
|
+
return BigInt(Math.floor(Date.now() / 1e3) + minutes * 60);
|
|
391
|
+
}
|
|
392
|
+
function buildSafeApproveCalls(params) {
|
|
393
|
+
const { token, spender, amount, currentAllowance } = params;
|
|
394
|
+
const approveCalldata = (amt) => {
|
|
395
|
+
const selector = "0x095ea7b3";
|
|
396
|
+
const paddedSpender = spender.slice(2).padStart(64, "0");
|
|
397
|
+
const paddedAmount = amt.toString(16).padStart(64, "0");
|
|
398
|
+
return `${selector}${paddedSpender}${paddedAmount}`;
|
|
399
|
+
};
|
|
400
|
+
const calls = [];
|
|
401
|
+
if (currentAllowance > 0n && currentAllowance !== amount) {
|
|
402
|
+
calls.push({ target: token, value: 0n, callData: approveCalldata(0n) });
|
|
403
|
+
}
|
|
404
|
+
calls.push({ target: token, value: 0n, callData: approveCalldata(amount) });
|
|
405
|
+
return calls;
|
|
406
|
+
}
|
|
407
|
+
async function getKyberQuote(params) {
|
|
408
|
+
const ETH_PLACEHOLDER = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
409
|
+
const url = new URL(`${KYBERSWAP_API_BASE}/routes`);
|
|
410
|
+
url.searchParams.set("tokenIn", params.tokenIn === "ETH" ? ETH_PLACEHOLDER : params.tokenIn);
|
|
411
|
+
url.searchParams.set("tokenOut", params.tokenOut === "ETH" ? ETH_PLACEHOLDER : params.tokenOut);
|
|
412
|
+
url.searchParams.set("amountIn", params.amountIn.toString());
|
|
413
|
+
url.searchParams.set("gasInclude", "true");
|
|
414
|
+
const res = await fetch(url.toString());
|
|
415
|
+
if (!res.ok) {
|
|
416
|
+
throw new Error(`KyberSwap API error ${res.status}: ${await res.text()}`);
|
|
417
|
+
}
|
|
418
|
+
const json = await res.json();
|
|
419
|
+
if (json.code !== 0) throw new Error(`KyberSwap error: ${json.message}`);
|
|
420
|
+
return json.data.routeSummary;
|
|
421
|
+
}
|
|
422
|
+
async function buildKyberSwap(params) {
|
|
423
|
+
const res = await fetch(`${KYBERSWAP_API_BASE}/route/build`, {
|
|
424
|
+
method: "POST",
|
|
425
|
+
headers: { "Content-Type": "application/json" },
|
|
426
|
+
body: JSON.stringify({
|
|
427
|
+
routeSummary: params.routeSummary,
|
|
428
|
+
sender: params.sender,
|
|
429
|
+
recipient: params.recipient,
|
|
430
|
+
slippageTolerance: params.slippageBps ?? 50
|
|
431
|
+
})
|
|
432
|
+
});
|
|
433
|
+
if (!res.ok) {
|
|
434
|
+
throw new Error(`KyberSwap build error ${res.status}: ${await res.text()}`);
|
|
435
|
+
}
|
|
436
|
+
const json = await res.json();
|
|
437
|
+
if (json.code !== 0) throw new Error(`KyberSwap build error: ${json.message}`);
|
|
438
|
+
return json.data;
|
|
439
|
+
}
|
|
440
|
+
function assertCallsNotEmpty(calls) {
|
|
441
|
+
if (calls.length === 0) {
|
|
442
|
+
throw new MegaFlowError(
|
|
443
|
+
"No calls in batch. Add at least one call before executing.",
|
|
444
|
+
"EMPTY_BATCH"
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function assertWalletConnected(walletClient) {
|
|
449
|
+
if (!walletClient) {
|
|
450
|
+
throw new MegaFlowError(
|
|
451
|
+
"Wallet not connected. Call connect() or connectWithAccount() first.",
|
|
452
|
+
"WALLET_NOT_CONNECTED"
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function assertAccount(walletClient) {
|
|
457
|
+
if (!walletClient.account) {
|
|
458
|
+
throw new MegaFlowError(
|
|
459
|
+
"Wallet has no account attached.",
|
|
460
|
+
"NO_ACCOUNT"
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function isUserRejection(error) {
|
|
465
|
+
if (error instanceof Error) {
|
|
466
|
+
return error.message.includes("User rejected") || error.message.includes("user rejected") || error.message.includes("User denied") || error.message.includes("ACTION_REJECTED");
|
|
467
|
+
}
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
function isValidAddress(address) {
|
|
471
|
+
return /^0x[a-fA-F0-9]{40}$/.test(address);
|
|
472
|
+
}
|
|
473
|
+
function isValidHex(hex) {
|
|
474
|
+
return /^0x[a-fA-F0-9]*$/.test(hex);
|
|
475
|
+
}
|
|
476
|
+
function chunkCalls(calls, maxPerBatch = 64) {
|
|
477
|
+
const chunks = [];
|
|
478
|
+
for (let i = 0; i < calls.length; i += maxPerBatch) {
|
|
479
|
+
chunks.push(calls.slice(i, i + maxPerBatch));
|
|
480
|
+
}
|
|
481
|
+
return chunks;
|
|
482
|
+
}
|
|
483
|
+
function calculateTotalValue(calls) {
|
|
484
|
+
return calls.reduce((sum, call) => sum + call.value, 0n);
|
|
485
|
+
}
|
|
486
|
+
var MegaFlowError = class extends Error {
|
|
487
|
+
constructor(message, code, cause) {
|
|
488
|
+
super(`[MegaFlow] ${message}`);
|
|
489
|
+
this.code = code;
|
|
490
|
+
this.cause = cause;
|
|
491
|
+
this.name = "MegaFlowError";
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// src/builder.ts
|
|
496
|
+
var import_viem3 = require("viem");
|
|
497
|
+
var MegaFlowBuilder = class {
|
|
498
|
+
constructor(config = {}) {
|
|
499
|
+
this.calls = [];
|
|
500
|
+
this.operations = [];
|
|
501
|
+
this.routerAddress = config.routerAddress ?? MAINNET_ROUTER_ADDRESS;
|
|
502
|
+
this.chain = config.chain ?? megaethMainnet;
|
|
503
|
+
this.debug = config.debug ?? false;
|
|
504
|
+
const rpcUrl = config.rpcUrl ?? this.chain.rpcUrls.default.http[0];
|
|
505
|
+
this.publicClient = (0, import_viem3.createPublicClient)({
|
|
506
|
+
chain: this.chain,
|
|
507
|
+
transport: (0, import_viem3.http)(rpcUrl, {
|
|
508
|
+
timeout: 3e4
|
|
509
|
+
})
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
// ==========================================================================
|
|
513
|
+
// Connection
|
|
514
|
+
// ==========================================================================
|
|
515
|
+
/**
|
|
516
|
+
* Connect a pre-built viem WalletClient.
|
|
517
|
+
*/
|
|
518
|
+
connect(walletClient) {
|
|
519
|
+
this.walletClient = walletClient;
|
|
520
|
+
this.log("Wallet connected", { account: walletClient.account?.address });
|
|
521
|
+
return this;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Connect using a viem Account object.
|
|
525
|
+
* Creates an internal WalletClient bound to MegaETH's chain/RPC.
|
|
526
|
+
*/
|
|
527
|
+
connectWithAccount(account, rpcUrl) {
|
|
528
|
+
this.walletClient = (0, import_viem3.createWalletClient)({
|
|
529
|
+
account,
|
|
530
|
+
chain: this.chain,
|
|
531
|
+
transport: (0, import_viem3.http)(rpcUrl ?? this.chain.rpcUrls.default.http[0])
|
|
532
|
+
});
|
|
533
|
+
return this;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Disconnect the wallet (for cleanup).
|
|
537
|
+
*/
|
|
538
|
+
disconnect() {
|
|
539
|
+
this.walletClient = void 0;
|
|
540
|
+
return this;
|
|
541
|
+
}
|
|
542
|
+
// ==========================================================================
|
|
543
|
+
// Generic Call Builders
|
|
544
|
+
// ==========================================================================
|
|
545
|
+
/**
|
|
546
|
+
* Add a raw call (pre-encoded calldata).
|
|
547
|
+
*/
|
|
548
|
+
add(target, callData, value = 0n) {
|
|
549
|
+
const call = { target, value, callData };
|
|
550
|
+
this.calls.push(call);
|
|
551
|
+
this.recordOperation("custom", call);
|
|
552
|
+
return this;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Add a type-safe ABI-encoded call.
|
|
556
|
+
*
|
|
557
|
+
* @example
|
|
558
|
+
* builder.addCall({
|
|
559
|
+
* target: '0x...',
|
|
560
|
+
* abi: myAbi,
|
|
561
|
+
* functionName: 'mint',
|
|
562
|
+
* args: [recipient, amount],
|
|
563
|
+
* })
|
|
564
|
+
*/
|
|
565
|
+
addCall(params) {
|
|
566
|
+
const callData = (0, import_viem3.encodeFunctionData)({
|
|
567
|
+
abi: params.abi,
|
|
568
|
+
functionName: params.functionName,
|
|
569
|
+
args: params.args ?? []
|
|
570
|
+
});
|
|
571
|
+
return this.add(params.target, callData, params.value ?? 0n);
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Send native ETH to an address.
|
|
575
|
+
* Uses a minimal `receive()` call (empty calldata is not allowed by MegaRouter,
|
|
576
|
+
* so we send a 1-byte noop that any contract with a `receive()` will handle).
|
|
577
|
+
*/
|
|
578
|
+
sendETH(to, value) {
|
|
579
|
+
const call = { target: to, value, callData: "0x00" };
|
|
580
|
+
this.calls.push(call);
|
|
581
|
+
this.recordOperation("custom", call, { type: "sendETH", to, value: value.toString() });
|
|
582
|
+
return this;
|
|
583
|
+
}
|
|
584
|
+
// ==========================================================================
|
|
585
|
+
// ERC20 Helpers
|
|
586
|
+
// ==========================================================================
|
|
587
|
+
/**
|
|
588
|
+
* ERC20 approve.
|
|
589
|
+
* Uses the simple single-approve pattern.
|
|
590
|
+
* For safe two-step approve, use `safeApprove()`.
|
|
591
|
+
*/
|
|
592
|
+
approve(token, spender, amount) {
|
|
593
|
+
return this.addCall({
|
|
594
|
+
target: token,
|
|
595
|
+
abi: ERC20_ABI,
|
|
596
|
+
functionName: "approve",
|
|
597
|
+
args: [spender, amount]
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Safe ERC20 approve — resets allowance to 0 first if non-zero,
|
|
602
|
+
* then sets the new amount. Prevents ERC20 approval race conditions
|
|
603
|
+
* (required by some tokens like USDT).
|
|
604
|
+
*
|
|
605
|
+
* @param currentAllowance Current on-chain allowance. Pass 0n if unknown and you want a single approve.
|
|
606
|
+
*/
|
|
607
|
+
safeApprove(token, spender, amount, currentAllowance) {
|
|
608
|
+
const approvalCalls = buildSafeApproveCalls({
|
|
609
|
+
token,
|
|
610
|
+
spender,
|
|
611
|
+
amount,
|
|
612
|
+
currentAllowance
|
|
613
|
+
});
|
|
614
|
+
for (const call of approvalCalls) {
|
|
615
|
+
this.calls.push(call);
|
|
616
|
+
this.recordOperation("safeApprove", call, { token, spender, amount: amount.toString() });
|
|
617
|
+
}
|
|
618
|
+
return this;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* ERC20 transfer.
|
|
622
|
+
*/
|
|
623
|
+
transfer(token, to, amount) {
|
|
624
|
+
return this.addCall({
|
|
625
|
+
target: token,
|
|
626
|
+
abi: ERC20_ABI,
|
|
627
|
+
functionName: "transfer",
|
|
628
|
+
args: [to, amount]
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* ERC20 transferFrom.
|
|
633
|
+
*/
|
|
634
|
+
transferFrom(token, from, to, amount) {
|
|
635
|
+
return this.addCall({
|
|
636
|
+
target: token,
|
|
637
|
+
abi: ERC20_ABI,
|
|
638
|
+
functionName: "transferFrom",
|
|
639
|
+
args: [from, to, amount]
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Batch transfer same token to multiple recipients in one batch.
|
|
644
|
+
* @example .multiTransfer(USDC, [{ to: addr1, amount: 100n }, { to: addr2, amount: 200n }])
|
|
645
|
+
*/
|
|
646
|
+
multiTransfer(token, transfers) {
|
|
647
|
+
for (const { to, amount } of transfers) {
|
|
648
|
+
this.transfer(token, to, amount);
|
|
649
|
+
}
|
|
650
|
+
return this;
|
|
651
|
+
}
|
|
652
|
+
// ==========================================================================
|
|
653
|
+
// ERC721 Helpers
|
|
654
|
+
// ==========================================================================
|
|
655
|
+
/**
|
|
656
|
+
* ERC721 safeTransferFrom.
|
|
657
|
+
*/
|
|
658
|
+
transferNFT(nft, from, to, tokenId) {
|
|
659
|
+
return this.addCall({
|
|
660
|
+
target: nft,
|
|
661
|
+
abi: ERC721_ABI,
|
|
662
|
+
functionName: "safeTransferFrom",
|
|
663
|
+
args: [from, to, tokenId]
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* ERC721 approve (single token).
|
|
668
|
+
*/
|
|
669
|
+
approveNFT(nft, to, tokenId) {
|
|
670
|
+
return this.addCall({
|
|
671
|
+
target: nft,
|
|
672
|
+
abi: ERC721_ABI,
|
|
673
|
+
functionName: "approve",
|
|
674
|
+
args: [to, tokenId]
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* ERC721 setApprovalForAll.
|
|
679
|
+
*/
|
|
680
|
+
setApprovalForAll(nft, operator, approved) {
|
|
681
|
+
return this.addCall({
|
|
682
|
+
target: nft,
|
|
683
|
+
abi: ERC721_ABI,
|
|
684
|
+
functionName: "setApprovalForAll",
|
|
685
|
+
args: [operator, approved]
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
// ==========================================================================
|
|
689
|
+
// WETH Helpers
|
|
690
|
+
// ==========================================================================
|
|
691
|
+
/**
|
|
692
|
+
* Wrap ETH → WETH.
|
|
693
|
+
* The value is sent as ETH and WETH is received in return.
|
|
694
|
+
*/
|
|
695
|
+
wrapETH(wethAddress = MEGAETH_TOKENS.WETH, amount) {
|
|
696
|
+
return this.addCall({
|
|
697
|
+
target: wethAddress,
|
|
698
|
+
abi: WETH_ABI,
|
|
699
|
+
functionName: "deposit",
|
|
700
|
+
value: amount
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Unwrap WETH → ETH.
|
|
705
|
+
*/
|
|
706
|
+
unwrapWETH(wethAddress = MEGAETH_TOKENS.WETH, amount) {
|
|
707
|
+
return this.addCall({
|
|
708
|
+
target: wethAddress,
|
|
709
|
+
abi: WETH_ABI,
|
|
710
|
+
functionName: "withdraw",
|
|
711
|
+
args: [amount]
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
// ==========================================================================
|
|
715
|
+
// DEX Helpers (Uniswap V2 interface — works with any V2-compatible DEX)
|
|
716
|
+
// ==========================================================================
|
|
717
|
+
/**
|
|
718
|
+
* Token→Token swap via Uniswap V2 router interface.
|
|
719
|
+
*/
|
|
720
|
+
swapExactTokensForTokens(params) {
|
|
721
|
+
return this.addCall({
|
|
722
|
+
target: params.router,
|
|
723
|
+
abi: UNISWAP_V2_ROUTER_ABI,
|
|
724
|
+
functionName: "swapExactTokensForTokens",
|
|
725
|
+
args: [
|
|
726
|
+
params.amountIn,
|
|
727
|
+
params.amountOutMin,
|
|
728
|
+
params.path,
|
|
729
|
+
params.to,
|
|
730
|
+
params.deadline ?? deadlineInMinutes(5)
|
|
731
|
+
]
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* ETH→Token swap via Uniswap V2 router interface.
|
|
736
|
+
*/
|
|
737
|
+
swapExactETHForTokens(params) {
|
|
738
|
+
return this.addCall({
|
|
739
|
+
target: params.router,
|
|
740
|
+
abi: UNISWAP_V2_ROUTER_ABI,
|
|
741
|
+
functionName: "swapExactETHForTokens",
|
|
742
|
+
args: [
|
|
743
|
+
params.amountOutMin,
|
|
744
|
+
params.path,
|
|
745
|
+
params.to,
|
|
746
|
+
params.deadline ?? deadlineInMinutes(5)
|
|
747
|
+
],
|
|
748
|
+
value: params.amountIn
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Token→ETH swap via Uniswap V2 router interface.
|
|
753
|
+
*/
|
|
754
|
+
swapExactTokensForETH(params) {
|
|
755
|
+
return this.addCall({
|
|
756
|
+
target: params.router,
|
|
757
|
+
abi: UNISWAP_V2_ROUTER_ABI,
|
|
758
|
+
functionName: "swapExactTokensForETH",
|
|
759
|
+
args: [
|
|
760
|
+
params.amountIn,
|
|
761
|
+
params.amountOutMin,
|
|
762
|
+
params.path,
|
|
763
|
+
params.to,
|
|
764
|
+
params.deadline ?? deadlineInMinutes(5)
|
|
765
|
+
]
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Approve a token and then swap in the same batch (2 calls, 1 tx).
|
|
770
|
+
* This is the core "Sui-style composability" pattern.
|
|
771
|
+
*/
|
|
772
|
+
approveAndSwap(params) {
|
|
773
|
+
return this.approve(params.token, params.router, params.amountIn).swapExactTokensForTokens(params);
|
|
774
|
+
}
|
|
775
|
+
// ==========================================================================
|
|
776
|
+
// KyberSwap Integration (Best-Route Aggregator on MegaETH)
|
|
777
|
+
// ==========================================================================
|
|
778
|
+
/**
|
|
779
|
+
* Add a KyberSwap aggregator swap call.
|
|
780
|
+
* Fetches route and encodes the on-chain call automatically.
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* await builder.kyberSwap({
|
|
784
|
+
* tokenIn: USDC,
|
|
785
|
+
* tokenOut: WETH,
|
|
786
|
+
* amountIn: parseUnits('100', 6),
|
|
787
|
+
* slippageBps: 30, // 0.3%
|
|
788
|
+
* }, senderAddress);
|
|
789
|
+
*/
|
|
790
|
+
async kyberSwap(params, sender) {
|
|
791
|
+
const route = await getKyberQuote(params);
|
|
792
|
+
const built = await buildKyberSwap({
|
|
793
|
+
routeSummary: route,
|
|
794
|
+
sender,
|
|
795
|
+
recipient: sender,
|
|
796
|
+
slippageBps: params.slippageBps
|
|
797
|
+
});
|
|
798
|
+
return this.add(
|
|
799
|
+
built.routerAddress,
|
|
800
|
+
built.data,
|
|
801
|
+
BigInt(built.value)
|
|
802
|
+
);
|
|
803
|
+
}
|
|
804
|
+
// ==========================================================================
|
|
805
|
+
// Utility Methods
|
|
806
|
+
// ==========================================================================
|
|
807
|
+
/** Get a copy of queued calls */
|
|
808
|
+
getCalls() {
|
|
809
|
+
return [...this.calls];
|
|
810
|
+
}
|
|
811
|
+
/** Get recorded operations (for debugging/tracking) */
|
|
812
|
+
getOperations() {
|
|
813
|
+
return [...this.operations];
|
|
814
|
+
}
|
|
815
|
+
/** Get full builder state (for serialization/debugging) */
|
|
816
|
+
getState() {
|
|
817
|
+
return {
|
|
818
|
+
calls: [...this.calls],
|
|
819
|
+
operations: [...this.operations],
|
|
820
|
+
totalValue: this.getTotalValue(),
|
|
821
|
+
chainId: this.chain.id
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
/** Clear all queued calls and operation history */
|
|
825
|
+
clear() {
|
|
826
|
+
this.calls = [];
|
|
827
|
+
this.operations = [];
|
|
828
|
+
return this;
|
|
829
|
+
}
|
|
830
|
+
/** Remove and return the last call */
|
|
831
|
+
pop() {
|
|
832
|
+
this.operations.pop();
|
|
833
|
+
return this.calls.pop();
|
|
834
|
+
}
|
|
835
|
+
/** Total ETH value across all calls */
|
|
836
|
+
getTotalValue() {
|
|
837
|
+
return this.calls.reduce((sum, c) => sum + c.value, 0n);
|
|
838
|
+
}
|
|
839
|
+
/** Number of calls in the current batch */
|
|
840
|
+
get length() {
|
|
841
|
+
return this.calls.length;
|
|
842
|
+
}
|
|
843
|
+
/** Whether batch is empty */
|
|
844
|
+
get isEmpty() {
|
|
845
|
+
return this.calls.length === 0;
|
|
846
|
+
}
|
|
847
|
+
/** Whether a wallet is connected */
|
|
848
|
+
get isConnected() {
|
|
849
|
+
return !!this.walletClient?.account;
|
|
850
|
+
}
|
|
851
|
+
/** The connected account address (or undefined) */
|
|
852
|
+
get account() {
|
|
853
|
+
return this.walletClient?.account?.address;
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Get a human-readable summary of the queued batch.
|
|
857
|
+
* Great for logging/debugging before execution.
|
|
858
|
+
*/
|
|
859
|
+
summary() {
|
|
860
|
+
const lines = [
|
|
861
|
+
`MegaFlow Batch (${this.calls.length} calls)`,
|
|
862
|
+
`Chain: ${this.chain.name} (${this.chain.id})`,
|
|
863
|
+
`Router: ${this.routerAddress}`,
|
|
864
|
+
`Total Value: ${this.getTotalValue()} wei`,
|
|
865
|
+
"",
|
|
866
|
+
"Operations:"
|
|
867
|
+
];
|
|
868
|
+
this.operations.forEach((op, i) => {
|
|
869
|
+
const target = op.call.target.slice(0, 10) + "...";
|
|
870
|
+
lines.push(` ${i + 1}. ${op.type} \u2192 ${target}`);
|
|
871
|
+
});
|
|
872
|
+
return lines.join("\n");
|
|
873
|
+
}
|
|
874
|
+
// ==========================================================================
|
|
875
|
+
// On-Chain Reads
|
|
876
|
+
// ==========================================================================
|
|
877
|
+
/** Read the protocol flat fee from the MegaRouter contract */
|
|
878
|
+
async getFlatFee() {
|
|
879
|
+
return this.publicClient.readContract({
|
|
880
|
+
address: this.routerAddress,
|
|
881
|
+
abi: MEGA_ROUTER_ABI,
|
|
882
|
+
functionName: "flatFee"
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
/** Calculate total ETH required (sum of call values + flat fee) */
|
|
886
|
+
async calculateRequiredETH() {
|
|
887
|
+
if (this.calls.length === 0) return 0n;
|
|
888
|
+
return this.publicClient.readContract({
|
|
889
|
+
address: this.routerAddress,
|
|
890
|
+
abi: MEGA_ROUTER_ABI,
|
|
891
|
+
functionName: "calculateRequiredETH",
|
|
892
|
+
args: [this.calls]
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
// ==========================================================================
|
|
896
|
+
// Simulation (Dry Run)
|
|
897
|
+
// ==========================================================================
|
|
898
|
+
/**
|
|
899
|
+
* Simulate the batch via eth_call — no state changes, no gas spent.
|
|
900
|
+
* Very useful for pre-flight checks before submitting the real transaction.
|
|
901
|
+
*/
|
|
902
|
+
async simulate() {
|
|
903
|
+
assertCallsNotEmpty(this.calls);
|
|
904
|
+
try {
|
|
905
|
+
const requiredETH = await this.calculateRequiredETH();
|
|
906
|
+
const { result } = await this.publicClient.simulateContract({
|
|
907
|
+
address: this.routerAddress,
|
|
908
|
+
abi: MEGA_ROUTER_ABI,
|
|
909
|
+
functionName: "executeBatch",
|
|
910
|
+
args: [this.calls],
|
|
911
|
+
value: requiredETH,
|
|
912
|
+
account: this.walletClient?.account
|
|
913
|
+
});
|
|
914
|
+
const gasEstimate = await this.publicClient.estimateContractGas({
|
|
915
|
+
address: this.routerAddress,
|
|
916
|
+
abi: MEGA_ROUTER_ABI,
|
|
917
|
+
functionName: "executeBatch",
|
|
918
|
+
args: [this.calls],
|
|
919
|
+
value: requiredETH,
|
|
920
|
+
account: this.walletClient?.account
|
|
921
|
+
});
|
|
922
|
+
return {
|
|
923
|
+
success: true,
|
|
924
|
+
results: result,
|
|
925
|
+
gasEstimate: applyMegaethGasBuffer(gasEstimate)
|
|
926
|
+
};
|
|
927
|
+
} catch (err) {
|
|
928
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
929
|
+
const revertReason = decodeRevertError(errorMessage);
|
|
930
|
+
const failedCallIndex = this.extractFailedCallIndex(errorMessage);
|
|
931
|
+
this.log("Simulation failed", { error: errorMessage, failedCallIndex });
|
|
932
|
+
return {
|
|
933
|
+
success: false,
|
|
934
|
+
error: errorMessage,
|
|
935
|
+
revertReason,
|
|
936
|
+
failedCallIndex
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
// ==========================================================================
|
|
941
|
+
// Execution — Standard (async)
|
|
942
|
+
// ==========================================================================
|
|
943
|
+
/**
|
|
944
|
+
* Execute the batch using the standard async flow:
|
|
945
|
+
* 1. Simulate to get request params
|
|
946
|
+
* 2. walletClient.writeContract → txHash
|
|
947
|
+
* 3. waitForTransactionReceipt
|
|
948
|
+
*
|
|
949
|
+
* On MegaETH this is still very fast (~100-200ms) due to 10ms blocks.
|
|
950
|
+
* For true synchronous receipts use `executeSync()`.
|
|
951
|
+
*/
|
|
952
|
+
async execute(options = {}) {
|
|
953
|
+
assertCallsNotEmpty(this.calls);
|
|
954
|
+
assertWalletConnected(this.walletClient);
|
|
955
|
+
assertAccount(this.walletClient);
|
|
956
|
+
this.log("Executing batch", { callCount: this.calls.length });
|
|
957
|
+
const requiredETH = await this.calculateRequiredETH();
|
|
958
|
+
const { request } = await this.publicClient.simulateContract({
|
|
959
|
+
address: this.routerAddress,
|
|
960
|
+
abi: MEGA_ROUTER_ABI,
|
|
961
|
+
functionName: "executeBatch",
|
|
962
|
+
args: [this.calls],
|
|
963
|
+
value: requiredETH,
|
|
964
|
+
account: this.walletClient.account,
|
|
965
|
+
gas: options.gasLimit,
|
|
966
|
+
maxFeePerGas: options.maxFeePerGas,
|
|
967
|
+
maxPriorityFeePerGas: options.maxPriorityFeePerGas,
|
|
968
|
+
nonce: options.nonce
|
|
969
|
+
});
|
|
970
|
+
const hash = await this.walletClient.writeContract(request);
|
|
971
|
+
this.log("Transaction sent", { hash });
|
|
972
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
973
|
+
this.log("Transaction confirmed", { status: receipt.status, gasUsed: receipt.gasUsed.toString() });
|
|
974
|
+
const results = parseCallResults(receipt, this.calls.length);
|
|
975
|
+
return {
|
|
976
|
+
hash,
|
|
977
|
+
receipt,
|
|
978
|
+
results,
|
|
979
|
+
gasUsed: receipt.gasUsed,
|
|
980
|
+
effectiveGasPrice: receipt.effectiveGasPrice,
|
|
981
|
+
totalCost: receipt.gasUsed * receipt.effectiveGasPrice
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
// ==========================================================================
|
|
985
|
+
// Execution — Synchronous (MegaETH-specific, EIP-7966)
|
|
986
|
+
// ==========================================================================
|
|
987
|
+
/**
|
|
988
|
+
* Execute using MegaETH's `eth_sendRawTransactionSync` (EIP-7966).
|
|
989
|
+
* Returns the full receipt in one RPC round-trip (~10ms).
|
|
990
|
+
*
|
|
991
|
+
* This is the preferred method on MegaETH — no polling required.
|
|
992
|
+
*/
|
|
993
|
+
async executeSync(options = {}) {
|
|
994
|
+
assertCallsNotEmpty(this.calls);
|
|
995
|
+
assertWalletConnected(this.walletClient);
|
|
996
|
+
assertAccount(this.walletClient);
|
|
997
|
+
const startTime = performance.now();
|
|
998
|
+
this.log("Executing batch (sync)", { callCount: this.calls.length });
|
|
999
|
+
const requiredETH = await this.calculateRequiredETH();
|
|
1000
|
+
const { request } = await this.publicClient.simulateContract({
|
|
1001
|
+
address: this.routerAddress,
|
|
1002
|
+
abi: MEGA_ROUTER_ABI,
|
|
1003
|
+
functionName: "executeBatch",
|
|
1004
|
+
args: [this.calls],
|
|
1005
|
+
value: requiredETH,
|
|
1006
|
+
account: this.walletClient.account,
|
|
1007
|
+
gas: options.gasLimit
|
|
1008
|
+
});
|
|
1009
|
+
const signedTx = await this.walletClient.signTransaction({
|
|
1010
|
+
...request,
|
|
1011
|
+
account: this.walletClient.account
|
|
1012
|
+
});
|
|
1013
|
+
const receipt = await this.publicClient.request({
|
|
1014
|
+
method: "eth_sendRawTransactionSync",
|
|
1015
|
+
params: [signedTx]
|
|
1016
|
+
});
|
|
1017
|
+
const executionTimeMs = performance.now() - startTime;
|
|
1018
|
+
this.log("Sync execution completed", { executionTimeMs: executionTimeMs.toFixed(2) });
|
|
1019
|
+
const results = parseCallResults(receipt, this.calls.length);
|
|
1020
|
+
return { receipt, results, gasUsed: receipt.gasUsed, executionTimeMs };
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Same as executeSync but uses `realtime_sendRawTransaction` (MegaETH original).
|
|
1024
|
+
* Both are functionally identical; use `executeSync` for cross-chain compatibility.
|
|
1025
|
+
*/
|
|
1026
|
+
async executeRealtime(options = {}) {
|
|
1027
|
+
assertCallsNotEmpty(this.calls);
|
|
1028
|
+
assertWalletConnected(this.walletClient);
|
|
1029
|
+
assertAccount(this.walletClient);
|
|
1030
|
+
const startTime = performance.now();
|
|
1031
|
+
const requiredETH = await this.calculateRequiredETH();
|
|
1032
|
+
const { request } = await this.publicClient.simulateContract({
|
|
1033
|
+
address: this.routerAddress,
|
|
1034
|
+
abi: MEGA_ROUTER_ABI,
|
|
1035
|
+
functionName: "executeBatch",
|
|
1036
|
+
args: [this.calls],
|
|
1037
|
+
value: requiredETH,
|
|
1038
|
+
account: this.walletClient.account,
|
|
1039
|
+
gas: options.gasLimit
|
|
1040
|
+
});
|
|
1041
|
+
const signedTx = await this.walletClient.signTransaction({
|
|
1042
|
+
...request,
|
|
1043
|
+
account: this.walletClient.account
|
|
1044
|
+
});
|
|
1045
|
+
const receipt = await this.publicClient.request({
|
|
1046
|
+
method: "realtime_sendRawTransaction",
|
|
1047
|
+
params: [signedTx]
|
|
1048
|
+
});
|
|
1049
|
+
const executionTimeMs = performance.now() - startTime;
|
|
1050
|
+
const results = parseCallResults(receipt, this.calls.length);
|
|
1051
|
+
return { receipt, results, gasUsed: receipt.gasUsed, executionTimeMs };
|
|
1052
|
+
}
|
|
1053
|
+
// ==========================================================================
|
|
1054
|
+
// Private Helpers
|
|
1055
|
+
// ==========================================================================
|
|
1056
|
+
recordOperation(type, call, metadata) {
|
|
1057
|
+
this.operations.push({
|
|
1058
|
+
type,
|
|
1059
|
+
call,
|
|
1060
|
+
metadata,
|
|
1061
|
+
timestamp: Date.now()
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
extractFailedCallIndex(errorMessage) {
|
|
1065
|
+
const match = errorMessage.match(/CallFailed\((\d+)/);
|
|
1066
|
+
if (match) return parseInt(match[1], 10);
|
|
1067
|
+
const indexMatch = errorMessage.match(/call\s*#?\s*(\d+)/i);
|
|
1068
|
+
if (indexMatch) return parseInt(indexMatch[1], 10);
|
|
1069
|
+
return void 0;
|
|
1070
|
+
}
|
|
1071
|
+
log(message, data) {
|
|
1072
|
+
if (this.debug) {
|
|
1073
|
+
console.log(`[MegaFlow] ${message}`, data ?? "");
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
// src/client.ts
|
|
1079
|
+
var import_viem4 = require("viem");
|
|
1080
|
+
var import_actions = require("viem/actions");
|
|
1081
|
+
var MegaFlowClient = class {
|
|
1082
|
+
constructor(config = {}) {
|
|
1083
|
+
// Local nonce cache: address → last used nonce
|
|
1084
|
+
this.nonceCache = /* @__PURE__ */ new Map();
|
|
1085
|
+
this.config = {
|
|
1086
|
+
...config,
|
|
1087
|
+
routerAddress: config.routerAddress ?? MAINNET_ROUTER_ADDRESS
|
|
1088
|
+
};
|
|
1089
|
+
if (config.publicClient) {
|
|
1090
|
+
this.publicClient = config.publicClient;
|
|
1091
|
+
} else {
|
|
1092
|
+
const rpcUrl = config.rpcUrl ?? (config.chain ?? megaethMainnet).rpcUrls.default.http[0];
|
|
1093
|
+
this.publicClient = (0, import_viem4.createPublicClient)({
|
|
1094
|
+
chain: config.chain ?? megaethMainnet,
|
|
1095
|
+
transport: (0, import_viem4.http)(rpcUrl, { timeout: 3e4 })
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
if (config.walletClient) {
|
|
1099
|
+
this.walletClient = config.walletClient;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
// ==========================================================================
|
|
1103
|
+
// Connection
|
|
1104
|
+
// ==========================================================================
|
|
1105
|
+
connectWithAccount(account, rpcUrl) {
|
|
1106
|
+
const chain = this.config.chain ?? megaethMainnet;
|
|
1107
|
+
this.walletClient = (0, import_viem4.createWalletClient)({
|
|
1108
|
+
account,
|
|
1109
|
+
chain,
|
|
1110
|
+
transport: (0, import_viem4.http)(rpcUrl ?? chain.rpcUrls.default.http[0])
|
|
1111
|
+
});
|
|
1112
|
+
return this;
|
|
1113
|
+
}
|
|
1114
|
+
connect(walletClient) {
|
|
1115
|
+
this.walletClient = walletClient;
|
|
1116
|
+
return this;
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Enable a WebSocket client for lower latency.
|
|
1120
|
+
* WebSocket is 5-6x faster than HTTP on MegaETH.
|
|
1121
|
+
*
|
|
1122
|
+
* Requires the chain to have a webSocket URL configured.
|
|
1123
|
+
*/
|
|
1124
|
+
enableWebSocket(wsUrl) {
|
|
1125
|
+
const chain = this.config.chain ?? megaethMainnet;
|
|
1126
|
+
const url = wsUrl ?? chain.rpcUrls.default.webSocket?.[0];
|
|
1127
|
+
if (!url) throw new Error("No WebSocket URL available for this chain.");
|
|
1128
|
+
this.wsClient = (0, import_viem4.createPublicClient)({
|
|
1129
|
+
chain,
|
|
1130
|
+
// viem's webSocket transport handles reconnection internally.
|
|
1131
|
+
// MegaETH requires a keepalive ping every 30s — viem handles this
|
|
1132
|
+
// via the underlying WebSocket ping frames automatically.
|
|
1133
|
+
transport: (0, import_viem4.webSocket)(url)
|
|
1134
|
+
});
|
|
1135
|
+
return this;
|
|
1136
|
+
}
|
|
1137
|
+
/** Active public client (prefers WebSocket when enabled) */
|
|
1138
|
+
get activeClient() {
|
|
1139
|
+
return this.wsClient ?? this.publicClient;
|
|
1140
|
+
}
|
|
1141
|
+
// ==========================================================================
|
|
1142
|
+
// Batch Builder Factory
|
|
1143
|
+
// ==========================================================================
|
|
1144
|
+
/**
|
|
1145
|
+
* Create a new MegaFlowBuilder linked to this client's configuration.
|
|
1146
|
+
* Start building calls, then call `.execute()` or `.executeSync()`.
|
|
1147
|
+
*/
|
|
1148
|
+
batch() {
|
|
1149
|
+
const builder = new MegaFlowBuilder({
|
|
1150
|
+
routerAddress: this.config.routerAddress,
|
|
1151
|
+
rpcUrl: this.config.rpcUrl,
|
|
1152
|
+
chain: this.config.chain
|
|
1153
|
+
});
|
|
1154
|
+
if (this.walletClient) builder.connect(this.walletClient);
|
|
1155
|
+
return builder;
|
|
1156
|
+
}
|
|
1157
|
+
// ==========================================================================
|
|
1158
|
+
// Token Reads (Multicall batched for efficiency)
|
|
1159
|
+
// ==========================================================================
|
|
1160
|
+
/**
|
|
1161
|
+
* Get the native ETH balance of an address.
|
|
1162
|
+
*/
|
|
1163
|
+
async getETHBalance(address) {
|
|
1164
|
+
return this.publicClient.getBalance({ address });
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* Get the ERC20 token balance of an address.
|
|
1168
|
+
*/
|
|
1169
|
+
async getTokenBalance(token, owner) {
|
|
1170
|
+
return this.publicClient.readContract({
|
|
1171
|
+
address: token,
|
|
1172
|
+
abi: ERC20_ABI,
|
|
1173
|
+
functionName: "balanceOf",
|
|
1174
|
+
args: [owner]
|
|
1175
|
+
});
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Get the current ERC20 allowance.
|
|
1179
|
+
*/
|
|
1180
|
+
async getAllowance(token, owner, spender) {
|
|
1181
|
+
return this.publicClient.readContract({
|
|
1182
|
+
address: token,
|
|
1183
|
+
abi: ERC20_ABI,
|
|
1184
|
+
functionName: "allowance",
|
|
1185
|
+
args: [owner, spender]
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Get multiple token balances in a single multicall.
|
|
1190
|
+
* Much faster than sequential calls on MegaETH (v2.0.14+).
|
|
1191
|
+
*/
|
|
1192
|
+
async getMultipleBalances(tokens, owner) {
|
|
1193
|
+
const contracts = tokens.map((token) => ({
|
|
1194
|
+
address: token,
|
|
1195
|
+
abi: ERC20_ABI,
|
|
1196
|
+
functionName: "balanceOf",
|
|
1197
|
+
args: [owner]
|
|
1198
|
+
}));
|
|
1199
|
+
const results = await (0, import_actions.multicall)(this.publicClient, { contracts });
|
|
1200
|
+
return tokens.map((token, i) => ({
|
|
1201
|
+
token,
|
|
1202
|
+
balance: results[i].status === "success" ? results[i].result : 0n
|
|
1203
|
+
}));
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* Get token metadata (name, symbol, decimals) in a single multicall round-trip.
|
|
1207
|
+
*/
|
|
1208
|
+
async getTokenInfo(token) {
|
|
1209
|
+
const [name, symbol, decimals, totalSupply] = await (0, import_actions.multicall)(this.publicClient, {
|
|
1210
|
+
contracts: [
|
|
1211
|
+
{ address: token, abi: ERC20_ABI, functionName: "name" },
|
|
1212
|
+
{ address: token, abi: ERC20_ABI, functionName: "symbol" },
|
|
1213
|
+
{ address: token, abi: ERC20_ABI, functionName: "decimals" },
|
|
1214
|
+
{ address: token, abi: ERC20_ABI, functionName: "totalSupply" }
|
|
1215
|
+
]
|
|
1216
|
+
});
|
|
1217
|
+
return {
|
|
1218
|
+
name: name.result ?? "",
|
|
1219
|
+
symbol: symbol.result ?? "",
|
|
1220
|
+
decimals: decimals.result ?? 18,
|
|
1221
|
+
totalSupply: totalSupply.result ?? 0n
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
// ==========================================================================
|
|
1225
|
+
// Router Reads
|
|
1226
|
+
// ==========================================================================
|
|
1227
|
+
/** Get protocol flat fee from the deployed router */
|
|
1228
|
+
async getFlatFee() {
|
|
1229
|
+
return this.publicClient.readContract({
|
|
1230
|
+
address: this.config.routerAddress,
|
|
1231
|
+
abi: MEGA_ROUTER_ABI,
|
|
1232
|
+
functionName: "flatFee"
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1235
|
+
/** Get fee collector address from the deployed router */
|
|
1236
|
+
async getFeeCollector() {
|
|
1237
|
+
return this.publicClient.readContract({
|
|
1238
|
+
address: this.config.routerAddress,
|
|
1239
|
+
abi: MEGA_ROUTER_ABI,
|
|
1240
|
+
functionName: "feeCollector"
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
// ==========================================================================
|
|
1244
|
+
// Nonce Management
|
|
1245
|
+
// ==========================================================================
|
|
1246
|
+
/**
|
|
1247
|
+
* Get the next usable nonce for an address, respecting locally tracked nonces.
|
|
1248
|
+
*
|
|
1249
|
+
* Critical for MegaETH: 10ms blocks mean sequential txs can race.
|
|
1250
|
+
* Local nonce tracking prevents "already known" / "nonce too low" errors.
|
|
1251
|
+
*/
|
|
1252
|
+
async getNextNonce(address) {
|
|
1253
|
+
const key = address.toLowerCase();
|
|
1254
|
+
const networkNonce = await this.publicClient.getTransactionCount({
|
|
1255
|
+
address,
|
|
1256
|
+
blockTag: "pending"
|
|
1257
|
+
});
|
|
1258
|
+
const lastUsed = this.nonceCache.get(key) ?? -1;
|
|
1259
|
+
const nonce = Math.max(networkNonce, lastUsed + 1);
|
|
1260
|
+
this.nonceCache.set(key, nonce);
|
|
1261
|
+
return nonce;
|
|
1262
|
+
}
|
|
1263
|
+
/** Reset locally cached nonce for an address (call after a tx confirms or fails). */
|
|
1264
|
+
resetNonce(address) {
|
|
1265
|
+
this.nonceCache.delete(address.toLowerCase());
|
|
1266
|
+
}
|
|
1267
|
+
// ==========================================================================
|
|
1268
|
+
// Gas Helpers
|
|
1269
|
+
// ==========================================================================
|
|
1270
|
+
/**
|
|
1271
|
+
* Get the current gas price from MegaETH RPC.
|
|
1272
|
+
* Returns the raw value (avoids viem's 20% buffer).
|
|
1273
|
+
*/
|
|
1274
|
+
async getGasPrice() {
|
|
1275
|
+
const price = await this.publicClient.request({
|
|
1276
|
+
method: "eth_gasPrice"
|
|
1277
|
+
});
|
|
1278
|
+
return BigInt(price);
|
|
1279
|
+
}
|
|
1280
|
+
// ==========================================================================
|
|
1281
|
+
// MegaETH Bridge Helper
|
|
1282
|
+
// ==========================================================================
|
|
1283
|
+
/**
|
|
1284
|
+
* Bridge ETH from Ethereum mainnet to MegaETH.
|
|
1285
|
+
*
|
|
1286
|
+
* NOTE: This must be called from an Ethereum mainnet wallet client,
|
|
1287
|
+
* not the MegaETH wallet client.
|
|
1288
|
+
*
|
|
1289
|
+
* @param l1WalletClient - Ethereum mainnet wallet client
|
|
1290
|
+
* @param amount - Amount in wei to bridge
|
|
1291
|
+
*/
|
|
1292
|
+
async bridgeETHToMegaETH(l1WalletClient, amount) {
|
|
1293
|
+
if (!l1WalletClient.account) {
|
|
1294
|
+
throw new Error("L1 wallet client has no account");
|
|
1295
|
+
}
|
|
1296
|
+
const hash = await l1WalletClient.sendTransaction({
|
|
1297
|
+
account: l1WalletClient.account,
|
|
1298
|
+
to: MEGAETH_L1_BRIDGE,
|
|
1299
|
+
value: amount,
|
|
1300
|
+
chain: l1WalletClient.chain
|
|
1301
|
+
});
|
|
1302
|
+
return hash;
|
|
1303
|
+
}
|
|
1304
|
+
// ==========================================================================
|
|
1305
|
+
// Connection Status
|
|
1306
|
+
// ==========================================================================
|
|
1307
|
+
get isConnected() {
|
|
1308
|
+
return !!this.walletClient?.account;
|
|
1309
|
+
}
|
|
1310
|
+
get account() {
|
|
1311
|
+
return this.walletClient?.account?.address;
|
|
1312
|
+
}
|
|
1313
|
+
get routerAddress() {
|
|
1314
|
+
return this.config.routerAddress;
|
|
1315
|
+
}
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
// src/index.ts
|
|
1319
|
+
function createMegaFlow(config) {
|
|
1320
|
+
return new MegaFlowBuilder(config);
|
|
1321
|
+
}
|
|
1322
|
+
function createMegaFlowClient(config) {
|
|
1323
|
+
return new MegaFlowClient(config);
|
|
1324
|
+
}
|
|
1325
|
+
function createMegaFlowMainnet(routerAddress) {
|
|
1326
|
+
return new MegaFlowBuilder({ routerAddress, chain: megaethMainnet });
|
|
1327
|
+
}
|
|
1328
|
+
function createMegaFlowTestnet(routerAddress) {
|
|
1329
|
+
return new MegaFlowBuilder({ routerAddress, chain: megaethTestnet });
|
|
1330
|
+
}
|
|
1331
|
+
function createMegaFlowClientMainnet(routerAddress, rpcUrl) {
|
|
1332
|
+
return new MegaFlowClient({ routerAddress, chain: megaethMainnet, rpcUrl });
|
|
1333
|
+
}
|
|
1334
|
+
function createMegaFlowClientTestnet(routerAddress, rpcUrl) {
|
|
1335
|
+
return new MegaFlowClient({ routerAddress, chain: megaethTestnet, rpcUrl });
|
|
1336
|
+
}
|
|
1337
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1338
|
+
0 && (module.exports = {
|
|
1339
|
+
ERC20_ABI,
|
|
1340
|
+
ERC721_ABI,
|
|
1341
|
+
KYBERSWAP_API_BASE,
|
|
1342
|
+
MEGAETH_BASE_FEE,
|
|
1343
|
+
MEGAETH_GAS_LIMITS,
|
|
1344
|
+
MEGAETH_L1_BRIDGE,
|
|
1345
|
+
MEGAETH_TOKENS,
|
|
1346
|
+
MEGA_ROUTER_ABI,
|
|
1347
|
+
MegaFlowBuilder,
|
|
1348
|
+
MegaFlowClient,
|
|
1349
|
+
MegaFlowError,
|
|
1350
|
+
UNISWAP_V2_ROUTER_ABI,
|
|
1351
|
+
WETH_ABI,
|
|
1352
|
+
applyMegaethGasBuffer,
|
|
1353
|
+
buildKyberSwap,
|
|
1354
|
+
buildSafeApproveCalls,
|
|
1355
|
+
calculateTotalValue,
|
|
1356
|
+
chainById,
|
|
1357
|
+
chunkCalls,
|
|
1358
|
+
createMegaFlow,
|
|
1359
|
+
createMegaFlowClient,
|
|
1360
|
+
createMegaFlowClientMainnet,
|
|
1361
|
+
createMegaFlowClientTestnet,
|
|
1362
|
+
createMegaFlowMainnet,
|
|
1363
|
+
createMegaFlowTestnet,
|
|
1364
|
+
deadlineInMinutes,
|
|
1365
|
+
getKyberQuote,
|
|
1366
|
+
isUserRejection,
|
|
1367
|
+
isValidAddress,
|
|
1368
|
+
isValidHex,
|
|
1369
|
+
megaethChains,
|
|
1370
|
+
megaethMainnet,
|
|
1371
|
+
megaethTestnet,
|
|
1372
|
+
parseBatchExecutedEvent,
|
|
1373
|
+
parseCallExecutedEvents,
|
|
1374
|
+
parseCallResults
|
|
1375
|
+
});
|