@continuumdao/ctm-mpc-defi 0.2.0 → 0.2.2

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 (100) hide show
  1. package/README.md +20 -78
  2. package/dist/agent/catalog.cjs +563 -5
  3. package/dist/agent/catalog.cjs.map +1 -1
  4. package/dist/agent/catalog.d.ts +166 -20
  5. package/dist/agent/catalog.js +551 -7
  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 +13 -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 +79 -224
  16. package/dist/chains/evm/index.cjs.map +1 -1
  17. package/dist/chains/evm/index.d.ts +26 -26
  18. package/dist/chains/evm/index.js +69 -209
  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 +68 -106
  23. package/dist/core/index.cjs.map +1 -1
  24. package/dist/core/index.d.ts +21 -36
  25. package/dist/core/index.js +57 -96
  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 +356 -1855
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.ts +7 -11
  31. package/dist/index.js +332 -1826
  32. package/dist/index.js.map +1 -1
  33. package/dist/protocols/evm/aave-v4/index.cjs +1152 -669
  34. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
  35. package/dist/protocols/evm/aave-v4/index.d.ts +418 -3
  36. package/dist/protocols/evm/aave-v4/index.js +1126 -670
  37. package/dist/protocols/evm/aave-v4/index.js.map +1 -1
  38. package/dist/protocols/evm/curve-dao/index.cjs +257 -131
  39. package/dist/protocols/evm/curve-dao/index.cjs.map +1 -1
  40. package/dist/protocols/evm/curve-dao/index.d.ts +69 -5
  41. package/dist/protocols/evm/curve-dao/index.js +242 -124
  42. package/dist/protocols/evm/curve-dao/index.js.map +1 -1
  43. package/dist/protocols/evm/ethena/index.cjs +394 -402
  44. package/dist/protocols/evm/ethena/index.cjs.map +1 -1
  45. package/dist/protocols/evm/ethena/index.d.ts +47 -3
  46. package/dist/protocols/evm/ethena/index.js +390 -404
  47. package/dist/protocols/evm/ethena/index.js.map +1 -1
  48. package/dist/protocols/evm/euler-v2/index.cjs +2810 -1191
  49. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
  50. package/dist/protocols/evm/euler-v2/index.d.ts +465 -3
  51. package/dist/protocols/evm/euler-v2/index.js +2761 -1192
  52. package/dist/protocols/evm/euler-v2/index.js.map +1 -1
  53. package/dist/protocols/evm/lido/index.cjs +351 -236
  54. package/dist/protocols/evm/lido/index.cjs.map +1 -1
  55. package/dist/protocols/evm/lido/index.d.ts +34 -4
  56. package/dist/protocols/evm/lido/index.js +348 -238
  57. package/dist/protocols/evm/lido/index.js.map +1 -1
  58. package/dist/protocols/evm/maple/index.cjs +390 -395
  59. package/dist/protocols/evm/maple/index.cjs.map +1 -1
  60. package/dist/protocols/evm/maple/index.d.ts +23 -3
  61. package/dist/protocols/evm/maple/index.js +390 -397
  62. package/dist/protocols/evm/maple/index.js.map +1 -1
  63. package/dist/protocols/evm/sky/index.cjs +454 -232
  64. package/dist/protocols/evm/sky/index.cjs.map +1 -1
  65. package/dist/protocols/evm/sky/index.d.ts +57 -3
  66. package/dist/protocols/evm/sky/index.js +444 -231
  67. package/dist/protocols/evm/sky/index.js.map +1 -1
  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 +7 -6
  77. package/dist/agent/catalog.d.cts +0 -939
  78. package/dist/chains/evm/index.d.cts +0 -64
  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 -16
  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/aave-v4/index.d.cts +0 -500
  89. package/dist/protocols/evm/curve-dao/index.d.cts +0 -147
  90. package/dist/protocols/evm/ethena/index.d.cts +0 -161
  91. package/dist/protocols/evm/euler-v2/index.d.cts +0 -317
  92. package/dist/protocols/evm/lido/index.d.cts +0 -120
  93. package/dist/protocols/evm/maple/index.d.cts +0 -109
  94. package/dist/protocols/evm/sky/index.d.cts +0 -218
  95. package/dist/protocols/evm/uniswap-v4/index.d.cts +0 -324
  96. package/dist/registry-BwZoE668.d.cts +0 -8
  97. package/dist/txParams-BC7ogvdR.d.cts +0 -19
  98. package/dist/txParams-BC7ogvdR.d.ts +0 -19
  99. package/dist/types-B8idm_gu.d.cts +0 -34
  100. package/dist/types-Ce2qNHai.d.ts +0 -57
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var viem = require('viem');
4
+ var continuumNodeSdk = require('@continuumdao/continuum-node-sdk');
5
+ var chains = require('viem/chains');
4
6
 
5
7
  // src/core/registry.ts
6
8
  var modules = [];
@@ -144,101 +146,239 @@ function isEvmChainInEthenaUsdeList(chainId) {
144
146
  return usdeTokenAddressOnEvmChain(chainId) != null;
145
147
  }
146
148
 
