@continuumdao/ctm-mpc-defi 0.2.0 → 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 (100) hide show
  1. package/README.md +20 -78
  2. package/dist/agent/catalog.cjs +511 -4
  3. package/dist/agent/catalog.cjs.map +1 -1
  4. package/dist/agent/catalog.d.ts +140 -20
  5. package/dist/agent/catalog.js +501 -6
  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 -226
  16. package/dist/chains/evm/index.cjs.map +1 -1
  17. package/dist/chains/evm/index.d.ts +14 -26
  18. package/dist/chains/evm/index.js +21 -211
  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 -1868
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.ts +7 -11
  31. package/dist/index.js +227 -1839
  32. package/dist/index.js.map +1 -1
  33. package/dist/protocols/evm/aave-v4/index.cjs +385 -662
  34. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -1
  35. package/dist/protocols/evm/aave-v4/index.d.ts +1 -2
  36. package/dist/protocols/evm/aave-v4/index.js +385 -662
  37. package/dist/protocols/evm/aave-v4/index.js.map +1 -1
  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 +290 -402
  44. package/dist/protocols/evm/ethena/index.cjs.map +1 -1
  45. package/dist/protocols/evm/ethena/index.d.ts +1 -2
  46. package/dist/protocols/evm/ethena/index.js +291 -403
  47. package/dist/protocols/evm/ethena/index.js.map +1 -1
  48. package/dist/protocols/evm/euler-v2/index.cjs +485 -1163
  49. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -1
  50. package/dist/protocols/evm/euler-v2/index.d.ts +1 -2
  51. package/dist/protocols/evm/euler-v2/index.js +486 -1164
  52. package/dist/protocols/evm/euler-v2/index.js.map +1 -1
  53. package/dist/protocols/evm/lido/index.cjs +241 -236
  54. package/dist/protocols/evm/lido/index.cjs.map +1 -1
  55. package/dist/protocols/evm/lido/index.d.ts +1 -2
  56. package/dist/protocols/evm/lido/index.js +242 -237
  57. package/dist/protocols/evm/lido/index.js.map +1 -1
  58. package/dist/protocols/evm/maple/index.cjs +310 -398
  59. package/dist/protocols/evm/maple/index.cjs.map +1 -1
  60. package/dist/protocols/evm/maple/index.d.ts +1 -2
  61. package/dist/protocols/evm/maple/index.js +311 -399
  62. package/dist/protocols/evm/maple/index.js.map +1 -1
  63. package/dist/protocols/evm/sky/index.cjs +238 -233
  64. package/dist/protocols/evm/sky/index.cjs.map +1 -1
  65. package/dist/protocols/evm/sky/index.d.ts +1 -2
  66. package/dist/protocols/evm/sky/index.js +236 -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,7 @@
1
1
  'use strict';
2
2
 
3
3
  var viem = require('viem');
4
+ var continuumNodeSdk = require('@continuumdao/continuum-node-sdk');
4
5
 
5
6
  // src/core/registry.ts
6
7
  var modules = [];
@@ -25,87 +26,234 @@ function isEthereumMainnetChainId(chainIdStr) {
25
26
  return String(chainIdStr ?? "").trim() === String(LIDO_ETHEREUM_MAINNET_CHAIN_ID);
26
27
  }
27
28
 
