@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,4 +1,5 @@
1
- import { getAddress, parseAbi, zeroAddress, parseUnits, encodeFunctionData, defineChain, createPublicClient, http, parseGwei, serializeTransaction, keccak256, formatUnits } from 'viem';
1
+ import { getAddress, parseAbi, zeroAddress, parseUnits, encodeFunctionData, defineChain, createPublicClient, http, decodeEventLog, parseGwei, serializeTransaction, keccak256 } from 'viem';
2
+ import { fetchChainFeeParams, gasLimitFromEstimateAndChainConfig, gweiToDecimalString, proposalTxParamsToFeeSnapshot, alignEip1559FeesWithLatestBase, getClientIdFromKeyGenResult } from '@continuumdao/continuum-node-sdk';
2
3
 
3
4
  // src/core/registry.ts
4
5
  var modules = [];
@@ -23,87 +24,234 @@ function isEthereumMainnetChainId(chainIdStr) {
23
24
  return String(chainIdStr ?? "").trim() === String(LIDO_ETHEREUM_MAINNET_CHAIN_ID);
24
25
  }
25
26
 
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;
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;
34
35
  }
35
36
 
36
- // src/chains/evm/txParams.ts
37
- function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
38
- if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
39
- return estimatedGas;
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;
40
79
  }
41
- const cfg = BigInt(Math.floor(chainGasLimit));
42
- return cfg > estimatedGas ? cfg : estimatedGas;
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) };
43
86
  }
44
- async function fetchChainFeeParams(rpcUrl, chainId) {
45
- const url = rpcUrl.trim();
46
- if (!url) return { isEip1559: false };
47
- const chainIdNum = chainId;
48
- if (Number.isNaN(chainIdNum)) return { isEip1559: false };
49
- const chain = defineChain({
50
- id: chainIdNum,
51
- name: "Discovery",
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",
52
111
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
53
- rpcUrls: { default: { http: [url] } }
112
+ rpcUrls: { default: { http: [rpcUrl] } }
54
113
  });
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 };
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
+ }
69
141
  }
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 {
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;
76
149
  }
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 };
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;
91
235
  }
92
236
  }
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");
237
+ const extraJSON = {};
238
+ if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
239
+ extraJSON.customGasChainDetails = customGasChainDetails;
99
240
  }
100
- if (maxF < maxP) {
101
- maxF = baseWei > 0n ? baseWei + maxP + parseGwei("0.001") : maxP * 2n;
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();
102
253
  }
103
- return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
104
- }
105
- function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
106
- return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
254
+ return result;
107
255
  }
108
256
 
109
257
  // src/protocols/evm/lido/multisign.ts
@@ -132,13 +280,6 @@ var wstethAbi = parseAbi([
132
280
  "function wrap(uint256 stETHAmount) returns (uint256)",
133
281
  "function unwrap(uint256 wstETHAmount) returns (uint256)"
134
282
  ]);
