@continuumdao/ctm-mpc-defi 0.1.4 → 0.2.1

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