28
- // src/core/keygen.ts
29
- function firstClientIdFromKeyGen(data) {
30
- const map = data?.ClientKeys;
31
- if (!map || typeof map !== "object") return null;
32
- for (const v of Object.values(map)) {
33
- if (typeof v === "string" && v.trim()) return v.trim();
34
- }
35
- return null;
29
+ // src/core/purpose.ts
30
+ function mergePurposeText(purposeText, purposeSuffix) {
31
+ const t = purposeText.trim();
32
+ const suffix = (purposeSuffix ?? "").trim();
33
+ if (!suffix) return t;
34
+ return t ? `${t}
35
+
36
+ ${suffix}` : suffix;
36
37
  }
37
38
 
38
- // src/chains/evm/txParams.ts
39
- function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
40
- if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
41
- return estimatedGas;
39
+ // src/core/envelope.ts
40
+ function finalizeMultisign(input) {
41
+ const { keyGen, destinationChainID, legs } = input;
42
+ if (legs.length === 0) {
43
+ throw new Error("finalizeMultisign requires at least one leg");
42
44
  }
43
- const cfg = BigInt(Math.floor(chainGasLimit));
44
- return cfg > estimatedGas ? cfg : estimatedGas;
45
+ const ph = (keyGen.pubkeyhex ?? "").trim();
46
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
47
+ const keyList = keyGen.keylist ?? [];
48
+ const clientId = continuumNodeSdk.getClientIdFromKeyGenResult(keyGen);
49
+ const first = legs[0];
50
+ const messageHashes = legs.map((l) => l.msgHash);
51
+ const messageRawBatch = legs.map((l) => l.msgRaw);
52
+ const batchMeta = legs.map((l) => ({
53
+ destinationAddress: l.destinationAddress,
54
+ signatureText: l.signatureText,
55
+ ...l.audit
56
+ }));
57
+ const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
58
+ const extraPayload = {
59
+ batchMeta,
60
+ ...input.extraJSON ?? {}
61
+ };
62
+ const extraJSON = JSON.stringify(extraPayload);
63
+ const bodyForSign = {
64
+ keyList,
65
+ pubKey: ph,
66
+ msgHash: messageHashes[0],
67
+ msgRaw: first.msgRaw,
68
+ destinationChainID,
69
+ destinationAddress: input.destinationAddress ?? first.destinationAddress,
70
+ extraJSON,
71
+ signatureText: first.signatureText,
72
+ purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
73
+ ...first.feeSnapshot
74
+ };
75
+ if (legs.length > 1) {
76
+ bodyForSign.messageHashes = messageHashes;
77
+ bodyForSign.messageRawBatch = messageRawBatch;
78
+ }
79
+ if (proposalTxParams.length > 0) {
80
+ bodyForSign.proposalTxParams = proposalTxParams;
81
+ }
82
+ const valueWei = first.valueWei;
83
+ if (valueWei != null && valueWei > 0n) {
84
+ bodyForSign.value = valueWei.toString();
85
+ }
86
+ if (clientId) bodyForSign.clientId = clientId;
87
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
88
+ }
89
+ function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
90
+ if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
91
+ return continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
92
+ }
93
+ return (estimatedGas * 12n + 9n) / 10n;
45
94
  }