147
- // src/core/keygen.ts
148
- function firstClientIdFromKeyGen(data) {
149
- const map = data?.ClientKeys;
150
- if (!map || typeof map !== "object") return null;
151
- for (const v of Object.values(map)) {
152
- if (typeof v === "string" && v.trim()) return v.trim();
153
- }
154
- return null;
149
+ // src/core/purpose.ts
150
+ function mergePurposeText(purposeText, purposeSuffix) {
151
+ const t = purposeText.trim();
152
+ const suffix = (purposeSuffix ?? "").trim();
153
+ if (!suffix) return t;
154
+ return t ? `${t}
155
+
156
+ ${suffix}` : suffix;
155
157
  }
156
158
 
157
- // src/chains/evm/txParams.ts
158
- function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
159
- if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
160
- return estimatedGas;
159
+ // src/core/envelope.ts
160
+ function finalizeMultisign(input) {
161
+ const { keyGen, destinationChainID, legs } = input;
162
+ if (legs.length === 0) {
163
+ throw new Error("finalizeMultisign requires at least one leg");
164
+ }
165
+ const ph = (keyGen.pubkeyhex ?? "").trim();
166
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
167
+ const keyList = keyGen.keylist ?? [];
168
+ const clientId = continuumNodeSdk.getClientIdFromKeyGenResult(keyGen);
169
+ const first = legs[0];
170
+ const messageHashes = legs.map((l) => l.msgHash);
171
+ const messageRawBatch = legs.map((l) => l.msgRaw);
172
+ const batchMeta = legs.map((l) => ({
173
+ destinationAddress: l.destinationAddress,
174
+ signatureText: l.signatureText,
175
+ ...l.audit
176
+ }));
177
+ const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
178
+ const extraPayload = {
179
+ batchMeta,
180
+ ...input.extraJSON ?? {}
181
+ };
182
+ const extraJSON = JSON.stringify(extraPayload);
183
+ const bodyForSign = {
184
+ keyList,
185
+ pubKey: ph,
186
+ msgHash: messageHashes[0],
187
+ msgRaw: first.msgRaw,
188
+ destinationChainID,
189
+ destinationAddress: input.destinationAddress ?? first.destinationAddress,
190
+ extraJSON,
191
+ signatureText: first.signatureText,
192
+ purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
193
+ ...first.feeSnapshot
194
+ };
195
+ if (legs.length > 1) {
196
+ bodyForSign.messageHashes = messageHashes;
197
+ bodyForSign.messageRawBatch = messageRawBatch;
198
+ }
199
+ if (proposalTxParams.length > 0) {
200
+ bodyForSign.proposalTxParams = proposalTxParams;
161
201
  }
162
- const cfg = BigInt(Math.floor(chainGasLimit));
163
- return cfg > estimatedGas ? cfg : estimatedGas;
202
+ const valueWei = first.valueWei;
203
+ if (valueWei != null && valueWei > 0n) {
204
+ bodyForSign.value = valueWei.toString();
205
+ }
206
+ if (clientId) bodyForSign.clientId = clientId;
207
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
164
208
  }
165
- async function fetchChainFeeParams(rpcUrl, chainId) {
166
- const url = rpcUrl.trim();
167
- if (!url) return { isEip1559: false };
168
- const chainIdNum = chainId;
169
- if (Number.isNaN(chainIdNum)) return { isEip1559: false };
170
- const chain = viem.defineChain({
171
- id: chainIdNum,
172
- name: "Discovery",
209
+ function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
210
+ if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
211
+ return continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
212
+ }
213
+ return (estimatedGas * 12n + 9n) / 10n;
214
+ }
215
+
216
+ // src/chains/evm/buildBatch.ts
217
+ async function buildEvmMultisignBatch(args) {
218
+ const { context, steps } = args;
219
+ const {
220
+ chainId,
221
+ rpcUrl,
222
+ executorAddress,
223
+ chainDetail,
224
+ useCustomGas,
225
+ customGasChainDetails,
226
+ keyGen,
227
+ purposeText
228
+ } = context;
229
+ if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
230
+ const ch = viem.defineChain({
231
+ id: chainId,
232
+ name: "Destination",
173
233
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
174
- rpcUrls: { default: { http: [url] } }
234
+ rpcUrls: { default: { http: [rpcUrl] } }
175
235
  });
176
- const publicClient = viem.createPublicClient({
177
- chain,
178
- transport: viem.http(url)
179
- });
180
- const getGasPriceGwei = async () => {
181
- const gasPriceWei = await publicClient.getGasPrice();
182
- return parseFloat(viem.formatUnits(gasPriceWei, 9));
183
- };
184
- try {
185
- const block = await publicClient.getBlock({ blockTag: "latest" });
186
- const baseFeePerGas = block?.baseFeePerGas;
187
- if (baseFeePerGas == null || baseFeePerGas === void 0) {
188
- const gasPriceGwei2 = await getGasPriceGwei();
189
- return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
236
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
237
+ const feeParams = await continuumNodeSdk.fetchChainFeeParams(rpcUrl, chainId);
238
+ const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
239
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
240
+ const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
241
+ const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
242
+ const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
243
+ const executor = viem.getAddress(executorAddress);
244
+ const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
245
+ const legs = [];
246
+ for (let i = 0; i < steps.length; i++) {
247
+ const step = steps[i];
248
+ const currentNonce = baseNonce + i;
249
+ let estimatedGas;
250
+ if (args.estimateGasForStep) {
251
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
252
+ } else {
253
+ try {
254
+ estimatedGas = await publicClient.estimateGas({
255
+ to: step.to,
256
+ data: step.data,
257
+ value: step.value,
258
+ account: executor
259
+ });
260
+ } catch {
261
+ estimatedGas = step.fallbackGas ?? 100000n;
262
+ }
190
263
  }
191
- const baseFeeGwei = parseFloat(viem.formatUnits(baseFeePerGas, 9));
192
- let priorityFeeGwei;
193
- try {
194
- const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
195
- priorityFeeGwei = parseFloat(viem.formatUnits(priorityWei, 9));
196
- } catch {
264
+ let gasLimitI;
265
+ if (args.resolveGasLimit) {
266
+ gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
267
+ } else if (step.routerSwap) {
268
+ gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
269
+ } else {
270
+ gasLimitI = useCustomGas ? continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
197
271
  }
198
- const gasPriceGwei = await getGasPriceGwei();
199
- return {
200
- isEip1559: true,
201
- baseFeeGwei,
202
- priorityFeeGwei,
203
- gasPriceGwei
204
- };
205
- } catch {
206
- try {
207
- const gasPriceWei = await publicClient.getGasPrice();
208
- const gasPriceGwei = parseFloat(viem.formatUnits(gasPriceWei, 9));
209
- return { isEip1559: false, gasPriceGwei };
210
- } catch {
211
- return { isEip1559: false };
272
+ let proposalTxParams;
273
+ let feeSnapshot;
274
+ let serialized;
275
+ if (legacy) {
276
+ let gasPriceWei = await publicClient.getGasPrice();
277
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
278
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
279
+ }
280
+ if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
281
+ const configured = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(Number(chainDetail.gasPrice)));
282
+ if (configured > gasPriceWei) gasPriceWei = configured;
283
+ }
284
+ serialized = viem.serializeTransaction({
285
+ type: "legacy",
286
+ to: step.to,
287
+ data: step.data,
288
+ value: step.value,
289
+ gas: gasLimitI,
290
+ gasPrice: gasPriceWei,
291
+ nonce: currentNonce,
292
+ chainId
293
+ });
294
+ proposalTxParams = {
295
+ nonce: currentNonce,
296
+ gasLimit: gasLimitI.toString(),
297
+ txType: "legacy",
298
+ gasPrice: gasPriceWei.toString()
299
+ };
300
+ feeSnapshot = continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams);
301
+ } else {
302
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
303
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
304
+ const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
305
+ const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
306
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
307
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
308
+ const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
309
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
310
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
311
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(continuumNodeSdk.gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
312
+ let maxFeePerGas = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(maxFeePerGasGwei));
313
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
314
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
315
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
316
+ }
317
+ ({ maxFeePerGas, maxPriorityFeePerGas } = continuumNodeSdk.alignEip1559FeesWithLatestBase(
318
+ maxFeePerGas,
319
+ maxPriorityFeePerGas,
320
+ latestBaseFeeWei
321
+ ));
322
+ serialized = viem.serializeTransaction({
323
+ type: "eip1559",
324
+ to: step.to,
325
+ data: step.data,
326
+ value: step.value,
327
+ gas: gasLimitI,
328
+ maxFeePerGas,
329
+ maxPriorityFeePerGas,
330
+ nonce: currentNonce,
331
+ chainId
332
+ });
333
+ proposalTxParams = {
334
+ nonce: currentNonce,
335
+ gasLimit: gasLimitI.toString(),
336
+ txType: "eip1559",
337
+ maxFeePerGas: maxFeePerGas.toString(),
338
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
339
+ };
340
+ feeSnapshot = i === 0 ? continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
341
+ }
342
+ const h = viem.keccak256(serialized);
343
+ const msgHash = h.startsWith("0x") ? h.slice(2) : h;
344
+ const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
345
+ legs.push({
346
+ msgHash,
347
+ msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
348
+ destinationAddress: step.to,
349
+ signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
350
+ audit: batchMetaExtra,
351
+ feeSnapshot: i === 0 ? feeSnapshot : {},
352
+ proposalTxParams,
353
+ valueWei: i === 0 ? step.value : void 0
354
+ });
355
+ if (i === 0 && args.firstMsgRawNo0x != null) {
356
+ legs[0].msgRaw = args.firstMsgRawNo0x;
212
357
  }
213
358
  }
214
- }
215
- function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
216
- let maxP = maxPriorityFeePerGas;
217
- let maxF = maxFeePerGas;
218
- if (baseWei > 0n && maxF < baseWei + maxP) {
219
- maxF = baseWei + maxP + viem.parseGwei("0.001");
220
- }
221
- if (maxF < maxP) {
222
- maxF = baseWei > 0n ? baseWei + maxP + viem.parseGwei("0.001") : maxP * 2n;
359
+ const extraJSON = {};
360
+ if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
361
+ extraJSON.customGasChainDetails = customGasChainDetails;
362
+ }
363
+ const result = finalizeMultisign({
364
+ keyGen,
365
+ purposeText,
366
+ purposeSuffix: args.purposeSuffix,
367
+ destinationChainID: String(chainId),
368
+ destinationAddress: args.destinationAddress ?? steps[0].to,
369
+ legs,
370
+ extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0
371
+ });
372
+ const pv = args.payableValueWei;
373
+ if (pv != null && pv > 0n) {
374
+ result.bodyForSign.value = pv.toString();
223
375
  }
224
- return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
376
+ return result;
225
377
  }