135
- function gweiToDecimalString(n) {
136
- if (!Number.isFinite(n)) return "0";
137
- if (n === 0) return "0";
138
- const s = String(n);
139
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
140
- return s;
141
- }
142
283
  function parseExtraJsonObject(detail) {
143
284
  const raw = detail?.ExtraJSON ?? detail?.extraJSON;
144
285
  if (raw == null) return null;
@@ -226,167 +367,31 @@ function resolveLidoBatchStepGasFromSignRequest(detail, batchIndex) {
226
367
  return null;
227
368
  }
228
369
  async function finalizeLidoMultipart(steps, args) {
229
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
230
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
231
- const keyList = args.keyGen.keylist ?? [];
232
- const clientId = firstClientIdFromKeyGen(args.keyGen);
233
- const ch = defineChain({
234
- id: LIDO_ETHEREUM_MAINNET_CHAIN_ID,
235
- name: "Ethereum",
236
- nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
237
- rpcUrls: { default: { http: [args.rpcUrl.trim()] } }
238
- });
239
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl.trim()) });
240
- const feeParams = await fetchChainFeeParams(args.rpcUrl.trim(), LIDO_ETHEREUM_MAINNET_CHAIN_ID);
241
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
242
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
243
- const useCustomGas = args.useCustomGas;
244
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
245
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
246
- const executor = getAddress(args.executorAddress);
247
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
248
- const messageHashes = [];
249
- const messageRawBatch = [];
250
- const proposalTxParamsBatch = [];
251
- let firstTxFeePayload = {};
252
- let firstDataNo0x = "";
253
- const batchMeta = [];
254
- for (let i = 0; i < steps.length; i++) {
255
- const s = steps[i];
256
- const v = getAddress(s.to);
257
- const data = s.data;
258
- const currentNonce = baseNonce + i;
259
- let estimatedGas;
260
- try {
261
- estimatedGas = await publicClient.estimateGas({
262
- to: v,
263
- data,
264
- value: s.value,
265
- account: executor
266
- });
267
- } catch {
268
- estimatedGas = s.fallbackGas;
269
- }
270
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
271
- if (legacy) {
272
- let gasPriceWei = await publicClient.getGasPrice();
273
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
274
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
275
- }
276
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
277
- const configured = parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
278
- if (configured > gasPriceWei) gasPriceWei = configured;
279
- }
280
- const ser = serializeTransaction({
281
- type: "legacy",
282
- to: v,
283
- data,
284
- value: s.value,
285
- gas: gasLimitI,
286
- gasPrice: gasPriceWei,
287
- nonce: currentNonce,
288
- chainId: LIDO_ETHEREUM_MAINNET_CHAIN_ID
289
- });
290
- const h = keccak256(ser);
291
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
292
- messageRawBatch.push(ser);
293
- proposalTxParamsBatch.push({
294
- nonce: currentNonce,
295
- gasLimit: gasLimitI.toString(),
296
- txType: "legacy",
297
- gasPrice: gasPriceWei.toString()
298
- });
299
- if (i === 0) {
300
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
301
- firstDataNo0x = data.startsWith("0x") ? data.slice(2) : data;
302
- }
303
- } else {
304
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
305
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
306
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
307
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
308
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
309
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
310
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
311
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
312
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
313
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
314
- let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
315
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
316
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
317
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
318
- }
319
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
320
- maxFeePerGas,
321
- maxPriorityFeePerGas,
322
- latestBaseFeeWei
323
- ));
324
- const ser = serializeTransaction({
325
- type: "eip1559",
326
- to: v,
327
- data,
328
- value: s.value,
329
- gas: gasLimitI,
330
- maxFeePerGas,
331
- maxPriorityFeePerGas,
332
- nonce: currentNonce,
333
- chainId: LIDO_ETHEREUM_MAINNET_CHAIN_ID
334
- });
335
- const h = keccak256(ser);
336
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
337
- messageRawBatch.push(ser);
338
- proposalTxParamsBatch.push({
339
- nonce: currentNonce,
340
- gasLimit: gasLimitI.toString(),
341
- txType: "eip1559",
342
- maxFeePerGas: maxFeePerGas.toString(),
343
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
344
- });
345
- if (i === 0) {
346
- firstTxFeePayload = {
347
- txNonce: currentNonce,
348
- txGasLimit: gasLimitI.toString(),
349
- txMaxFeePerGas: maxFeePerGas.toString(),
350
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
351
- };
352
- firstDataNo0x = data.startsWith("0x") ? data.slice(2) : data;
353
- }
354
- }
355
- batchMeta.push(s.buildBatchMeta({ gasLimit: gasLimitI }));
356
- }
357
- const extraPayload = { batchMeta };
358
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
359
- extraPayload.customGasChainDetails = args.customGasChainDetails;
360
- }
361
- const extraJSON = JSON.stringify(extraPayload);
362
- const firstSigText = batchMeta[0].signatureText;
363
- const firstValue = steps[0].value;
364
- const bodyForSign = {
365
- keyList,
366
- pubKey: ph,
367
- msgHash: messageHashes[0],
368
- msgRaw: firstDataNo0x,
369
- messageHashes,
370
- messageRawBatch,
371
- destinationChainID: String(LIDO_ETHEREUM_MAINNET_CHAIN_ID),
370
+ const evmSteps = steps.map((s) => ({
371
+ to: s.to,
372
+ data: s.data,
373
+ value: s.value,
374
+ fallbackGas: s.fallbackGas
375
+ }));
376
+ const firstDataNo0x = evmSteps[0].data.startsWith("0x") ? evmSteps[0].data.slice(2) : evmSteps[0].data;
377
+ return buildEvmMultisignBatch({
378
+ context: {
379
+ chainCategory: "evm",
380
+ keyGen: args.keyGen,
381
+ purposeText: args.purposeText,
382
+ chainId: LIDO_ETHEREUM_MAINNET_CHAIN_ID,
383
+ rpcUrl: args.rpcUrl.trim(),
384
+ executorAddress: args.executorAddress,
385
+ chainDetail: args.chainDetail,
386
+ useCustomGas: args.useCustomGas,
387
+ customGasChainDetails: args.customGasChainDetails
388
+ },
389
+ steps: evmSteps,
390
+ purposeSuffix: args.purposeSuffix,
391
+ firstMsgRawNo0x: firstDataNo0x,
372
392
  destinationAddress: steps[0].to,
373
- extraJSON,
374
- signatureText: firstSigText,
375
- purpose: (() => {
376
- const t = args.purposeText.trim();
377
- const suffix = args.purposeSuffix.trim();
378
- return (t ? `${t}
379
-
380
- ` : "") + suffix;
381
- })(),
382
- ...firstTxFeePayload,
383
- proposalTxParams: proposalTxParamsBatch
384
- };
385
- if (firstValue > 0n) {
386
- bodyForSign.value = firstValue.toString();
387
- }
388
- if (clientId) bodyForSign.clientId = clientId;
389
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
393
+ buildBatchMeta: ({ index, gasLimit }) => steps[index].buildBatchMeta({ gasLimit })
394
+ });
390
395
  }