46
- async function fetchChainFeeParams(rpcUrl, chainId) {
47
- const url = rpcUrl.trim();
48
- if (!url) return { isEip1559: false };
49
- const chainIdNum = chainId;
50
- if (Number.isNaN(chainIdNum)) return { isEip1559: false };
51
- const chain = viem.defineChain({
52
- id: chainIdNum,
53
- name: "Discovery",
95
+
96
+ // src/chains/evm/buildBatch.ts
97
+ async function buildEvmMultisignBatch(args) {
98
+ const { context, steps } = args;
99
+ const {
100
+ chainId,
101
+ rpcUrl,
102
+ executorAddress,
103
+ chainDetail,
104
+ useCustomGas,
105
+ customGasChainDetails,
106
+ keyGen,
107
+ purposeText
108
+ } = context;
109
+ if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
110
+ const ch = viem.defineChain({
111
+ id: chainId,
112
+ name: "Destination",
54
113
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
55
- rpcUrls: { default: { http: [url] } }
114
+ rpcUrls: { default: { http: [rpcUrl] } }
56
115
  });
57
- const publicClient = viem.createPublicClient({
58
- chain,
59
- transport: viem.http(url)
60
- });
61
- const getGasPriceGwei = async () => {
62
- const gasPriceWei = await publicClient.getGasPrice();
63
- return parseFloat(viem.formatUnits(gasPriceWei, 9));
64
- };
65
- try {
66
- const block = await publicClient.getBlock({ blockTag: "latest" });
67
- const baseFeePerGas = block?.baseFeePerGas;
68
- if (baseFeePerGas == null || baseFeePerGas === void 0) {
69
- const gasPriceGwei2 = await getGasPriceGwei();
70
- return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
116
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(rpcUrl) });
117
+ const feeParams = await continuumNodeSdk.fetchChainFeeParams(rpcUrl, chainId);
118
+ const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
119
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
120
+ const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
121
+ const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
122
+ const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
123
+ const executor = viem.getAddress(executorAddress);
124
+ const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
125
+ const legs = [];
126
+ for (let i = 0; i < steps.length; i++) {
127
+ const step = steps[i];
128
+ const currentNonce = baseNonce + i;
129
+ let estimatedGas;
130
+ if (args.estimateGasForStep) {
131
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient, executor });
132
+ } else {
133
+ try {
134
+ estimatedGas = await publicClient.estimateGas({
135
+ to: step.to,
136
+ data: step.data,
137
+ value: step.value,
138
+ account: executor
139
+ });
140
+ } catch {
141
+ estimatedGas = step.fallbackGas ?? 100000n;
142
+ }
71
143
  }
72
- const baseFeeGwei = parseFloat(viem.formatUnits(baseFeePerGas, 9));
73
- let priorityFeeGwei;
74
- try {
75
- const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
76
- priorityFeeGwei = parseFloat(viem.formatUnits(priorityWei, 9));
77
- } catch {
144
+ let gasLimitI;
145
+ if (args.resolveGasLimit) {
146
+ gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient });
147
+ } else if (step.routerSwap) {
148
+ gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
149
+ } else {
150
+ gasLimitI = useCustomGas ? continuumNodeSdk.gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
78
151
  }
79
- const gasPriceGwei = await getGasPriceGwei();
80
- return {
81
- isEip1559: true,
82
- baseFeeGwei,
83
- priorityFeeGwei,
84
- gasPriceGwei
85
- };
86
- } catch {
87
- try {
88
- const gasPriceWei = await publicClient.getGasPrice();
89
- const gasPriceGwei = parseFloat(viem.formatUnits(gasPriceWei, 9));
90
- return { isEip1559: false, gasPriceGwei };
91
- } catch {
92
- return { isEip1559: false };
152
+ let proposalTxParams;
153
+ let feeSnapshot;
154
+ let serialized;
155
+ if (legacy) {
156
+ let gasPriceWei = await publicClient.getGasPrice();
157
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
158
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
159
+ }
160
+ if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
161
+ const configured = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(Number(chainDetail.gasPrice)));
162
+ if (configured > gasPriceWei) gasPriceWei = configured;
163
+ }
164
+ serialized = viem.serializeTransaction({
165
+ type: "legacy",
166
+ to: step.to,
167
+ data: step.data,
168
+ value: step.value,
169
+ gas: gasLimitI,
170
+ gasPrice: gasPriceWei,
171
+ nonce: currentNonce,
172
+ chainId
173
+ });
174
+ proposalTxParams = {
175
+ nonce: currentNonce,
176
+ gasLimit: gasLimitI.toString(),
177
+ txType: "legacy",
178
+ gasPrice: gasPriceWei.toString()
179
+ };
180
+ feeSnapshot = continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams);
181
+ } else {
182
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
183
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
184
+ const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
185
+ const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
186
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
187
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
188
+ const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
189
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
190
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
191
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(continuumNodeSdk.gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
192
+ let maxFeePerGas = viem.parseGwei(continuumNodeSdk.gweiToDecimalString(maxFeePerGasGwei));
193
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
194
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
195
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
196
+ }
197
+ ({ maxFeePerGas, maxPriorityFeePerGas } = continuumNodeSdk.alignEip1559FeesWithLatestBase(
198
+ maxFeePerGas,
199
+ maxPriorityFeePerGas,
200
+ latestBaseFeeWei
201
+ ));
202
+ serialized = viem.serializeTransaction({
203
+ type: "eip1559",
204
+ to: step.to,
205
+ data: step.data,
206
+ value: step.value,
207
+ gas: gasLimitI,
208
+ maxFeePerGas,
209
+ maxPriorityFeePerGas,
210
+ nonce: currentNonce,
211
+ chainId
212
+ });
213
+ proposalTxParams = {
214
+ nonce: currentNonce,
215
+ gasLimit: gasLimitI.toString(),
216
+ txType: "eip1559",
217
+ maxFeePerGas: maxFeePerGas.toString(),
218
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
219
+ };
220
+ feeSnapshot = i === 0 ? continuumNodeSdk.proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
221
+ }
222
+ const h = viem.keccak256(serialized);
223
+ const msgHash = h.startsWith("0x") ? h.slice(2) : h;
224
+ const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
225
+ legs.push({
226
+ msgHash,
227
+ msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
228
+ destinationAddress: step.to,
229
+ signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
230
+ audit: batchMetaExtra,
231
+ feeSnapshot: i === 0 ? feeSnapshot : {},
232
+ proposalTxParams,
233
+ valueWei: i === 0 ? step.value : void 0
234
+ });
235
+ if (i === 0 && args.firstMsgRawNo0x != null) {
236
+ legs[0].msgRaw = args.firstMsgRawNo0x;
93
237
  }
94
238
  }