226
- function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
227
- return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
228
- }
229
-
230
- // src/protocols/evm/ethena/multisign.ts
231
378
  var AAVE_ERC20_APPROVE_FALLBACK = 100000n;
232
379
  var ETHENA_SUSDE_DEPOSIT_GET_SIG_GAS_FALLBACK = 1200000n;
233
380
  var ETHENA_SUSDE_DEPOSIT_FALLBACK = ETHENA_SUSDE_DEPOSIT_GET_SIG_GAS_FALLBACK;
234
381
  var ETHENA_SUSDE_REDEEM_FALLBACK = 1200000n;
235
- function gweiToDecimalString(n) {
236
- if (!Number.isFinite(n)) return "0";
237
- if (n === 0) return "0";
238
- const s = String(n);
239
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
240
- return s;
241
- }
242
382
  var erc20AllowanceAbi = viem.parseAbi([
243
383
  "function allowance(address owner, address spender) view returns (uint256)",
244
384
  "function decimals() view returns (uint8)"
@@ -295,10 +435,6 @@ async function buildEvmMultisignBodyEthenaUsdeStakeToSusde(args) {
295
435
  if (args.chainId !== 1) {
296
436
  throw new Error("Ethena USDe \u2192 sUSDe stake is only supported on Ethereum mainnet (chain id 1).");
297
437
  }
298
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
299
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
300
- const keyList = args.keyGen.keylist ?? [];
301
- const clientId = firstClientIdFromKeyGen(args.keyGen);
302
438
  const usde = viem.getAddress(args.usde);
303
439
  const mainnetUsde = viem.getAddress(USDE_ETHEREUM_MAINNET);
304
440
  if (usde.toLowerCase() !== mainnetUsde.toLowerCase()) {
@@ -353,141 +489,58 @@ async function buildEvmMultisignBodyEthenaUsdeStakeToSusde(args) {
353
489
  args: [amountWei, receiver]
354
490
  });
355
491
  steps.push({ kind: "deposit", to: susde, data: depositData, value: 0n });
356
- const feeParams = await fetchChainFeeParams(args.rpcUrl, 1);
357
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
358
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
359
- const useCustomGas = args.useCustomGas;
360
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
361
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
362
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
363
- const messageHashes = [];
364
- const messageRawBatch = [];
365
- const proposalTxParamsBatch = [];
366
- const batchMeta = [];
367
- let firstTxFeePayload = {};
368
- let firstDataNo0x = "";
369
- for (let i = 0; i < steps.length; i++) {
370
- const s = steps[i];
371
- const currentNonce = baseNonce + i;
372
- let estimatedGas;
373
- const skipDepositEstimateBecausePriorApproves = s.kind === "deposit" && i > 0;
374
- if (skipDepositEstimateBecausePriorApproves) {
375
- estimatedGas = ETHENA_SUSDE_DEPOSIT_FALLBACK;
376
- } else {
377
- try {
378
- estimatedGas = await publicClient.estimateGas({
379
- to: s.to,
380
- data: s.data,
381
- value: s.value,
382
- account: executor
383
- });
384
- } catch {
385
- estimatedGas = s.kind === "approve" ? AAVE_ERC20_APPROVE_FALLBACK : ETHENA_SUSDE_DEPOSIT_FALLBACK;
386
- }
387
- }
388
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
389
- if (legacy) {
390
- let gasPriceWei = await publicClient.getGasPrice();
391
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
392
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
393
- }
394
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
395
- const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
396
- if (configured > gasPriceWei) gasPriceWei = configured;
397
- }
398
- const ser = viem.serializeTransaction({
399
- type: "legacy",
400
- to: s.to,
401
- data: s.data,
402
- value: s.value,
403
- gas: gasLimitI,
404
- gasPrice: gasPriceWei,
405
- nonce: currentNonce,
406
- chainId: 1
407
- });
408
- const h = viem.keccak256(ser);
409
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
410
- messageRawBatch.push(ser);
411
- proposalTxParamsBatch.push({
412
- nonce: currentNonce,
413
- gasLimit: gasLimitI.toString(),
414
- txType: "legacy",
415
- gasPrice: gasPriceWei.toString()
416
- });
417
- if (i === 0) {
418
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
419
- firstDataNo0x = s.data.startsWith("0x") ? s.data.slice(2) : s.data;
420
- }
421
- } else {
422
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
423
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
424
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
425
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
426
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
427
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
428
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
429
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
430
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
431
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
432
- let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
433
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
434
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
435
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
492
+ const evmSteps = steps.map((s) => ({
493
+ to: s.to,
494
+ data: s.data,
495
+ value: s.value,
496
+ fallbackGas: s.kind === "approve" ? AAVE_ERC20_APPROVE_FALLBACK : ETHENA_SUSDE_DEPOSIT_FALLBACK
497
+ }));
498
+ const n = steps.length;
499
+ const purposeSuffix = n === 1 ? "Ethena: 1-tx \u2014 deposit USDe to sUSDe (allowance already set)." : `Ethena: ${n}-tx batch \u2014 approve USDe to sUSDe vault, then deposit.`;
500
+ const firstDataNo0x = evmSteps[0].data.startsWith("0x") ? evmSteps[0].data.slice(2) : evmSteps[0].data;
501
+ const gasLimitConfig = args.useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
502
+ return buildEvmMultisignBatch({
503
+ context: {
504
+ chainCategory: "evm",
505
+ keyGen: args.keyGen,
506
+ purposeText: args.purposeText,
507
+ chainId: 1,
508
+ rpcUrl: args.rpcUrl.trim(),
509
+ executorAddress: executor,
510
+ chainDetail: args.chainDetail,
511
+ useCustomGas: args.useCustomGas,
512
+ customGasChainDetails: args.customGasChainDetails
513
+ },
514
+ steps: evmSteps,
515
+ purposeSuffix,
516
+ firstMsgRawNo0x: firstDataNo0x,
517
+ destinationAddress: steps[0].to,
518
+ resolveGasLimit: ({ index, estimatedGas }) => {
519
+ const s = steps[index];
520
+ if (s.kind === "deposit" && index > 0) {
521
+ const gas = ETHENA_SUSDE_DEPOSIT_FALLBACK;
522
+ return args.useCustomGas ? continuumNodeSdk.gasLimitFromEstimateAndChainConfig(gas, gasLimitConfig) : gas;
436
523
  }
437
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
438
- maxFeePerGas,
439
- maxPriorityFeePerGas,
440
- latestBaseFeeWei
441
- ));
442
- const ser = viem.serializeTransaction({
443
- type: "eip1559",
444
- to: s.to,
445
- data: s.data,
446
- value: s.value,
447
- gas: gasLimitI,
448
- maxFeePerGas,
449
- maxPriorityFeePerGas,
450
- nonce: currentNonce,
451
- chainId: 1
452
- });
453
- const h = viem.keccak256(ser);
454
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
455
- messageRawBatch.push(ser);
456
- proposalTxParamsBatch.push({
457
- nonce: currentNonce,
458
- gasLimit: gasLimitI.toString(),
459
- txType: "eip1559",
460
- maxFeePerGas: maxFeePerGas.toString(),
461
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
462
- });
463
- if (i === 0) {
464
- firstTxFeePayload = {
465
- txNonce: currentNonce,
466
- txGasLimit: gasLimitI.toString(),
467
- txMaxFeePerGas: maxFeePerGas.toString(),
468
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
524
+ return estimatedGas;
525
+ },
526
+ buildBatchMeta: ({ index, gasLimit }) => {
527
+ const s = steps[index];
528
+ if (s.kind === "approve") {
529
+ return {
530
+ signatureText: JSON.stringify({
531
+ kind: "Ethena",
532
+ name: "ERC20.approve",
533
+ to: "sUSDe vault",
534
+ function: "approve(address spender, uint256 amount)",
535
+ spender: susde,
536
+ amountHuman: args.amountHuman,
537
+ note: "Allowance to stake this USDe amount (not unlimited)."
538
+ }),
539
+ evm: { type: "ethena_erc20_approve", version: 1, chainId: "1" },
540
+ ethena: { step: "approve_usde", susde }
469
541
  };
470
- firstDataNo0x = s.data.startsWith("0x") ? s.data.slice(2) : s.data;
471
542
  }
472
- }
473
- if (s.kind === "approve") {
474
- batchMeta.push({
475
- destinationAddress: usde,
476
- signatureText: JSON.stringify({
477
- kind: "Ethena",
478
- name: "ERC20.approve",
479
- to: "sUSDe vault",
480
- function: "approve(address spender, uint256 amount)",
481
- spender: susde,
482
- amountHuman: args.amountHuman,
483
- note: "Allowance to stake this USDe amount (not unlimited)."
484
- }),
485
- evm: { type: "ethena_erc20_approve", version: 1, chainId: "1" },
486
- ethena: { step: "approve_usde", susde }
487
- });
488
- } else {
489
- batchMeta.push({
490
- destinationAddress: susde,
543
+ return {
491
544
  signatureText: JSON.stringify({
492
545
  kind: "Ethena",
493
546
  name: "StakedUSDe (ERC-4626).deposit",
@@ -497,40 +550,10 @@ async function buildEvmMultisignBodyEthenaUsdeStakeToSusde(args) {
497
550
  amountHuman: args.amountHuman
498
551
  }),
499
552
  evm: { type: "ethena_susde_deposit", version: 1, chainId: "1" },
500
- ethena: { step: "deposit", vault: susde, gasBuildDeposit: { baseGasUnits: gasLimitI.toString() } }
501
- });
553
+ ethena: { step: "deposit", vault: susde, gasBuildDeposit: { baseGasUnits: gasLimit.toString() } }
554
+ };
502
555
  }
503
- }
504
- const extraPayload = { batchMeta };
505
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
506
- extraPayload.customGasChainDetails = args.customGasChainDetails;
507
- }
508
- const extraJSON = JSON.stringify(extraPayload);
509
- const firstSigText = batchMeta[0].signatureText;
510
- const n = steps.length;
511
- const purposeSuffix = n === 1 ? "Ethena: 1-tx \u2014 deposit USDe to sUSDe (allowance already set)." : `Ethena: ${n}-tx batch \u2014 approve USDe to sUSDe vault, then deposit.`;
512
- const bodyForSign = {
513
- keyList,
514
- pubKey: ph,
515
- msgHash: messageHashes[0],
516
- msgRaw: firstDataNo0x,
517
- messageHashes,
518
- messageRawBatch,
519
- destinationChainID: "1",
520
- destinationAddress: steps[0].to,
521
- extraJSON,
522
- signatureText: firstSigText,
523
- purpose: (() => {
524
- const t = args.purposeText.trim();
525
- return (t ? `${t}
526
-
527
- ` : "") + purposeSuffix;
528
- })(),
529
- ...firstTxFeePayload,
530
- proposalTxParams: proposalTxParamsBatch
531
- };
532
- if (clientId) bodyForSign.clientId = clientId;
533
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
556
+ });
534
557
  }
535
558
  var ETHENA_SUSDE_COOLDOWN_SHARES_FALLBACK = 1200000n;
536
559
  var ETHENA_SUSDE_UNSTAKE_CLAIM_FALLBACK = 500000n;
@@ -538,161 +561,27 @@ async function buildEthenaStakedUsdeOneTxMultisignBody(args) {
538
561
  if (args.chainId !== 1) {
539
562
  throw new Error("Ethena sUSDe exit is only supported on Ethereum mainnet (chain id 1).");
540
563
  }
541
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
542
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
543
- const keyList = args.keyGen.keylist ?? [];
544
- const clientId = firstClientIdFromKeyGen(args.keyGen);
545
- const { executor } = args;
546
- const steps = [args.step];
547
- const feeParams = await fetchChainFeeParams(args.rpcUrl, 1);
548
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
549
- const useCustomGas = args.useCustomGas;
550
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
551
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
552
- const ch = viem.defineChain({
553
- id: 1,
554
- name: "Ethereum",
555
- nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
556
- rpcUrls: { default: { http: [args.rpcUrl.trim()] } }
564
+ const step = args.step;
565
+ const evmSteps = [{ to: step.to, data: step.data, value: step.value, fallbackGas: args.gasFallback }];
566
+ const firstDataNo0x = step.data.startsWith("0x") ? step.data.slice(2) : step.data;
567
+ return buildEvmMultisignBatch({
568
+ context: {
569
+ chainCategory: "evm",
570
+ keyGen: args.keyGen,
571
+ purposeText: args.purposeText,
572
+ chainId: 1,
573
+ rpcUrl: args.rpcUrl.trim(),
574
+ executorAddress: args.executor,
575
+ chainDetail: args.chainDetail,
576
+ useCustomGas: args.useCustomGas,
577
+ customGasChainDetails: args.customGasChainDetails
578
+ },
579
+ steps: evmSteps,
580
+ purposeSuffix: args.purposeSuffix,
581
+ firstMsgRawNo0x: firstDataNo0x,
582
+ destinationAddress: step.to,
583
+ buildBatchMeta: ({ gasLimit }) => args.makeBatchMeta({ gasLimitI: gasLimit })
557
584
  });
558
- const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl.trim()) });
559
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
560
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
561
- const messageHashes = [];
562
- const messageRawBatch = [];
563
- const proposalTxParamsBatch = [];
564
- const batchMeta = [];
565
- let firstTxFeePayload = {};
566
- let firstDataNo0x = "";
567
- for (let i = 0; i < steps.length; i++) {
568
- const s = steps[i];
569
- const currentNonce = baseNonce + i;
570
- let estimatedGas;
571
- try {
572
- estimatedGas = await publicClient.estimateGas({
573
- to: s.to,
574
- data: s.data,
575
- value: s.value,
576
- account: executor
577
- });
578
- } catch {
579
- estimatedGas = args.gasFallback;
580
- }
581
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
582
- if (legacy) {
583
- let gasPriceWei = await publicClient.getGasPrice();
584
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
585
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
586
- }
587
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
588
- const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
589
- if (configured > gasPriceWei) gasPriceWei = configured;
590
- }
591
- const ser = viem.serializeTransaction({
592
- type: "legacy",
593
- to: s.to,
594
- data: s.data,
595
- value: s.value,
596
- gas: gasLimitI,
597
- gasPrice: gasPriceWei,
598
- nonce: currentNonce,
599
- chainId: 1
600
- });
601
- const h = viem.keccak256(ser);
602
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
603
- messageRawBatch.push(ser);
604
- proposalTxParamsBatch.push({
605
- nonce: currentNonce,
606
- gasLimit: gasLimitI.toString(),
607
- txType: "legacy",
608
- gasPrice: gasPriceWei.toString()
609
- });
610
- if (i === 0) {
611
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
612
- firstDataNo0x = s.data.startsWith("0x") ? s.data.slice(2) : s.data;
613
- }
614
- } else {
615
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
616
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
617
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
618
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
619
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
620
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
621
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
622
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
623
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
624
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
625
- let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
626
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
627
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
628
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
629
- }
630
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
631
- maxFeePerGas,
632
- maxPriorityFeePerGas,
633
- latestBaseFeeWei
634
- ));
635
- const ser = viem.serializeTransaction({
636
- type: "eip1559",
637
- to: s.to,
638
- data: s.data,
639
- value: s.value,
640
- gas: gasLimitI,
641
- maxFeePerGas,
642
- maxPriorityFeePerGas,
643
- nonce: currentNonce,
644
- chainId: 1
645
- });
646
- const h = viem.keccak256(ser);
647
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
648
- messageRawBatch.push(ser);
649
- proposalTxParamsBatch.push({
650
- nonce: currentNonce,
651
- gasLimit: gasLimitI.toString(),
652
- txType: "eip1559",
653
- maxFeePerGas: maxFeePerGas.toString(),
654
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
655
- });
656
- if (i === 0) {
657
- firstTxFeePayload = {
658
- txNonce: currentNonce,
659
- txGasLimit: gasLimitI.toString(),
660
- txMaxFeePerGas: maxFeePerGas.toString(),
661
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
662
- };
663
- firstDataNo0x = s.data.startsWith("0x") ? s.data.slice(2) : s.data;
664
- }
665
- }
666
- batchMeta.push(args.makeBatchMeta({ gasLimitI }));
667
- }
668
- const extraPayload = { batchMeta };
669
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
670
- extraPayload.customGasChainDetails = args.customGasChainDetails;
671
- }
672
- const extraJSON = JSON.stringify(extraPayload);
673
- const firstSigText = batchMeta[0].signatureText;
674
- const bodyForSign = {
675
- keyList,
676
- pubKey: ph,
677
- msgHash: messageHashes[0],
678
- msgRaw: firstDataNo0x,
679
- messageHashes,
680
- messageRawBatch,
681
- destinationChainID: "1",
682
- destinationAddress: steps[0].to,
683
- extraJSON,
684
- signatureText: firstSigText,
685
- purpose: (() => {
686
- const t = args.purposeText.trim();
687
- return (t ? `${t}
688
-
689
- ` : "") + args.purposeSuffix;
690
- })(),
691
- ...firstTxFeePayload,
692
- proposalTxParams: proposalTxParamsBatch
693
- };
694
- if (clientId) bodyForSign.clientId = clientId;
695
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
696
585
  }