391
396
  async function buildEvmMultisignBodyLidoSubmit(args) {
392
397
  if (args.chainId !== LIDO_ETHEREUM_MAINNET_CHAIN_ID) {
@@ -782,6 +787,111 @@ function isLidoEvmSignRequestForEstimateWorkaround(detail, batchIndex) {
782
787
  return isLidoBatchStepEvmSignRequest(detail, batchIndex ?? 0);
783
788
  }
784
789
 
790
+ // src/protocols/evm/lido/aprApi.ts
791
+ async function fetchLidoStethAprLastPct() {
792
+ try {
793
+ const res = await fetch("https://eth-api.lido.fi/v1/protocol/steth/apr/last", { cache: "no-store" });
794
+ if (!res.ok) return null;
795
+ const j = await res.json();
796
+ const apr = j?.data?.apr;
797
+ const n = typeof apr === "number" ? apr : typeof apr === "string" ? parseFloat(apr) : NaN;
798
+ return Number.isFinite(n) ? n : null;
799
+ } catch {
800
+ return null;
801
+ }
802
+ }
803
+
804
+ // src/protocols/evm/lido/withdrawalTimeApi.ts
805
+ function formatIsoUtc(i) {
806
+ try {
807
+ const d = new Date(i);
808
+ if (Number.isNaN(d.getTime())) return i;
809
+ return d.toUTCString();
810
+ } catch {
811
+ return i;
812
+ }
813
+ }
814
+ async function fetchLidoWithdrawalFinalizationEta(args) {
815
+ const raw = args.amountHuman.trim().replace(/,/g, "");
816
+ if (!raw || raw === "." || raw === "0") {
817
+ return { ok: false, error: "" };
818
+ }
819
+ const q = encodeURIComponent(raw);
820
+ try {
821
+ const res = await fetch(`https://wq-api.lido.fi/v2/request-time/calculate?amount=${q}`, {
822
+ cache: "no-store"
823
+ });
824
+ if (!res.ok) {
825
+ return { ok: false, error: `Withdrawal ETA API (${res.status})` };
826
+ }
827
+ const j = await res.json();
828
+ const at = j?.requestInfo?.finalizationAt;
829
+ const s = typeof at === "string" && at.trim() ? at.trim() : null;
830
+ if (!s || j.status !== "calculated") {
831
+ return { ok: false, error: "" };
832
+ }
833
+ return {
834
+ ok: true,
835
+ finalizationAtIso: s,
836
+ displayLabel: formatIsoUtc(s)
837
+ };
838
+ } catch (e) {
839
+ return {
840
+ ok: false,
841
+ error: e instanceof Error ? e.message : "ETA unavailable"
842
+ };
843
+ }
844
+ }
845
+ var erc721TransferAbi = parseAbi([
846
+ "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
847
+ ]);
848
+ var requestWithdrawalsAbiSlice = parseAbi(["function requestWithdrawals(uint256[] amounts, address owner)"]);
849
+ var _requestWithdrawalsSelectorPrefix = null;
850
+ function requestWithdrawalsCalldataPrefix() {
851
+ if (_requestWithdrawalsSelectorPrefix === null) {
852
+ _requestWithdrawalsSelectorPrefix = encodeFunctionData({
853
+ abi: requestWithdrawalsAbiSlice,
854
+ functionName: "requestWithdrawals",
855
+ args: [[0n], getAddress(zeroAddress)]
856
+ }).slice(0, 10);
857
+ }
858
+ return _requestWithdrawalsSelectorPrefix;
859
+ }
860
+ function calldataStartsWithRequestWithdrawals(dataHex) {
861
+ const raw = typeof dataHex === "string" ? dataHex.trim() : "";
862
+ if (!raw.startsWith("0x") && !raw.startsWith("0X")) return false;
863
+ const head = (raw.startsWith("0x") || raw.startsWith("0X") ? raw.toLowerCase() : `0x${raw}`).toLowerCase();
864
+ return head.startsWith(requestWithdrawalsCalldataPrefix().toLowerCase());
865
+ }
866
+ function withdrawalQueueMintedTokenIdsFromReceipt(receipt, withdrawalQueue = LIDO_WITHDRAWAL_QUEUE_MAINNET) {
867
+ let wqNorm;
868
+ try {
869
+ wqNorm = getAddress(withdrawalQueue).toLowerCase();
870
+ } catch {
871
+ return [];
872
+ }
873
+ const out = [];
874
+ const zeroFrom = zeroAddress.toLowerCase();
875
+ for (const log of receipt.logs) {
876
+ if ((log.address ?? "").toLowerCase() !== wqNorm) continue;
877
+ try {
878
+ const decoded = decodeEventLog({
879
+ abi: erc721TransferAbi,
880
+ data: log.data,
881
+ topics: log.topics
882
+ });
883
+ const args = decoded && typeof decoded === "object" && "args" in decoded ? decoded.args : void 0;
884
+ const from = args?.from?.toLowerCase();
885
+ if (!from || from !== zeroFrom) continue;
886
+ if (typeof args?.tokenId !== "bigint") continue;
887
+ out.push(args.tokenId);
888
+ } catch {
889
+ }
890
+ }
891
+ out.sort((a, b) => a === b ? 0 : a < b ? -1 : 1);
892
+ return out;
893
+ }
894
+
785
895
  // src/protocols/evm/lido/index.ts
786
896
  var LIDO_PROTOCOL_ID = "lido";
787
897
  var lidoProtocolModule = {
@@ -804,6 +914,6 @@ var lidoProtocolModule = {
804
914
  };
805
915
  registerProtocolModule(lidoProtocolModule);
806
916
 
807
- export { LIDO_CLAIM_WITHDRAWAL_FALLBACK_GAS_UNITS, LIDO_ETHEREUM_MAINNET_CHAIN_ID, LIDO_EVM_TYPES, LIDO_PROTOCOL_ID, LIDO_REQUEST_WITHDRAWALS_FALLBACK_GAS_UNITS, LIDO_STETH_CONTRACT_MAINNET, LIDO_SUBMIT_FALLBACK_GAS_UNITS, LIDO_SUBMIT_REFERRAL_MAINNET, LIDO_WITHDRAWAL_QUEUE_MAINNET, LIDO_WSTETH_CONTRACT_MAINNET, LIDO_WSTETH_UNWRAP_FALLBACK_GAS_UNITS, LIDO_WSTETH_WRAP_FALLBACK_GAS_UNITS, MIN_LIDO_EXECUTE_GAS, buildEvmMultisignBodyLidoClaimWithdrawal, buildEvmMultisignBodyLidoRequestWithdrawals, buildEvmMultisignBodyLidoSubmit, buildEvmMultisignBodyLidoUnwrapWstEth, buildEvmMultisignBodyLidoWrapStEth, isEthereumMainnetChainId, isLidoBatchStepEvmSignRequest, isLidoEvmSignRequestForEstimateWorkaround, lidoProtocolModule, lidoRequestWithdrawalsBatchIndices, resolveLidoBatchStepGasFromSignRequest };
917
+ export { LIDO_CLAIM_WITHDRAWAL_FALLBACK_GAS_UNITS, LIDO_ETHEREUM_MAINNET_CHAIN_ID, LIDO_EVM_TYPES, LIDO_PROTOCOL_ID, LIDO_REQUEST_WITHDRAWALS_FALLBACK_GAS_UNITS, LIDO_STETH_CONTRACT_MAINNET, LIDO_SUBMIT_FALLBACK_GAS_UNITS, LIDO_SUBMIT_REFERRAL_MAINNET, LIDO_WITHDRAWAL_QUEUE_MAINNET, LIDO_WSTETH_CONTRACT_MAINNET, LIDO_WSTETH_UNWRAP_FALLBACK_GAS_UNITS, LIDO_WSTETH_WRAP_FALLBACK_GAS_UNITS, MIN_LIDO_EXECUTE_GAS, buildEvmMultisignBodyLidoClaimWithdrawal, buildEvmMultisignBodyLidoRequestWithdrawals, buildEvmMultisignBodyLidoSubmit, buildEvmMultisignBodyLidoUnwrapWstEth, buildEvmMultisignBodyLidoWrapStEth, calldataStartsWithRequestWithdrawals, fetchLidoStethAprLastPct, fetchLidoWithdrawalFinalizationEta, isEthereumMainnetChainId, isLidoBatchStepEvmSignRequest, isLidoEvmSignRequestForEstimateWorkaround, lidoProtocolModule, lidoRequestWithdrawalsBatchIndices, requestWithdrawalsCalldataPrefix, resolveLidoBatchStepGasFromSignRequest, withdrawalQueueMintedTokenIdsFromReceipt };
808
918
  //# sourceMappingURL=index.js.map
809
919
  //# sourceMappingURL=index.js.map