@continuumdao/ctm-mpc-defi 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/agent/catalog.cjs +878 -141
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.cts +756 -12
  4. package/dist/agent/catalog.d.ts +756 -12
  5. package/dist/agent/catalog.js +829 -142
  6. package/dist/agent/catalog.js.map +1 -1
  7. package/dist/chains/evm/index.cjs +13 -0
  8. package/dist/chains/evm/index.cjs.map +1 -1
  9. package/dist/chains/evm/index.d.cts +3 -1
  10. package/dist/chains/evm/index.d.ts +3 -1
  11. package/dist/chains/evm/index.js +13 -1
  12. package/dist/chains/evm/index.js.map +1 -1
  13. package/dist/index.cjs +825 -141
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +2 -1
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.js +825 -142
  18. package/dist/index.js.map +1 -1
  19. package/dist/protocols/evm/aave-v4/index.cjs +1987 -0
  20. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -0
  21. package/dist/protocols/evm/aave-v4/index.d.cts +500 -0
  22. package/dist/protocols/evm/aave-v4/index.d.ts +500 -0
  23. package/dist/protocols/evm/aave-v4/index.js +1943 -0
  24. package/dist/protocols/evm/aave-v4/index.js.map +1 -0
  25. package/dist/protocols/evm/ethena/index.cjs +965 -0
  26. package/dist/protocols/evm/ethena/index.cjs.map +1 -0
  27. package/dist/protocols/evm/ethena/index.d.cts +161 -0
  28. package/dist/protocols/evm/ethena/index.d.ts +161 -0
  29. package/dist/protocols/evm/ethena/index.js +943 -0
  30. package/dist/protocols/evm/ethena/index.js.map +1 -0
  31. package/dist/protocols/evm/euler-v2/index.cjs +2263 -0
  32. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -0
  33. package/dist/protocols/evm/euler-v2/index.d.cts +317 -0
  34. package/dist/protocols/evm/euler-v2/index.d.ts +317 -0
  35. package/dist/protocols/evm/euler-v2/index.js +2238 -0
  36. package/dist/protocols/evm/euler-v2/index.js.map +1 -0
  37. package/dist/protocols/evm/lido/index.cjs +834 -0
  38. package/dist/protocols/evm/lido/index.cjs.map +1 -0
  39. package/dist/protocols/evm/lido/index.d.cts +120 -0
  40. package/dist/protocols/evm/lido/index.d.ts +120 -0
  41. package/dist/protocols/evm/lido/index.js +809 -0
  42. package/dist/protocols/evm/lido/index.js.map +1 -0
  43. package/dist/protocols/evm/maple/index.cjs +707 -0
  44. package/dist/protocols/evm/maple/index.cjs.map +1 -0
  45. package/dist/protocols/evm/maple/index.d.cts +109 -0
  46. package/dist/protocols/evm/maple/index.d.ts +109 -0
  47. package/dist/protocols/evm/maple/index.js +693 -0
  48. package/dist/protocols/evm/maple/index.js.map +1 -0
  49. package/dist/protocols/evm/sky/index.cjs +1254 -0
  50. package/dist/protocols/evm/sky/index.cjs.map +1 -0
  51. package/dist/protocols/evm/sky/index.d.cts +218 -0
  52. package/dist/protocols/evm/sky/index.d.ts +218 -0
  53. package/dist/protocols/evm/sky/index.js +1229 -0
  54. package/dist/protocols/evm/sky/index.js.map +1 -0
  55. package/package.json +37 -3