697
586
  async function buildEvmMultisignBodyEthenaSusdeRedeemToUsde(args) {
698
587
  if (args.chainId !== 1) {
@@ -917,6 +806,103 @@ function resolveEthenaBatchStepGasFromSignRequest(detail, batchIndex) {
917
806
  }
918
807
  return null;
919
808
  }
809
+ var ETHENA_MINTING_V2_MAINNET = "0xe3490297a08d6fC8Da46Edb7B6142E4F461b62D3";
810
+ var ETHENA_MINTING_READ_ABI = viem.parseAbi([
811
+ "function isSupportedAsset(address asset) view returns (bool)"
812
+ ]);
813
+ var cachedClient = null;
814
+ function publicClientForMainnetRpc(rpcUrl) {
815
+ if (cachedClient && cachedClient.rpc === rpcUrl) return cachedClient.client;
816
+ const c = viem.createPublicClient({ chain: chains.mainnet, transport: viem.http(rpcUrl.trim(), { batch: { batchSize: 64 } }) });
817
+ cachedClient = { rpc: rpcUrl, client: c };
818
+ return c;
819
+ }
820
+ async function fetchEthenaMintingIsSupportedMap(rpcUrl, contractAddresses) {
821
+ const out = /* @__PURE__ */ new Map();
822
+ if (!rpcUrl.trim()) return out;
823
+ const unique = [];
824
+ const seen = /* @__PURE__ */ new Set();
825
+ for (const raw of contractAddresses) {
826
+ try {
827
+ const a = viem.getAddress(raw);
828
+ const k = a.toLowerCase();
829
+ if (seen.has(k)) continue;
830
+ seen.add(k);
831
+ unique.push(a);
832
+ } catch {
833
+ }
834
+ }
835
+ if (unique.length === 0) return out;
836
+ const client = publicClientForMainnetRpc(rpcUrl);
837
+ const res = await client.multicall({
838
+ contracts: unique.map((asset) => ({
839
+ address: ETHENA_MINTING_V2_MAINNET,
840
+ abi: ETHENA_MINTING_READ_ABI,
841
+ functionName: "isSupportedAsset",
842
+ args: [asset]
843
+ })),
844
+ allowFailure: true
845
+ });
846
+ for (let i = 0; i < unique.length; i++) {
847
+ const row = res[i];
848
+ if (row.status !== "success") continue;
849
+ if (row.result === true) out.set(unique[i], true);
850
+ }
851
+ return out;
852
+ }
853
+ var STAKED_USDE_V2_VIEWS_ABI = viem.parseAbi([
854
+ "function cooldownDuration() view returns (uint24)",
855
+ "function cooldowns(address account) view returns (uint104 cooldownEnd, uint152 underlyingAmount)"
856
+ ]);
857
+ function publicClientForMainnet(rpcUrl) {
858
+ return viem.createPublicClient({
859
+ chain: { id: 1, name: "Ethereum", nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, rpcUrls: { default: { http: [rpcUrl.trim()] } } },
860
+ transport: viem.http(rpcUrl.trim())
861
+ });
862
+ }
863
+ async function readEthenaStakedUsdeCooldownDuration(args) {
864
+ const v = viem.getAddress(args.vault ?? SUSDE_ETHEREUM_MAINNET);
865
+ const c = publicClientForMainnet(args.rpcUrl);
866
+ const d = await c.readContract({
867
+ address: v,
868
+ abi: STAKED_USDE_V2_VIEWS_ABI,
869
+ functionName: "cooldownDuration"
870
+ });
871
+ return BigInt(d);
872
+ }
873
+ async function readEthenaStakedUsdeUserCooldown(args) {
874
+ const v = viem.getAddress(args.vault ?? SUSDE_ETHEREUM_MAINNET);
875
+ const c = publicClientForMainnet(args.rpcUrl);
876
+ const r = await c.readContract({
877
+ address: v,
878
+ abi: STAKED_USDE_V2_VIEWS_ABI,
879
+ functionName: "cooldowns",
880
+ args: [viem.getAddress(args.account)]
881
+ });
882
+ const tuple = r;
883
+ return {
884
+ cooldownEnd: BigInt(tuple[0]),
885
+ underlyingAmount: BigInt(tuple[1])
886
+ };
887
+ }
888
+ async function readEthenaMainnetBlockTimestamp(rpcUrl) {
889
+ const c = publicClientForMainnet(rpcUrl);
890
+ const b = await c.getBlock({ blockTag: "latest" });
891
+ return b.timestamp;
892
+ }
893
+ function formatEthenaCooldownDurationLabel(totalSec) {
894
+ if (totalSec <= 0n) return "0 seconds";
895
+ const d = totalSec / 86400n;
896
+ const h = totalSec % 86400n / 3600n;
897
+ const m = totalSec % 3600n / 60n;
898
+ const s = totalSec % 60n;
899
+ const parts = [];
900
+ if (d > 0n) parts.push(`${d} day${d === 1n ? "" : "s"}`);
901
+ if (h > 0n) parts.push(`${h} hour${h === 1n ? "" : "s"}`);
902
+ if (m > 0n) parts.push(`${m} min`);
903
+ if (s > 0n) parts.push(`${s} sec`);
904
+ return parts.join(", ");
905
+ }
920
906
 
921
907
  // src/protocols/evm/ethena/index.ts
922
908
  var ETHENA_PROTOCOL_ID = "ethena";
@@ -942,6 +928,7 @@ registerProtocolModule(ethenaProtocolModule);
942
928
 
943
929
  exports.ETHENA_KEY_ADDRESSES_DOC = ETHENA_KEY_ADDRESSES_DOC;
944
930
  exports.ETHENA_LABS_GITHUB = ETHENA_LABS_GITHUB;
931
+ exports.ETHENA_MINTING_V2_MAINNET = ETHENA_MINTING_V2_MAINNET;
945
932
  exports.ETHENA_PROTOCOL_ID = ETHENA_PROTOCOL_ID;
946
933
  exports.ETHENA_SUSDE_DEPOSIT_GET_SIG_GAS_FALLBACK = ETHENA_SUSDE_DEPOSIT_GET_SIG_GAS_FALLBACK;
947
934
  exports.SUSDE_ETHEREUM_MAINNET = SUSDE_ETHEREUM_MAINNET;
@@ -953,11 +940,16 @@ exports.buildEvmMultisignBodyEthenaSusdeRedeemToUsde = buildEvmMultisignBodyEthe
953
940
  exports.buildEvmMultisignBodyEthenaUnstakeClaim = buildEvmMultisignBodyEthenaUnstakeClaim;
954
941
  exports.buildEvmMultisignBodyEthenaUsdeStakeToSusde = buildEvmMultisignBodyEthenaUsdeStakeToSusde;
955
942
  exports.ethenaProtocolModule = ethenaProtocolModule;
943
+ exports.fetchEthenaMintingIsSupportedMap = fetchEthenaMintingIsSupportedMap;
944
+ exports.formatEthenaCooldownDurationLabel = formatEthenaCooldownDurationLabel;
956
945
  exports.isEthenaSusdeDepositBatchStep = isEthenaSusdeDepositBatchStep;
957
946
  exports.isEthenaUsdeOnAssetsChain = isEthenaUsdeOnAssetsChain;
958
947
  exports.isEvmChainInEthenaUsdeList = isEvmChainInEthenaUsdeList;
959
948
  exports.listEthenaUsdeEvmNetworkRows = listEthenaUsdeEvmNetworkRows;
949
+ exports.readEthenaMainnetBlockTimestamp = readEthenaMainnetBlockTimestamp;
960
950
  exports.readEthenaStakePreviewShares = readEthenaStakePreviewShares;
951
+ exports.readEthenaStakedUsdeCooldownDuration = readEthenaStakedUsdeCooldownDuration;
952
+ exports.readEthenaStakedUsdeUserCooldown = readEthenaStakedUsdeUserCooldown;
961
953
  exports.readEthenaUnstakePreviewUsde = readEthenaUnstakePreviewUsde;
962
954
  exports.resolveEthenaBatchStepGasFromSignRequest = resolveEthenaBatchStepGasFromSignRequest;
963
955
  exports.usdeTokenAddressOnEvmChain = usdeTokenAddressOnEvmChain;