95
- }
96
- function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
97
- let maxP = maxPriorityFeePerGas;
98
- let maxF = maxFeePerGas;
99
- if (baseWei > 0n && maxF < baseWei + maxP) {
100
- maxF = baseWei + maxP + viem.parseGwei("0.001");
239
+ const extraJSON = {};
240
+ if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
241
+ extraJSON.customGasChainDetails = customGasChainDetails;
101
242
  }
102
- if (maxF < maxP) {
103
- maxF = baseWei > 0n ? baseWei + maxP + viem.parseGwei("0.001") : maxP * 2n;
243
+ const result = finalizeMultisign({
244
+ keyGen,
245
+ purposeText,
246
+ purposeSuffix: args.purposeSuffix,
247
+ destinationChainID: String(chainId),
248
+ destinationAddress: args.destinationAddress ?? steps[0].to,
249
+ legs,
250
+ extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0
251
+ });
252
+ const pv = args.payableValueWei;
253
+ if (pv != null && pv > 0n) {
254
+ result.bodyForSign.value = pv.toString();
104
255
  }
105
- return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
106
- }
107
- function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
108
- return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
256
+ return result;
109
257
  }
110
258
 
111
259
  // src/protocols/evm/lido/multisign.ts
@@ -134,13 +282,6 @@ var wstethAbi = viem.parseAbi([
134
282
  "function wrap(uint256 stETHAmount) returns (uint256)",
135
283
  "function unwrap(uint256 wstETHAmount) returns (uint256)"
136
284
  ]);