@@ -0,0 +1,693 @@
1
+ import { parseAbi, getAddress, parseUnits, encodeFunctionData, parseGwei, serializeTransaction, keccak256, defineChain, createPublicClient, http, stringToBytes, toHex, formatUnits } from 'viem';
2
+
3
+ // src/core/registry.ts
4
+ var modules = [];
5
+ function registerProtocolModule(mod) {
6
+ const existing = modules.findIndex((m) => m.id === mod.id);
7
+ if (existing >= 0) {
8
+ modules[existing] = mod;
9
+ } else {
10
+ modules.push(mod);
11
+ }
12
+ }
13
+
14
+ // src/protocols/evm/maple/constants.ts
15
+ var MAPLE_SYRUP_DEPOSIT_DATA_ASCII = "0:continuumdao";
16
+ var MAPLE_INTEGRATION_DOCS_URL = "https://docs.maple.finance/integrate/ethereum-mainnet/frontend-integration";
17
+ function mapleGraphqlEndpointForChain(chainId) {
18
+ if (chainId === 1) return "https://api.maple.finance/v2/graphql";
19
+ if (chainId === 11155111) return "https://sepolia.api.maple.finance/v2/graphql";
20
+ return null;
21
+ }
22
+ function isMapleSyrupSupportedChain(chainId) {
23
+ return chainId === 1 || chainId === 11155111;
24
+ }
25
+
26
+ // src/core/keygen.ts
27
+ function firstClientIdFromKeyGen(data) {
28
+ const map = data?.ClientKeys;
29
+ if (!map || typeof map !== "object") return null;
30
+ for (const v of Object.values(map)) {
31
+ if (typeof v === "string" && v.trim()) return v.trim();
32
+ }
33
+ return null;
34
+ }
35
+
36
+ // src/chains/evm/txParams.ts
37
+ function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
38
+ if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
39
+ return estimatedGas;
40
+ }
41
+ const cfg = BigInt(Math.floor(chainGasLimit));
42
+ return cfg > estimatedGas ? cfg : estimatedGas;
43
+ }
44
+ async function fetchChainFeeParams(rpcUrl, chainId) {
45
+ const url = rpcUrl.trim();
46
+ if (!url) return { isEip1559: false };
47
+ const chainIdNum = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
48
+ if (Number.isNaN(chainIdNum)) return { isEip1559: false };
49
+ const chain = defineChain({
50
+ id: chainIdNum,
51
+ name: "Discovery",
52
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
53
+ rpcUrls: { default: { http: [url] } }
54
+ });
55
+ const publicClient = createPublicClient({
56
+ chain,
57
+ transport: http(url)
58
+ });
59
+ const getGasPriceGwei = async () => {
60
+ const gasPriceWei = await publicClient.getGasPrice();
61
+ return parseFloat(formatUnits(gasPriceWei, 9));
62
+ };
63
+ try {
64
+ const block = await publicClient.getBlock({ blockTag: "latest" });
65
+ const baseFeePerGas = block?.baseFeePerGas;
66
+ if (baseFeePerGas == null || baseFeePerGas === void 0) {
67
+ const gasPriceGwei2 = await getGasPriceGwei();
68
+ return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
69
+ }
70
+ const baseFeeGwei = parseFloat(formatUnits(baseFeePerGas, 9));
71
+ let priorityFeeGwei;
72
+ try {
73
+ const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
74
+ priorityFeeGwei = parseFloat(formatUnits(priorityWei, 9));
75
+ } catch {
76
+ }
77
+ const gasPriceGwei = await getGasPriceGwei();
78
+ return {
79
+ isEip1559: true,
80
+ baseFeeGwei,
81
+ priorityFeeGwei,
82
+ gasPriceGwei
83
+ };
84
+ } catch {
85
+ try {
86
+ const gasPriceWei = await publicClient.getGasPrice();
87
+ const gasPriceGwei = parseFloat(formatUnits(gasPriceWei, 9));
88
+ return { isEip1559: false, gasPriceGwei };
89
+ } catch {
90
+ return { isEip1559: false };
91
+ }
92
+ }
93
+ }
94
+ function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
95
+ let maxP = maxPriorityFeePerGas;
96
+ let maxF = maxFeePerGas;
97
+ if (baseWei > 0n && maxF < baseWei + maxP) {
98
+ maxF = baseWei + maxP + parseGwei("0.001");
99
+ }
100
+ if (maxF < maxP) {
101
+ maxF = baseWei > 0n ? baseWei + maxP + parseGwei("0.001") : maxP * 2n;
102
+ }
103
+ return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
104
+ }
105
+ function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
106
+ return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
107
+ }
108
+
109
+ // src/protocols/evm/maple/multisign.ts
110
+ var AAVE_ERC20_APPROVE_FALLBACK = 100000n;
111
+ var MAPLE_SYRUP_DEPOSIT_GAS_FALLBACK = 1500000n;
112
+ var MAPLE_SYRUP_DEPOSIT_FALLBACK = MAPLE_SYRUP_DEPOSIT_GAS_FALLBACK;
113
+ var MAPLE_REQUEST_REDEEM_FALLBACK = 1200000n;
114
+ var erc20AllowanceAbi = parseAbi([
115
+ "function allowance(address owner, address spender) view returns (uint256)",
116
+ "function decimals() view returns (uint8)"
117
+ ]);
118
+ var erc20ApproveAbi = parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
119
+ var syrupRouterAbi = parseAbi([
120
+ "function deposit(uint256 assets, bytes32 depositData)",
121
+ "function authorizeAndDeposit(uint256 bitmap,uint256 deadline,uint8 auth_v,bytes32 auth_r,bytes32 auth_s,uint256 amount,bytes32 depositData)"
122
+ ]);
123
+ var poolV2ExitAbi = parseAbi([
124
+ "function previewDeposit(uint256 assets) view returns (uint256 shares)",
125
+ "function convertToExitShares(uint256 assets_) view returns (uint256 shares_)",
126
+ "function requestRedeem(uint256 shares, address receiver) returns (uint256)",
127
+ "function decimals() view returns (uint8)"
128
+ ]);
129
+ function gweiToDecimalString(n) {
130
+ if (!Number.isFinite(n)) return "0";
131
+ if (n === 0) return "0";
132
+ const s = String(n);
133
+ if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
134
+ return s;
135
+ }
136
+ function formatBytes32DataLabel(s) {
137
+ const b = stringToBytes(s);
138
+ if (b.length > 32) throw new Error("Maple depositData label is too long for bytes32.");
139
+ const padded = new Uint8Array(32);
140
+ padded.set(b);
141
+ return toHex(padded);
142
+ }
143
+ function publicClientForChain(chainId, rpcUrl) {
144
+ const rpc = rpcUrl.trim();
145
+ if (chainId === 1) {
146
+ const ch = defineChain({
147
+ id: 1,
148
+ name: "Ethereum",
149
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
150
+ rpcUrls: { default: { http: [rpc] } }
151
+ });
152
+ return createPublicClient({ chain: ch, transport: http(rpc) });
153
+ }
154
+ if (chainId === 11155111) {
155
+ const ch = defineChain({
156
+ id: 11155111,
157
+ name: "Sepolia",
158
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
159
+ rpcUrls: { default: { http: [rpc] } }
160
+ });
161
+ return createPublicClient({ chain: ch, transport: http(rpc) });
162
+ }
163
+ throw new Error(`Maple Syrup multisign: unsupported chain id ${chainId}.`);
164
+ }
165
+ async function readMaplePoolShareDecimals(args) {
166
+ const client = publicClientForChain(args.chainId, args.rpcUrl);
167
+ const d = await client.readContract({
168
+ address: getAddress(args.pool),
169
+ abi: poolV2ExitAbi,
170
+ functionName: "decimals"
171
+ });
172
+ return Number(d);
173
+ }
174
+ async function readMapleStakePreviewShares(args) {
175
+ if (args.assetAmountWei <= 0n) return 0n;
176
+ const client = publicClientForChain(args.chainId, args.rpcUrl);
177
+ return client.readContract({
178
+ address: getAddress(args.pool),
179
+ abi: poolV2ExitAbi,
180
+ functionName: "previewDeposit",
181
+ args: [args.assetAmountWei]
182
+ });
183
+ }
184
+ async function readMapleUnstakeExitSharesForAssets(args) {
185
+ if (args.assetAmountWei <= 0n) return 0n;
186
+ const client = publicClientForChain(args.chainId, args.rpcUrl);
187
+ return client.readContract({
188
+ address: getAddress(args.pool),
189
+ abi: poolV2ExitAbi,
190
+ functionName: "convertToExitShares",
191
+ args: [args.assetAmountWei]
192
+ });
193
+ }
194
+ function parseMapleStakeAuthInputs(args) {
195
+ const bitmapT = args.bitmap.trim();
196
+ let bitmap;
197
+ if (!bitmapT) return { ok: false, message: "Enter authorization bitmap (uint256)." };
198
+ try {
199
+ bitmap = bitmapT.startsWith("0x") || bitmapT.startsWith("0X") ? BigInt(bitmapT) : BigInt(bitmapT);
200
+ } catch {
201
+ return { ok: false, message: "Bitmap must be a decimal or 0x-prefixed integer." };
202
+ }
203
+ const deadlineT = args.deadline.trim();
204
+ if (!deadlineT || !/^\d+$/.test(deadlineT)) {
205
+ return { ok: false, message: "Deadline must be a Unix timestamp (seconds), decimal digits only." };
206
+ }
207
+ const deadline = BigInt(deadlineT);
208
+ if (deadline <= 0n) return { ok: false, message: "Deadline must be positive." };
209
+ const vT = args.v.trim();
210
+ if (!/^\d+$/.test(vT)) return { ok: false, message: "v must be an integer 0\u2013255 (often 27 or 28)." };
211
+ const v = Number(vT);
212
+ if (!Number.isFinite(v) || v < 0 || v > 255) return { ok: false, message: "v must be between 0 and 255." };
213
+ let r = args.r.trim();
214
+ let s = args.s.trim();
215
+ if (!r.startsWith("0x")) r = "0x" + r;
216
+ if (!s.startsWith("0x")) s = "0x" + s;
217
+ if (!/^0x[0-9a-fA-F]{64}$/.test(r)) return { ok: false, message: "r must be 32 bytes (64 hex chars), optional 0x." };
218
+ if (!/^0x[0-9a-fA-F]{64}$/.test(s)) return { ok: false, message: "s must be 32 bytes (64 hex chars), optional 0x." };
219
+ return { ok: true, sig: { bitmap, deadline, v, r, s } };
220
+ }
221
+ async function buildEvmMultisignBodyMapleSyrupDeposit(args) {
222
+ if (args.chainId !== 1 && args.chainId !== 11155111) {
223
+ throw new Error("Maple Syrup deposit is only supported on Ethereum mainnet or Sepolia.");
224
+ }
225
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
226
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
227
+ const keyList = args.keyGen.keylist ?? [];
228
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
229
+ const asset = getAddress(args.asset);
230
+ const router = getAddress(args.syrupRouter);
231
+ const pool = getAddress(args.pool);
232
+ const executor = getAddress(args.executorAddress);
233
+ const depositData = formatBytes32DataLabel(MAPLE_SYRUP_DEPOSIT_DATA_ASCII);
234
+ const publicClient = publicClientForChain(args.chainId, args.rpcUrl);
235
+ const dec = args.assetDecimals;
236
+ if (!Number.isFinite(dec) || dec < 0 || dec > 255) throw new Error("Invalid asset decimals.");
237
+ const amountWei = parseUnits(args.amountHuman, dec);
238
+ if (amountWei === 0n) throw new Error("Amount is zero after converting with token decimals.");
239
+ const stakeMethod = args.stakeMethod ?? "deposit";
240
+ if (stakeMethod === "authorizeAndDeposit") {
241
+ const a = args.authorizeSig;
242
+ if (!a) throw new Error("authorizeSig is required for authorizeAndDeposit.");
243
+ if (a.v < 0 || a.v > 255) throw new Error("Invalid authorization v (must be 0\u2013255).");
244
+ }
245
+ const steps = [];
246
+ const currentAllowance = await publicClient.readContract({
247
+ address: asset,
248
+ abi: erc20AllowanceAbi,
249
+ functionName: "allowance",
250
+ args: [executor, router]
251
+ });
252
+ if (currentAllowance < amountWei) {
253
+ if (currentAllowance > 0n) {
254
+ const dataReset = encodeFunctionData({
255
+ abi: erc20ApproveAbi,
256
+ functionName: "approve",
257
+ args: [router, 0n]
258
+ });
259
+ steps.push({ kind: "approve", to: asset, data: dataReset, value: 0n });
260
+ }
261
+ const dataApprove = encodeFunctionData({
262
+ abi: erc20ApproveAbi,
263
+ functionName: "approve",
264
+ args: [router, amountWei]
265
+ });
266
+ steps.push({ kind: "approve", to: asset, data: dataApprove, value: 0n });
267
+ }
268
+ if (stakeMethod === "authorizeAndDeposit") {
269
+ const a = args.authorizeSig;
270
+ const authCalldata = encodeFunctionData({
271
+ abi: syrupRouterAbi,
272
+ functionName: "authorizeAndDeposit",
273
+ args: [a.bitmap, a.deadline, a.v, a.r, a.s, amountWei, depositData]
274
+ });
275
+ steps.push({ kind: "authorizeAndDeposit", to: router, data: authCalldata, value: 0n });
276
+ } else {
277
+ const depositCalldata = encodeFunctionData({
278
+ abi: syrupRouterAbi,
279
+ functionName: "deposit",
280
+ args: [amountWei, depositData]
281
+ });
282
+ steps.push({ kind: "deposit", to: router, data: depositCalldata, value: 0n });
283
+ }
284
+ const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
285
+ const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
286
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
287
+ const useCustomGas = args.useCustomGas;
288
+ const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
289
+ const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
290
+ const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
291
+ const messageHashes = [];
292
+ const messageRawBatch = [];
293
+ const proposalTxParamsBatch = [];
294
+ const batchMeta = [];
295
+ let firstTxFeePayload = {};
296
+ let firstDataNo0x = "";
297
+ for (let i = 0; i < steps.length; i++) {
298
+ const s = steps[i];
299
+ const currentNonce = baseNonce + i;
300
+ let estimatedGas;
301
+ const skipFinalStakeEstimateBecausePriorApproves = (s.kind === "deposit" || s.kind === "authorizeAndDeposit") && i > 0;
302
+ if (skipFinalStakeEstimateBecausePriorApproves) {
303
+ estimatedGas = MAPLE_SYRUP_DEPOSIT_FALLBACK;
304
+ } else {
305
+ try {
306
+ estimatedGas = await publicClient.estimateGas({
307
+ to: s.to,
308
+ data: s.data,
309
+ value: s.value,
310
+ account: executor
311
+ });
312
+ } catch (e) {
313
+ if (s.kind === "deposit" || s.kind === "authorizeAndDeposit") {
314
+ const msg = e instanceof Error ? e.message : String(e);
315
+ if (/NOT_AUTHORIZED|SR:D:NOT_AUTHORIZED/i.test(msg)) {
316
+ throw new Error(
317
+ stakeMethod === "authorizeAndDeposit" ? "Maple SyrupRouter rejected authorizeAndDeposit (SR:D:NOT_AUTHORIZED). Check the authorization signature, deadline, and bitmap from Maple; they must match this chain, router, and owner." : "Maple SyrupRouter rejected deposit (SR:D:NOT_AUTHORIZED): this executor is not allowlisted as a Syrup lender on-chain. Use authorizeAndDeposit with a Maple-issued signature for the first deposit, or ask Maple to allowlist this address."
318
+ );
319
+ }
320
+ }
321
+ estimatedGas = s.kind === "approve" ? AAVE_ERC20_APPROVE_FALLBACK : MAPLE_SYRUP_DEPOSIT_FALLBACK;
322
+ }
323
+ }
324
+ const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
325
+ if (legacy) {
326
+ let gasPriceWei = await publicClient.getGasPrice();
327
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
328
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
329
+ }
330
+ if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
331
+ const configured = parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
332
+ if (configured > gasPriceWei) gasPriceWei = configured;
333
+ }
334
+ const ser = serializeTransaction({
335
+ type: "legacy",
336
+ to: s.to,
337
+ data: s.data,
338
+ value: s.value,
339
+ gas: gasLimitI,
340
+ gasPrice: gasPriceWei,
341
+ nonce: currentNonce,
342
+ chainId: args.chainId
343
+ });
344
+ const h = keccak256(ser);
345
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
346
+ messageRawBatch.push(ser);
347
+ proposalTxParamsBatch.push({
348
+ nonce: currentNonce,
349
+ gasLimit: gasLimitI.toString(),
350
+ txType: "legacy",
351
+ gasPrice: gasPriceWei.toString()
352
+ });
353
+ if (i === 0) {
354
+ firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
355
+ firstDataNo0x = s.data.startsWith("0x") ? s.data.slice(2) : s.data;
356
+ }
357
+ } else {
358
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
359
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
360
+ const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
361
+ const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
362
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
363
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
364
+ const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
365
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
366
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
367
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
368
+ let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
369
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
370
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
371
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
372
+ }
373
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
374
+ maxFeePerGas,
375
+ maxPriorityFeePerGas,
376
+ latestBaseFeeWei
377
+ ));
378
+ const ser = serializeTransaction({
379
+ type: "eip1559",
380
+ to: s.to,
381
+ data: s.data,
382
+ value: s.value,
383
+ gas: gasLimitI,
384
+ maxFeePerGas,
385
+ maxPriorityFeePerGas,
386
+ nonce: currentNonce,
387
+ chainId: args.chainId
388
+ });
389
+ const h = keccak256(ser);
390
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
391
+ messageRawBatch.push(ser);
392
+ proposalTxParamsBatch.push({
393
+ nonce: currentNonce,
394
+ gasLimit: gasLimitI.toString(),
395
+ txType: "eip1559",
396
+ maxFeePerGas: maxFeePerGas.toString(),
397
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
398
+ });
399
+ if (i === 0) {
400
+ firstTxFeePayload = {
401
+ txNonce: currentNonce,
402
+ txGasLimit: gasLimitI.toString(),
403
+ txMaxFeePerGas: maxFeePerGas.toString(),
404
+ txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
405
+ };
406
+ firstDataNo0x = s.data.startsWith("0x") ? s.data.slice(2) : s.data;
407
+ }
408
+ }
409
+ const chainIdStr = String(args.chainId);
410
+ if (s.kind === "approve") {
411
+ batchMeta.push({
412
+ destinationAddress: asset,
413
+ signatureText: JSON.stringify({
414
+ kind: "MapleSyrup",
415
+ name: "ERC20.approve",
416
+ note: "Allow SyrupRouter to pull this deposit amount.",
417
+ spender: router,
418
+ asset,
419
+ pool,
420
+ amountHuman: args.amountHuman
421
+ }),
422
+ evm: { type: "maple_syrup_erc20_approve", version: 1, chainId: chainIdStr },
423
+ maple: { step: "approve_asset", router, pool, asset }
424
+ });
425
+ } else if (s.kind === "deposit") {
426
+ batchMeta.push({
427
+ destinationAddress: router,
428
+ signatureText: JSON.stringify({
429
+ kind: "MapleSyrup",
430
+ name: "SyrupRouter.deposit",
431
+ function: "deposit(uint256 assets, bytes32 depositData)",
432
+ assets: amountWei.toString(),
433
+ pool,
434
+ router,
435
+ amountHuman: args.amountHuman
436
+ }),
437
+ evm: { type: "maple_syrup_router_deposit", version: 1, chainId: chainIdStr },
438
+ maple: { step: "deposit", router, pool, gasBuildDeposit: { baseGasUnits: gasLimitI.toString() } }
439
+ });
440
+ } else {
441
+ const a = args.authorizeSig;
442
+ batchMeta.push({
443
+ destinationAddress: router,
444
+ signatureText: JSON.stringify({
445
+ kind: "MapleSyrup",
446
+ name: "SyrupRouter.authorizeAndDeposit",
447
+ function: "authorizeAndDeposit(uint256 bitmap,uint256 deadline,uint8 auth_v,bytes32 auth_r,bytes32 auth_s,uint256 amount,bytes32 depositData)",
448
+ bitmap: a.bitmap.toString(),
449
+ deadline: a.deadline.toString(),
450
+ auth_v: a.v,
451
+ assets: amountWei.toString(),
452
+ pool,
453
+ router,
454
+ amountHuman: args.amountHuman
455
+ }),
456
+ evm: { type: "maple_syrup_router_authorize_and_deposit", version: 1, chainId: chainIdStr },
457
+ maple: { step: "authorize_and_deposit", router, pool, gasBuildDeposit: { baseGasUnits: gasLimitI.toString() } }
458
+ });
459
+ }
460
+ }
461
+ const extraPayload = { batchMeta };
462
+ if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
463
+ extraPayload.customGasChainDetails = args.customGasChainDetails;
464
+ }
465
+ const extraJSON = JSON.stringify(extraPayload);
466
+ const firstSigText = batchMeta[0].signatureText;
467
+ const n = steps.length;
468
+ const finalVerb = stakeMethod === "authorizeAndDeposit" ? "authorizeAndDeposit (permission + deposit)" : "deposit";
469
+ const purposeSuffix = n === 1 ? `Maple Syrup: 1-tx \u2014 ${finalVerb} via SyrupRouter (allowance already set).` : `Maple Syrup: ${n}-tx batch \u2014 approve asset for SyrupRouter, then ${finalVerb}.`;
470
+ const bodyForSign = {
471
+ keyList,
472
+ pubKey: ph,
473
+ msgHash: messageHashes[0],
474
+ msgRaw: firstDataNo0x,
475
+ messageHashes,
476
+ messageRawBatch,
477
+ destinationChainID: String(args.chainId),
478
+ destinationAddress: steps[0].to,
479
+ extraJSON,
480
+ signatureText: firstSigText,
481
+ purpose: (() => {
482
+ const t = args.purposeText.trim();
483
+ return (t ? `${t}
484
+
485
+ ` : "") + purposeSuffix;
486
+ })(),
487
+ ...firstTxFeePayload,
488
+ proposalTxParams: proposalTxParamsBatch
489
+ };
490
+ if (clientId) bodyForSign.clientId = clientId;
491
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
492
+ }
493
+ async function buildEvmMultisignBodyMaplePoolRequestRedeem(args) {
494
+ if (args.chainId !== 1 && args.chainId !== 11155111) {
495
+ throw new Error("Maple Syrup redeem request is only supported on Ethereum mainnet or Sepolia.");
496
+ }
497
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
498
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
499
+ const keyList = args.keyGen.keylist ?? [];
500
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
501
+ const pool = getAddress(args.pool);
502
+ const executor = getAddress(args.executorAddress);
503
+ const receiver = executor;
504
+ const ud = args.underlyingDecimals;
505
+ if (!Number.isFinite(ud) || ud < 0 || ud > 255) throw new Error("Invalid underlying decimals.");
506
+ const assetWei = parseUnits(args.underlyingAmountHuman, ud);
507
+ if (assetWei === 0n) throw new Error("Amount is zero after converting with token decimals.");
508
+ const publicClient = publicClientForChain(args.chainId, args.rpcUrl);
509
+ const exitShares = await publicClient.readContract({
510
+ address: pool,
511
+ abi: poolV2ExitAbi,
512
+ functionName: "convertToExitShares",
513
+ args: [assetWei]
514
+ });
515
+ if (exitShares === 0n) throw new Error("Exit shares are zero for this amount; check pool liquidity and amount.");
516
+ const data = encodeFunctionData({
517
+ abi: poolV2ExitAbi,
518
+ functionName: "requestRedeem",
519
+ args: [exitShares, receiver]
520
+ });
521
+ const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
522
+ const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
523
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
524
+ const useCustomGas = args.useCustomGas;
525
+ const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
526
+ const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
527
+ const currentNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
528
+ let estimatedGas;
529
+ try {
530
+ estimatedGas = await publicClient.estimateGas({
531
+ to: pool,
532
+ data,
533
+ value: 0n,
534
+ account: executor
535
+ });
536
+ } catch {
537
+ estimatedGas = MAPLE_REQUEST_REDEEM_FALLBACK;
538
+ }
539
+ const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
540
+ const chainIdStr = String(args.chainId);
541
+ let firstTxFeePayload = {};
542
+ let ser;
543
+ let proposalTxParams0;
544
+ if (legacy) {
545
+ let gasPriceWei = await publicClient.getGasPrice();
546
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
547
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
548
+ }
549
+ if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
550
+ const configured = parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
551
+ if (configured > gasPriceWei) gasPriceWei = configured;
552
+ }
553
+ ser = serializeTransaction({
554
+ type: "legacy",
555
+ to: pool,
556
+ data,
557
+ value: 0n,
558
+ gas: gasLimitI,
559
+ gasPrice: gasPriceWei,
560
+ nonce: currentNonce,
561
+ chainId: args.chainId
562
+ });
563
+ firstTxFeePayload = {
564
+ txNonce: currentNonce,
565
+ txGasLimit: gasLimitI.toString(),
566
+ txGasPrice: gasPriceWei.toString()
567
+ };
568
+ proposalTxParams0 = {
569
+ nonce: currentNonce,
570
+ gasLimit: gasLimitI.toString(),
571
+ txType: "legacy",
572
+ gasPrice: gasPriceWei.toString()
573
+ };
574
+ } else {
575
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
576
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
577
+ const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
578
+ const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
579
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
580
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
581
+ const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
582
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
583
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
584
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
585
+ let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
586
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
587
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
588
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
589
+ }
590
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
591
+ maxFeePerGas,
592
+ maxPriorityFeePerGas,
593
+ latestBaseFeeWei
594
+ ));
595
+ ser = serializeTransaction({
596
+ type: "eip1559",
597
+ to: pool,
598
+ data,
599
+ value: 0n,
600
+ gas: gasLimitI,
601
+ maxFeePerGas,
602
+ maxPriorityFeePerGas,
603
+ nonce: currentNonce,
604
+ chainId: args.chainId
605
+ });
606
+ firstTxFeePayload = {
607
+ txNonce: currentNonce,
608
+ txGasLimit: gasLimitI.toString(),
609
+ txMaxFeePerGas: maxFeePerGas.toString(),
610
+ txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
611
+ };
612
+ proposalTxParams0 = {
613
+ nonce: currentNonce,
614
+ gasLimit: gasLimitI.toString(),
615
+ txType: "eip1559",
616
+ maxFeePerGas: maxFeePerGas.toString(),
617
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
618
+ };
619
+ }
620
+ const h = keccak256(ser);
621
+ const messageHashes = [h.startsWith("0x") ? h.slice(2) : h];
622
+ const messageRawBatch = [ser];
623
+ const proposalTxParamsBatch = [proposalTxParams0];
624
+ const firstDataNo0x = data.startsWith("0x") ? data.slice(2) : data;
625
+ const batchMeta = [
626
+ {
627
+ destinationAddress: pool,
628
+ signatureText: JSON.stringify({
629
+ kind: "MapleSyrup",
630
+ name: "PoolV2.requestRedeem",
631
+ function: "requestRedeem(uint256 shares, address receiver)",
632
+ shares: exitShares.toString(),
633
+ underlyingAmountHuman: args.underlyingAmountHuman,
634
+ receiver,
635
+ pool
636
+ }),
637
+ evm: { type: "maple_pool_v2_request_redeem", version: 1, chainId: chainIdStr },
638
+ maple: { step: "request_redeem", pool, gasBuildRedeem: { baseGasUnits: gasLimitI.toString() } }
639
+ }
640
+ ];
641
+ const extraPayload = { batchMeta };
642
+ if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
643
+ extraPayload.customGasChainDetails = args.customGasChainDetails;
644
+ }
645
+ const extraJSON = JSON.stringify(extraPayload);
646
+ const purposeSuffix = "Maple Syrup: 1-tx \u2014 request redeem (queue withdrawal of underlying from pool shares).";
647
+ const bodyForSign = {
648
+ keyList,
649
+ pubKey: ph,
650
+ msgHash: messageHashes[0],
651
+ msgRaw: firstDataNo0x,
652
+ messageHashes,
653
+ messageRawBatch,
654
+ destinationChainID: String(args.chainId),
655
+ destinationAddress: pool,
656
+ extraJSON,
657
+ signatureText: batchMeta[0].signatureText,
658
+ purpose: (() => {
659
+ const t = args.purposeText.trim();
660
+ return (t ? `${t}
661
+
662
+ ` : "") + purposeSuffix;
663
+ })(),
664
+ ...firstTxFeePayload,
665
+ proposalTxParams: proposalTxParamsBatch
666
+ };
667
+ if (clientId) bodyForSign.clientId = clientId;
668
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
669
+ }
670
+
671
+ // src/protocols/evm/maple/index.ts
672
+ var MAPLE_PROTOCOL_ID = "maple-syrup";
673
+ var mapleProtocolModule = {
674
+ id: MAPLE_PROTOCOL_ID,
675
+ chainCategory: "evm",
676
+ isChainSupported(ctx) {
677
+ if (ctx.chainCategory !== "evm") return false;
678
+ const n = typeof ctx.chainId === "number" ? ctx.chainId : Number.parseInt(String(ctx.chainId), 10);
679
+ return isMapleSyrupSupportedChain(n);
680
+ },
681
+ isTokenSupported(token) {
682
+ return token.category === "evm" && token.kind === "erc20";
683
+ },
684
+ actions: [
685
+ { id: "maple-syrup.deposit", protocolId: MAPLE_PROTOCOL_ID, chainCategory: "evm", description: "Deposit into Maple Syrup pool", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
686
+ { id: "maple-syrup.request-redeem", protocolId: MAPLE_PROTOCOL_ID, chainCategory: "evm", description: "Request redeem from pool", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} }
687
+ ]
688
+ };
689
+ registerProtocolModule(mapleProtocolModule);
690
+
691
+ export { MAPLE_INTEGRATION_DOCS_URL, MAPLE_PROTOCOL_ID, MAPLE_SYRUP_DEPOSIT_DATA_ASCII, MAPLE_SYRUP_DEPOSIT_GAS_FALLBACK, buildEvmMultisignBodyMaplePoolRequestRedeem, buildEvmMultisignBodyMapleSyrupDeposit, isMapleSyrupSupportedChain, mapleGraphqlEndpointForChain, mapleProtocolModule, parseMapleStakeAuthInputs, readMaplePoolShareDecimals, readMapleStakePreviewShares, readMapleUnstakeExitSharesForAssets };
692
+ //# sourceMappingURL=index.js.map
693
+ //# sourceMappingURL=index.js.map