137
- function gweiToDecimalString(n) {
138
- if (!Number.isFinite(n)) return "0";
139
- if (n === 0) return "0";
140
- const s = String(n);
141
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
142
- return s;
143
- }
144
285
  function parseExtraJsonObject(detail) {
145
286
  const raw = detail?.ExtraJSON ?? detail?.extraJSON;
146
287
  if (raw == null) return null;
@@ -228,167 +369,31 @@ function resolveLidoBatchStepGasFromSignRequest(detail, batchIndex) {
228
369
  return null;
229
370
  }
230
371
  async function finalizeLidoMultipart(steps, args) {
231
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
232
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
233
- const keyList = args.keyGen.keylist ?? [];
234
- const clientId = firstClientIdFromKeyGen(args.keyGen);
235
- const ch = viem.defineChain({
236
- id: LIDO_ETHEREUM_MAINNET_CHAIN_ID,
237
- name: "Ethereum",
238
- nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
239
- rpcUrls: { default: { http: [args.rpcUrl.trim()] } }
240
- });
241
- const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl.trim()) });
242
- const feeParams = await fetchChainFeeParams(args.rpcUrl.trim(), LIDO_ETHEREUM_MAINNET_CHAIN_ID);
243
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
244
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
245
- const useCustomGas = args.useCustomGas;
246
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
247
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
248
- const executor = viem.getAddress(args.executorAddress);
249
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
250
- const messageHashes = [];
251
- const messageRawBatch = [];
252
- const proposalTxParamsBatch = [];
253
- let firstTxFeePayload = {};
254
- let firstDataNo0x = "";
255
- const batchMeta = [];
256
- for (let i = 0; i < steps.length; i++) {
257
- const s = steps[i];
258
- const v = viem.getAddress(s.to);
259
- const data = s.data;
260
- const currentNonce = baseNonce + i;
261
- let estimatedGas;
262
- try {
263
- estimatedGas = await publicClient.estimateGas({
264
- to: v,
265
- data,
266
- value: s.value,
267
- account: executor
268
- });
269
- } catch {
270
- estimatedGas = s.fallbackGas;
271
- }
272
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
273
- if (legacy) {
274
- let gasPriceWei = await publicClient.getGasPrice();
275
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
276
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
277
- }
278
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
279
- const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
280
- if (configured > gasPriceWei) gasPriceWei = configured;
281
- }
282
- const ser = viem.serializeTransaction({
283
- type: "legacy",
284
- to: v,
285
- data,
286
- value: s.value,
287
- gas: gasLimitI,
288
- gasPrice: gasPriceWei,
289
- nonce: currentNonce,
290
- chainId: LIDO_ETHEREUM_MAINNET_CHAIN_ID
291
- });
292
- const h = viem.keccak256(ser);
293
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
294
- messageRawBatch.push(ser);
295
- proposalTxParamsBatch.push({
296
- nonce: currentNonce,
297
- gasLimit: gasLimitI.toString(),
298
- txType: "legacy",
299
- gasPrice: gasPriceWei.toString()
300
- });
301
- if (i === 0) {
302
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
303
- firstDataNo0x = data.startsWith("0x") ? data.slice(2) : data;
304
- }
305
- } else {
306
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
307
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
308
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
309
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
310
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
311
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
312
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
313
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
314
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
315
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
316
- let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
317
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
318
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
319
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
320
- }
321
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
322
- maxFeePerGas,
323
- maxPriorityFeePerGas,
324
- latestBaseFeeWei
325
- ));
326
- const ser = viem.serializeTransaction({
327
- type: "eip1559",
328
- to: v,
329
- data,
330
- value: s.value,
331
- gas: gasLimitI,
332
- maxFeePerGas,
333
- maxPriorityFeePerGas,
334
- nonce: currentNonce,
335
- chainId: LIDO_ETHEREUM_MAINNET_CHAIN_ID
336
- });
337
- const h = viem.keccak256(ser);
338
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
339
- messageRawBatch.push(ser);
340
- proposalTxParamsBatch.push({
341
- nonce: currentNonce,
342
- gasLimit: gasLimitI.toString(),
343
- txType: "eip1559",
344
- maxFeePerGas: maxFeePerGas.toString(),
345
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
346
- });
347
- if (i === 0) {
348
- firstTxFeePayload = {
349
- txNonce: currentNonce,
350
- txGasLimit: gasLimitI.toString(),
351
- txMaxFeePerGas: maxFeePerGas.toString(),
352
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
353
- };
354
- firstDataNo0x = data.startsWith("0x") ? data.slice(2) : data;
355
- }
356
- }
357
- batchMeta.push(s.buildBatchMeta({ gasLimit: gasLimitI }));
358
- }
359
- const extraPayload = { batchMeta };
360
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
361
- extraPayload.customGasChainDetails = args.customGasChainDetails;
362
- }
363
- const extraJSON = JSON.stringify(extraPayload);
364
- const firstSigText = batchMeta[0].signatureText;
365
- const firstValue = steps[0].value;
366
- const bodyForSign = {
367
- keyList,
368
- pubKey: ph,
369
- msgHash: messageHashes[0],
370
- msgRaw: firstDataNo0x,
371
- messageHashes,
372
- messageRawBatch,
373
- destinationChainID: String(LIDO_ETHEREUM_MAINNET_CHAIN_ID),
372
+ const evmSteps = steps.map((s) => ({
373
+ to: s.to,
374
+ data: s.data,
375
+ value: s.value,
376
+ fallbackGas: s.fallbackGas
377
+ }));
378
+ const firstDataNo0x = evmSteps[0].data.startsWith("0x") ? evmSteps[0].data.slice(2) : evmSteps[0].data;
379
+ return buildEvmMultisignBatch({
380
+ context: {
381
+ chainCategory: "evm",
382
+ keyGen: args.keyGen,
383
+ purposeText: args.purposeText,
384
+ chainId: LIDO_ETHEREUM_MAINNET_CHAIN_ID,
385
+ rpcUrl: args.rpcUrl.trim(),
386
+ executorAddress: args.executorAddress,
387
+ chainDetail: args.chainDetail,
388
+ useCustomGas: args.useCustomGas,
389
+ customGasChainDetails: args.customGasChainDetails
390
+ },
391
+ steps: evmSteps,
392
+ purposeSuffix: args.purposeSuffix,
393
+ firstMsgRawNo0x: firstDataNo0x,
374
394
  destinationAddress: steps[0].to,
375
- extraJSON,
376
- signatureText: firstSigText,
377
- purpose: (() => {
378
- const t = args.purposeText.trim();
379
- const suffix = args.purposeSuffix.trim();
380
- return (t ? `${t}
381
-
382
- ` : "") + suffix;
383
- })(),
384
- ...firstTxFeePayload,
385
- proposalTxParams: proposalTxParamsBatch
386
- };
387
- if (firstValue > 0n) {
388
- bodyForSign.value = firstValue.toString();
389
- }
390
- if (clientId) bodyForSign.clientId = clientId;
391
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
395
+ buildBatchMeta: ({ index, gasLimit }) => steps[index].buildBatchMeta({ gasLimit })
396
+ });
392
397
  }
393
398
  async function buildEvmMultisignBodyLidoSubmit(args) {
394
399
  if (args.chainId !== LIDO_ETHEREUM_MAINNET_CHAIN_ID) {