@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 { parseAbi, getAddress, defineChain, createPublicClient, http, parseUnits, encodeFunctionData, parseGwei, serializeTransaction, keccak256, zeroAddress, maxUint256, formatUnits } from 'viem';
1
+ import { parseAbi, getAddress, defineChain, createPublicClient, http, parseUnits, encodeFunctionData, zeroAddress, maxUint256, formatUnits, isAddress, 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 = [];
@@ -85,98 +86,238 @@ function eulerSameAssetApproveAmountWithBuffer(args) {
85
86
  return args.totalPullWei + extra + 1n;
86
87
  }
87
88
 
88
- // src/core/keygen.ts
89
- function firstClientIdFromKeyGen(data) {
90
- const map = data?.ClientKeys;
91
- if (!map || typeof map !== "object") return null;
92
- for (const v of Object.values(map)) {
93
- if (typeof v === "string" && v.trim()) return v.trim();
89
+ // src/core/purpose.ts
90
+ function mergePurposeText(purposeText, purposeSuffix) {
91
+ const t = purposeText.trim();
92
+ const suffix = (purposeSuffix ?? "").trim();
93
+ if (!suffix) return t;
94
+ return t ? `${t}
95
+
96
+ ${suffix}` : suffix;
97
+ }
98
+
99
+ // src/core/envelope.ts
100
+ function finalizeMultisign(input) {
101
+ const { keyGen, destinationChainID, legs } = input;
102
+ if (legs.length === 0) {
103
+ throw new Error("finalizeMultisign requires at least one leg");
94
104
  }
95
- return null;
105
+ const ph = (keyGen.pubkeyhex ?? "").trim();
106
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
107
+ const keyList = keyGen.keylist ?? [];
108
+ const clientId = getClientIdFromKeyGenResult(keyGen);
109
+ const first = legs[0];
110
+ const messageHashes = legs.map((l) => l.msgHash);
111
+ const messageRawBatch = legs.map((l) => l.msgRaw);
112
+ const batchMeta = legs.map((l) => ({
113
+ destinationAddress: l.destinationAddress,
114
+ signatureText: l.signatureText,
115
+ ...l.audit
116
+ }));
117
+ const proposalTxParams = legs.map((l) => l.proposalTxParams).filter((p) => p != null && typeof p === "object");
118
+ const extraPayload = {
119
+ batchMeta,
120
+ ...input.extraJSON ?? {}
121
+ };
122
+ const extraJSON = JSON.stringify(extraPayload);
123
+ const bodyForSign = {
124
+ keyList,
125
+ pubKey: ph,
126
+ msgHash: messageHashes[0],
127
+ msgRaw: first.msgRaw,
128
+ destinationChainID,
129
+ destinationAddress: input.destinationAddress ?? first.destinationAddress,
130
+ extraJSON,
131
+ signatureText: first.signatureText,
132
+ purpose: mergePurposeText(input.purposeText, input.purposeSuffix),
133
+ ...first.feeSnapshot
134
+ };
135
+ if (legs.length > 1) {
136
+ bodyForSign.messageHashes = messageHashes;
137
+ bodyForSign.messageRawBatch = messageRawBatch;
138
+ }
139
+ if (proposalTxParams.length > 0) {
140
+ bodyForSign.proposalTxParams = proposalTxParams;
141
+ }
142
+ const valueWei = first.valueWei;
143
+ if (valueWei != null && valueWei > 0n) {
144
+ bodyForSign.value = valueWei.toString();
145
+ }
146
+ if (clientId) bodyForSign.clientId = clientId;
147
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
148
+ }
149
+ function routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimit) {
150
+ if (chainGasLimit != null && Number.isFinite(chainGasLimit) && chainGasLimit > 0) {
151
+ return gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit);
152
+ }
153
+ return (estimatedGas * 12n + 9n) / 10n;
96
154
  }
97
155
 
98
- // src/chains/evm/txParams.ts
99
- function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
100
- if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
101
- return estimatedGas;
102
- }
103
- const cfg = BigInt(Math.floor(chainGasLimit));
104
- return cfg > estimatedGas ? cfg : estimatedGas;
105
- }
106
- async function fetchChainFeeParams(rpcUrl, chainId) {
107
- const url = rpcUrl.trim();
108
- if (!url) return { isEip1559: false };
109
- const chainIdNum = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
110
- if (Number.isNaN(chainIdNum)) return { isEip1559: false };
111
- const chain = defineChain({
112
- id: chainIdNum,
113
- name: "Discovery",
156
+ // src/chains/evm/buildBatch.ts
157
+ async function buildEvmMultisignBatch(args) {
158
+ const { context, steps } = args;
159
+ const {
160
+ chainId,
161
+ rpcUrl,
162
+ executorAddress,
163
+ chainDetail,
164
+ useCustomGas,
165
+ customGasChainDetails,
166
+ keyGen,
167
+ purposeText
168
+ } = context;
169
+ if (steps.length === 0) throw new Error("buildEvmMultisignBatch requires at least one step");
170
+ const ch = defineChain({
171
+ id: chainId,
172
+ name: "Destination",
114
173
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
115
- rpcUrls: { default: { http: [url] } }
174
+ rpcUrls: { default: { http: [rpcUrl] } }
116
175
  });
117
- const publicClient = createPublicClient({
118
- chain,
119
- transport: http(url)
120
- });
121
- const getGasPriceGwei = async () => {
122
- const gasPriceWei = await publicClient.getGasPrice();
123
- return parseFloat(formatUnits(gasPriceWei, 9));
124
- };
125
- try {
126
- const block = await publicClient.getBlock({ blockTag: "latest" });
127
- const baseFeePerGas = block?.baseFeePerGas;
128
- if (baseFeePerGas == null || baseFeePerGas === void 0) {
129
- const gasPriceGwei2 = await getGasPriceGwei();
130
- return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
131
- }
132
- const baseFeeGwei = parseFloat(formatUnits(baseFeePerGas, 9));
133
- let priorityFeeGwei;
134
- try {
135
- const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
136
- priorityFeeGwei = parseFloat(formatUnits(priorityWei, 9));
137
- } catch {
176
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(rpcUrl) });
177
+ const feeParams = await fetchChainFeeParams(rpcUrl, chainId);
178
+ const legacy = Boolean(chainDetail?.legacy) || !feeParams.isEip1559;
179
+ const latestBaseFeeWei = !legacy ? (await publicClient2.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
180
+ const gasLimitConfig = useCustomGas && chainDetail?.gasLimit != null ? Number(chainDetail.gasLimit) : void 0;
181
+ const chainGasLimitRouter = chainDetail?.gasLimit != null && Number.isFinite(Number(chainDetail.gasLimit)) && Number(chainDetail.gasLimit) > 0 ? Number(chainDetail.gasLimit) : void 0;
182
+ const gasFeeMultiplier = useCustomGas && chainDetail?.gasMultiplier != null ? Number(chainDetail.gasMultiplier) : void 0;
183
+ const executor = getAddress(executorAddress);
184
+ const baseNonce = await publicClient2.getTransactionCount({ address: executor, blockTag: "pending" });
185
+ const legs = [];
186
+ for (let i = 0; i < steps.length; i++) {
187
+ const step = steps[i];
188
+ const currentNonce = baseNonce + i;
189
+ let estimatedGas;
190
+ if (args.estimateGasForStep) {
191
+ estimatedGas = await args.estimateGasForStep({ step, index: i, publicClient: publicClient2, executor });
192
+ } else {
193
+ try {
194
+ estimatedGas = await publicClient2.estimateGas({
195
+ to: step.to,
196
+ data: step.data,
197
+ value: step.value,
198
+ account: executor
199
+ });
200
+ } catch {
201
+ estimatedGas = step.fallbackGas ?? 100000n;
202
+ }
138
203
  }
139
- const gasPriceGwei = await getGasPriceGwei();
140
- return {
141
- isEip1559: true,
142
- baseFeeGwei,
143
- priorityFeeGwei,
144
- gasPriceGwei
145
- };
146
- } catch {
147
- try {
148
- const gasPriceWei = await publicClient.getGasPrice();
149
- const gasPriceGwei = parseFloat(formatUnits(gasPriceWei, 9));
150
- return { isEip1559: false, gasPriceGwei };
151
- } catch {
152
- return { isEip1559: false };
204
+ let gasLimitI;
205
+ if (args.resolveGasLimit) {
206
+ gasLimitI = await args.resolveGasLimit({ step, index: i, estimatedGas, publicClient: publicClient2 });
207
+ } else if (step.routerSwap) {
208
+ gasLimitI = routerSwapGasLimitFromEstimate(estimatedGas, chainGasLimitRouter);
209
+ } else {
210
+ gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
211
+ }
212
+ let proposalTxParams;
213
+ let feeSnapshot;
214
+ let serialized;
215
+ if (legacy) {
216
+ let gasPriceWei = await publicClient2.getGasPrice();
217
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
218
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
219
+ }
220
+ if (useCustomGas && chainDetail?.gasPrice != null && chainDetail.gasPrice > 0) {
221
+ const configured = parseGwei(gweiToDecimalString(Number(chainDetail.gasPrice)));
222
+ if (configured > gasPriceWei) gasPriceWei = configured;
223
+ }
224
+ serialized = serializeTransaction({
225
+ type: "legacy",
226
+ to: step.to,
227
+ data: step.data,
228
+ value: step.value,
229
+ gas: gasLimitI,
230
+ gasPrice: gasPriceWei,
231
+ nonce: currentNonce,
232
+ chainId
233
+ });
234
+ proposalTxParams = {
235
+ nonce: currentNonce,
236
+ gasLimit: gasLimitI.toString(),
237
+ txType: "legacy",
238
+ gasPrice: gasPriceWei.toString()
239
+ };
240
+ feeSnapshot = proposalTxParamsToFeeSnapshot(proposalTxParams);
241
+ } else {
242
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
243
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
244
+ const configuredBase = useCustomGas && chainDetail?.baseFee != null ? Number(chainDetail.baseFee) : 0;
245
+ const configuredPriority = useCustomGas && chainDetail?.priorityFee != null ? Number(chainDetail.priorityFee) : 0;
246
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
247
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
248
+ const baseFeeMultiplierPct = useCustomGas && chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(chainDetail.baseFeeMultiplier)) : 100;
249
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
250
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
251
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
252
+ let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
253
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
254
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
255
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
256
+ }
257
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
258
+ maxFeePerGas,
259
+ maxPriorityFeePerGas,
260
+ latestBaseFeeWei
261
+ ));
262
+ serialized = serializeTransaction({
263
+ type: "eip1559",
264
+ to: step.to,
265
+ data: step.data,
266
+ value: step.value,
267
+ gas: gasLimitI,
268
+ maxFeePerGas,
269
+ maxPriorityFeePerGas,
270
+ nonce: currentNonce,
271
+ chainId
272
+ });
273
+ proposalTxParams = {
274
+ nonce: currentNonce,
275
+ gasLimit: gasLimitI.toString(),
276
+ txType: "eip1559",
277
+ maxFeePerGas: maxFeePerGas.toString(),
278
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
279
+ };
280
+ feeSnapshot = i === 0 ? proposalTxParamsToFeeSnapshot(proposalTxParams) : {};
281
+ }
282
+ const h = keccak256(serialized);
283
+ const msgHash = h.startsWith("0x") ? h.slice(2) : h;
284
+ const batchMetaExtra = args.buildBatchMeta({ step, index: i, gasLimit: gasLimitI });
285
+ legs.push({
286
+ msgHash,
287
+ msgRaw: i === 0 && args.firstMsgRawNo0x != null ? args.firstMsgRawNo0x : serialized,
288
+ destinationAddress: step.to,
289
+ signatureText: typeof batchMetaExtra.signatureText === "string" ? batchMetaExtra.signatureText : JSON.stringify(batchMetaExtra.signatureText ?? {}),
290
+ audit: batchMetaExtra,
291
+ feeSnapshot: i === 0 ? feeSnapshot : {},
292
+ proposalTxParams,
293
+ valueWei: i === 0 ? step.value : void 0
294
+ });
295
+ if (i === 0 && args.firstMsgRawNo0x != null) {
296
+ legs[0].msgRaw = args.firstMsgRawNo0x;
153
297
  }
154
298
  }
155
- }
156
- function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
157
- let maxP = maxPriorityFeePerGas;
158
- let maxF = maxFeePerGas;
159
- if (baseWei > 0n && maxF < baseWei + maxP) {
160
- maxF = baseWei + maxP + parseGwei("0.001");
299
+ const extraJSON = {};
300
+ if (useCustomGas && customGasChainDetails && Object.keys(customGasChainDetails).length > 0) {
301
+ extraJSON.customGasChainDetails = customGasChainDetails;
161
302
  }
162
- if (maxF < maxP) {
163
- maxF = baseWei > 0n ? baseWei + maxP + parseGwei("0.001") : maxP * 2n;
303
+ const result = finalizeMultisign({
304
+ keyGen,
305
+ purposeText,
306
+ purposeSuffix: args.purposeSuffix,
307
+ destinationChainID: String(chainId),
308
+ destinationAddress: args.destinationAddress ?? steps[0].to,
309
+ legs,
310
+ extraJSON: Object.keys(extraJSON).length > 0 ? extraJSON : void 0
311
+ });
312
+ const pv = args.payableValueWei;
313
+ if (pv != null && pv > 0n) {
314
+ result.bodyForSign.value = pv.toString();
164
315
  }
165
- return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
166
- }
167
- function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
168
- return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
316
+ return result;
169
317
  }
170
318
 
171
319
  // src/protocols/evm/euler-v2/vaultWithdrawMultisign.ts
172
320
  var EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS = 900000n;
173
- function gweiToDecimalString(n) {
174
- if (!Number.isFinite(n)) return "0";
175
- if (n === 0) return "0";
176
- const s = String(n);
177
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
178
- return s;
179
- }
180
321
  var erc20DecimalsAbi = parseAbi(["function decimals() view returns (uint8)"]);
181
322
  var erc4626AssetAbi = parseAbi(["function asset() view returns (address)"]);
182
323
  var erc4626MaxWithdrawAbi = parseAbi(["function maxWithdraw(address owner) view returns (uint256)"]);
@@ -190,14 +331,14 @@ async function fetchEulerVaultUnderlyingMeta(args) {
190
331
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
191
332
  rpcUrls: { default: { http: [args.rpcUrl] } }
192
333
  });
193
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
194
- const assetAddrRaw = await publicClient.readContract({
334
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
335
+ const assetAddrRaw = await publicClient2.readContract({
195
336
  address: args.evault,
196
337
  abi: erc4626AssetAbi,
197
338
  functionName: "asset"
198
339
  });
199
340
  const assetAddr = getAddress(assetAddrRaw);
200
- const d = await publicClient.readContract({
341
+ const d = await publicClient2.readContract({
201
342
  address: assetAddr,
202
343
  abi: erc20DecimalsAbi,
203
344
  functionName: "decimals"
@@ -229,8 +370,8 @@ async function fetchEulerVaultMaxWithdrawWei(args) {
229
370
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
230
371
  rpcUrls: { default: { http: [args.rpcUrl] } }
231
372
  });
232
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
233
- return publicClient.readContract({
373
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
374
+ return publicClient2.readContract({
234
375
  address: args.evault,
235
376
  abi: erc4626MaxWithdrawAbi,
236
377
  functionName: "maxWithdraw",
@@ -267,19 +408,19 @@ async function eulerLendEarnEffectiveMaxWeiBySimulation(args) {
267
408
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
268
409
  rpcUrls: { default: { http: [args.rpcUrl] } }
269
410
  });
270
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
411
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
271
412
  let shareBal;
272
413
  let vaultCash;
273
414
  try {
274
415
  ;
275
416
  [shareBal, vaultCash] = await Promise.all([
276
- publicClient.readContract({
417
+ publicClient2.readContract({
277
418
  address: args.evault,
278
419
  abi: eulerVaultSharesCashAbi,
279
420
  functionName: "balanceOf",
280
421
  args: [args.vaultShareOwner]
281
422
  }),
282
- publicClient.readContract({
423
+ publicClient2.readContract({
283
424
  address: args.evault,
284
425
  abi: eulerVaultSharesCashAbi,
285
426
  functionName: "cash"
@@ -291,7 +432,7 @@ async function eulerLendEarnEffectiveMaxWeiBySimulation(args) {
291
432
  if (shareBal === 0n) return 0n;
292
433
  let assetsOwned;
293
434
  try {
294
- assetsOwned = await publicClient.readContract({
435
+ assetsOwned = await publicClient2.readContract({
295
436
  address: args.evault,
296
437
  abi: eulerVaultSharesCashAbi,
297
438
  functionName: "convertToAssets",
@@ -308,7 +449,7 @@ async function eulerLendEarnEffectiveMaxWeiBySimulation(args) {
308
449
  while (lo <= hiProbe) {
309
450
  const mid = lo + (hiProbe - lo) / 2n;
310
451
  const ok = await eulerDirectWithdrawSimulatesOk({
311
- publicClient,
452
+ publicClient: publicClient2,
312
453
  evault: args.evault,
313
454
  vaultShareOwner: args.vaultShareOwner,
314
455
  receiver: args.receiver,
@@ -347,157 +488,7 @@ async function fetchEulerLendEarnVaultEffectiveMaxWithdrawWei(args) {
347
488
  txSender
348
489
  });
349
490
  }
350
- async function eulerMultisignBodyOneStep(args) {
351
- const ch = defineChain({
352
- id: args.chainId,
353
- name: "Destination",
354
- nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
355
- rpcUrls: { default: { http: [args.rpcUrl] } }
356
- });
357
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
358
- const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
359
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
360
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
361
- const useCustomGas = args.useCustomGas;
362
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
363
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
364
- const baseNonce = await publicClient.getTransactionCount({ address: args.executorAddress, blockTag: "pending" });
365
- let estimatedGas;
366
- try {
367
- estimatedGas = await publicClient.estimateGas({
368
- to: args.to,
369
- data: args.data,
370
- value: args.value,
371
- account: args.executorAddress
372
- });
373
- } catch {
374
- estimatedGas = args.estimateGasFallback;
375
- }
376
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
377
- const batchMeta0 = args.buildBatchMeta({ gasLimit: gasLimitI });
378
- let firstTxFeePayload = {};
379
- let firstDataNo0x = "";
380
- const messageHashes = [];
381
- const messageRawBatch = [];
382
- const proposalTxParamsBatch = [];
383
- const vTo = args.to;
384
- const vData = args.data;
385
- const vValue = args.value;
386
- if (legacy) {
387
- let gasPriceWei = await publicClient.getGasPrice();
388
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
389
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
390
- }
391
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
392
- const configured = parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
393
- if (configured > gasPriceWei) gasPriceWei = configured;
394
- }
395
- const ser = serializeTransaction({
396
- type: "legacy",
397
- to: vTo,
398
- data: vData,
399
- value: vValue,
400
- gas: gasLimitI,
401
- gasPrice: gasPriceWei,
402
- nonce: baseNonce,
403
- chainId: args.chainId
404
- });
405
- const h = keccak256(ser);
406
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
407
- messageRawBatch.push(ser);
408
- proposalTxParamsBatch.push({
409
- nonce: baseNonce,
410
- gasLimit: gasLimitI.toString(),
411
- txType: "legacy",
412
- gasPrice: gasPriceWei.toString()
413
- });
414
- firstTxFeePayload = { txNonce: baseNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
415
- firstDataNo0x = vData.startsWith("0x") ? vData.slice(2) : vData;
416
- } else {
417
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
418
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
419
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
420
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
421
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
422
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
423
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
424
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
425
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
426
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
427
- let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
428
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
429
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
430
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
431
- }
432
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
433
- maxFeePerGas,
434
- maxPriorityFeePerGas,
435
- latestBaseFeeWei
436
- ));
437
- const ser = serializeTransaction({
438
- type: "eip1559",
439
- to: vTo,
440
- data: vData,
441
- value: vValue,
442
- gas: gasLimitI,
443
- maxFeePerGas,
444
- maxPriorityFeePerGas,
445
- nonce: baseNonce,
446
- chainId: args.chainId
447
- });
448
- const h = keccak256(ser);
449
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
450
- messageRawBatch.push(ser);
451
- proposalTxParamsBatch.push({
452
- nonce: baseNonce,
453
- gasLimit: gasLimitI.toString(),
454
- txType: "eip1559",
455
- maxFeePerGas: maxFeePerGas.toString(),
456
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
457
- });
458
- firstTxFeePayload = {
459
- txNonce: baseNonce,
460
- txGasLimit: gasLimitI.toString(),
461
- txMaxFeePerGas: maxFeePerGas.toString(),
462
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
463
- };
464
- firstDataNo0x = vData.startsWith("0x") ? vData.slice(2) : vData;
465
- }
466
- const extraPayload = { batchMeta: [batchMeta0] };
467
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
468
- extraPayload.customGasChainDetails = args.customGasChainDetails;
469
- }
470
- const extraJSON = JSON.stringify(extraPayload);
471
- const firstSigText = batchMeta0.signatureText;
472
- const bodyForSign = {
473
- keyList: args.keyList,
474
- pubKey: args.ph,
475
- msgHash: messageHashes[0],
476
- msgRaw: firstDataNo0x,
477
- messageHashes,
478
- messageRawBatch,
479
- destinationChainID: String(args.chainId),
480
- destinationAddress: vTo,
481
- extraJSON,
482
- signatureText: firstSigText,
483
- purpose: (() => {
484
- const t = args.purposeText.trim();
485
- return (t ? `${t}
486
-
487
- ` : "") + args.purposeSuffix;
488
- })(),
489
- ...firstTxFeePayload,
490
- proposalTxParams: proposalTxParamsBatch
491
- };
492
- if (vValue > 0n) bodyForSign.value = vValue.toString();
493
- if (args.clientId) bodyForSign.clientId = args.clientId;
494
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
495
- }
496
491
  async function buildEvmMultisignBodyEulerV2VaultWithdraw(args) {
497
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
498
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
499
- const keyList = args.keyGen.keylist ?? [];
500
- const clientId = firstClientIdFromKeyGen(args.keyGen);
501
492
  const evault = getAddress(args.evault);
502
493
  const receiver = getAddress(args.owner);
503
494
  const shareOwner = getAddress(args.vaultShareOwner ?? args.owner);
@@ -522,24 +513,32 @@ async function buildEvmMultisignBodyEulerV2VaultWithdraw(args) {
522
513
  });
523
514
  const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
524
515
  const purposeSuffix = `Euler v2: 1-tx \u2014 eVault.withdraw (${args.amountHuman} underlying) from isolated vault "${vaultLabel}" (ERC-4626).`;
525
- return eulerMultisignBodyOneStep({
526
- ph,
527
- keyList,
528
- clientId: clientId ?? void 0,
529
- chainId: args.chainId,
530
- rpcUrl: args.rpcUrl,
531
- chainDetail: args.chainDetail,
532
- useCustomGas: args.useCustomGas,
533
- customGasChainDetails: args.customGasChainDetails,
534
- purposeText: args.purposeText,
516
+ const evmSteps = [
517
+ {
518
+ to: evault,
519
+ data: withdrawData,
520
+ value: 0n,
521
+ fallbackGas: EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS
522
+ }
523
+ ];
524
+ const firstDataNo0x = withdrawData.startsWith("0x") ? withdrawData.slice(2) : withdrawData;
525
+ return buildEvmMultisignBatch({
526
+ context: {
527
+ chainCategory: "evm",
528
+ keyGen: args.keyGen,
529
+ purposeText: args.purposeText,
530
+ chainId: args.chainId,
531
+ rpcUrl: args.rpcUrl,
532
+ executorAddress: executor,
533
+ chainDetail: args.chainDetail,
534
+ useCustomGas: args.useCustomGas,
535
+ customGasChainDetails: args.customGasChainDetails
536
+ },
537
+ steps: evmSteps,
535
538
  purposeSuffix,
536
- executorAddress: executor,
537
- to: evault,
538
- data: withdrawData,
539
- value: 0n,
540
- estimateGasFallback: EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS,
541
- buildBatchMeta: (ctx) => ({
542
- destinationAddress: evault,
539
+ firstMsgRawNo0x: firstDataNo0x,
540
+ destinationAddress: evault,
541
+ buildBatchMeta: ({ gasLimit }) => ({
543
542
  signatureText: JSON.stringify({
544
543
  kind: "EulerV2",
545
544
  name: "EVault.withdraw",
@@ -556,7 +555,7 @@ async function buildEvmMultisignBodyEulerV2VaultWithdraw(args) {
556
555
  amountHuman: args.amountHuman,
557
556
  evault,
558
557
  owner: shareOwner,
559
- gasBuildWithdraw: { baseGasUnits: ctx.gasLimit.toString() }
558
+ gasBuildWithdraw: { baseGasUnits: gasLimit.toString() }
560
559
  }
561
560
  })
562
561
  });
@@ -613,20 +612,20 @@ async function collateralMaxWithdrawBySimulation(args) {
613
612
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
614
613
  rpcUrls: { default: { http: [args.rpcUrl] } }
615
614
  });
616
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
615
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
617
616
  let shareBal;
618
617
  let assetsOwned;
619
618
  let vaultCash;
620
619
  try {
621
620
  ;
622
621
  [shareBal, vaultCash] = await Promise.all([
623
- publicClient.readContract({
622
+ publicClient2.readContract({
624
623
  address: args.collateralVault,
625
624
  abi: eulerCollateralVaultReadAbi,
626
625
  functionName: "balanceOf",
627
626
  args: [args.subAccount]
628
627
  }),
629
- publicClient.readContract({
628
+ publicClient2.readContract({
630
629
  address: args.collateralVault,
631
630
  abi: eulerCollateralVaultReadAbi,
632
631
  functionName: "cash"
@@ -637,7 +636,7 @@ async function collateralMaxWithdrawBySimulation(args) {
637
636
  }
638
637
  if (shareBal === 0n) return 0n;
639
638
  try {
640
- assetsOwned = await publicClient.readContract({
639
+ assetsOwned = await publicClient2.readContract({
641
640
  address: args.collateralVault,
642
641
  abi: eulerCollateralVaultReadAbi,
643
642
  functionName: "convertToAssets",
@@ -654,7 +653,7 @@ async function collateralMaxWithdrawBySimulation(args) {
654
653
  while (lo <= hiProbe) {
655
654
  const mid = lo + (hiProbe - lo) / 2n;
656
655
  const ok = await evcWithdrawSimulatesOk({
657
- publicClient,
656
+ publicClient: publicClient2,
658
657
  evc: args.evc,
659
658
  collateralVault: args.collateralVault,
660
659
  subAccount: args.subAccount,
@@ -697,16 +696,6 @@ var EULER_V2_ISOLATED_VAULT_DEPOSIT_FALLBACK_GAS = 950000n;
697
696
  var EULER_VAULT_DEPOSIT_ESTIMATE_FALLBACK = EULER_V2_ISOLATED_VAULT_DEPOSIT_FALLBACK_GAS;
698
697
  var EULER_ERC20_APPROVE_FALLBACK = 100000n;
699
698
  var EULER_WETH_DEPOSIT_FALLBACK = 120000n;
700
- function gweiToDecimalString2(n) {
701
- if (!Number.isFinite(n)) return "0";
702
- if (n === 0) return "0";
703
- const s = String(n);
704
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
705
- return s;
706
- }
707
- function txToViemStep(tx) {
708
- return { to: getAddress(tx.to), data: tx.data, value: tx.value };
709
- }
710
699
  var wethDepositAbi = parseAbi(["function deposit() payable"]);
711
700
  var erc20AllowanceAbi = parseAbi([
712
701
  "function allowance(address owner, address spender) view returns (uint256)",
@@ -717,10 +706,6 @@ var erc4626DepositAbi = parseAbi([
717
706
  "function deposit(uint256 assets, address receiver) returns (uint256 shares)"
718
707
  ]);
719
708
  async function buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch(args) {
720
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
721
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
722
- const keyList = args.keyGen.keylist ?? [];
723
- const clientId = firstClientIdFromKeyGen(args.keyGen);
724
709
  const asset = getAddress(args.underlying);
725
710
  const evault = getAddress(args.evault);
726
711
  const weth = getAddress(args.nativeWrapped);
@@ -735,8 +720,8 @@ async function buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch(args) {
735
720
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
736
721
  rpcUrls: { default: { http: [args.rpcUrl] } }
737
722
  });
738
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
739
- const dec = await publicClient.readContract({
723
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
724
+ const dec = await publicClient2.readContract({
740
725
  address: asset,
741
726
  abi: erc20AllowanceAbi,
742
727
  functionName: "decimals"
@@ -747,7 +732,7 @@ async function buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch(args) {
747
732
  if (args.isNativeIn) {
748
733
  const dataDeposit = encodeFunctionData({ abi: wethDepositAbi, functionName: "deposit", args: [] });
749
734
  steps.push({ kind: "weth_deposit", to: weth, data: dataDeposit, value: amountWei });
750
- const wethAllowance = await publicClient.readContract({
735
+ const wethAllowance = await publicClient2.readContract({
751
736
  address: weth,
752
737
  abi: erc20AllowanceAbi,
753
738
  functionName: "allowance",
@@ -770,7 +755,7 @@ async function buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch(args) {
770
755
  steps.push({ kind: "approve", to: weth, data: dataApprove, value: 0n });
771
756
  }
772
757
  } else {
773
- const currentAllowance = await publicClient.readContract({
758
+ const currentAllowance = await publicClient2.readContract({
774
759
  address: asset,
775
760
  abi: erc20AllowanceAbi,
776
761
  functionName: "allowance",
@@ -799,154 +784,79 @@ async function buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch(args) {
799
784
  args: [amountWei, receiver]
800
785
  });
801
786
  steps.push({ kind: "vault_deposit", to: evault, data: depositData, value: 0n });
802
- const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
803
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
804
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
805
- const useCustomGas = args.useCustomGas;
806
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
807
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
808
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
809
- const messageHashes = [];
810
- const messageRawBatch = [];
811
- const proposalTxParamsBatch = [];
812
- const batchMeta = [];
813
- let firstTxFeePayload = {};
814
- let firstDataNo0x = "";
815
787
  const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
816
- for (let i = 0; i < steps.length; i++) {
817
- const s = steps[i];
818
- const v = txToViemStep(s);
819
- const currentNonce = baseNonce + i;
820
- let estimatedGas;
821
- try {
822
- estimatedGas = await publicClient.estimateGas({
823
- to: v.to,
824
- data: v.data,
825
- value: v.value,
826
- account: executor
827
- });
828
- } catch {
829
- if (s.kind === "weth_deposit") estimatedGas = EULER_WETH_DEPOSIT_FALLBACK;
830
- else if (s.kind === "approve") estimatedGas = EULER_ERC20_APPROVE_FALLBACK;
831
- else estimatedGas = EULER_VAULT_DEPOSIT_ESTIMATE_FALLBACK;
788
+ const evmSteps = steps.map((s) => ({
789
+ to: s.to,
790
+ data: s.data,
791
+ value: s.value,
792
+ fallbackGas: s.kind === "weth_deposit" ? EULER_WETH_DEPOSIT_FALLBACK : s.kind === "approve" ? EULER_ERC20_APPROVE_FALLBACK : EULER_VAULT_DEPOSIT_ESTIMATE_FALLBACK
793
+ }));
794
+ const n = steps.length;
795
+ const hasWrap = args.isNativeIn;
796
+ const purposeSuffix = (() => {
797
+ if (hasWrap) {
798
+ return `Euler v2: ${n}-tx batch \u2014 wrap native to WETH (if needed), approve eVault for the exact amount, then ERC-4626 deposit into isolated vault "${vaultLabel}".`;
832
799
  }
833
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
834
- if (legacy) {
835
- let gasPriceWei = await publicClient.getGasPrice();
836
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
837
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
838
- }
839
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
840
- const configured = parseGwei(gweiToDecimalString2(Number(args.chainDetail.gasPrice)));
841
- if (configured > gasPriceWei) gasPriceWei = configured;
842
- }
843
- const ser = serializeTransaction({
844
- type: "legacy",
845
- to: v.to,
846
- data: v.data,
847
- value: v.value,
848
- gas: gasLimitI,
849
- gasPrice: gasPriceWei,
850
- nonce: currentNonce,
851
- chainId: args.chainId
852
- });
853
- const h = keccak256(ser);
854
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
855
- messageRawBatch.push(ser);
856
- proposalTxParamsBatch.push({
857
- nonce: currentNonce,
858
- gasLimit: gasLimitI.toString(),
859
- txType: "legacy",
860
- gasPrice: gasPriceWei.toString()
861
- });
862
- if (i === 0) {
863
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
864
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
865
- }
866
- } else {
867
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
868
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
869
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
870
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
871
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
872
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
873
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
874
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
875
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
876
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString2(effectivePriorityFeeGwei)) : parseGwei("1");
877
- let maxFeePerGas = parseGwei(gweiToDecimalString2(maxFeePerGasGwei));
878
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
879
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
880
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
800
+ if (n === 1) {
801
+ return `Euler v2: 1-tx \u2014 eVault.deposit (allowance already sufficient) into "${vaultLabel}".`;
802
+ }
803
+ return `Euler v2: ${n}-tx batch \u2014 approve eVault for the exact amount, then ERC-4626 deposit into "${vaultLabel}".`;
804
+ })();
805
+ const firstDataNo0x = evmSteps[0].data.startsWith("0x") ? evmSteps[0].data.slice(2) : evmSteps[0].data;
806
+ return buildEvmMultisignBatch({
807
+ context: {
808
+ chainCategory: "evm",
809
+ keyGen: args.keyGen,
810
+ purposeText: args.purposeText,
811
+ chainId: args.chainId,
812
+ rpcUrl: args.rpcUrl,
813
+ executorAddress: executor,
814
+ chainDetail: args.chainDetail,
815
+ useCustomGas: args.useCustomGas,
816
+ customGasChainDetails: args.customGasChainDetails
817
+ },
818
+ steps: evmSteps,
819
+ purposeSuffix,
820
+ firstMsgRawNo0x: firstDataNo0x,
821
+ destinationAddress: steps[0].to,
822
+ buildBatchMeta: ({ index, gasLimit }) => {
823
+ const s = steps[index];
824
+ if (s.kind === "weth_deposit") {
825
+ return {
826
+ signatureText: JSON.stringify({
827
+ kind: "EulerV2",
828
+ name: "WETH.deposit",
829
+ function: "deposit()",
830
+ valueWei: amountWei.toString(),
831
+ vaultMarket: vaultLabel,
832
+ note: "Wrap native for Euler v2 isolated vault deposit (same batch)."
833
+ }),
834
+ evm: { type: "euler_v2_weth_deposit", version: 1, chainId: String(args.chainId) },
835
+ eulerV2: {
836
+ step: "weth_deposit",
837
+ vaultMarket: vaultLabel,
838
+ amountHuman: args.amountHuman,
839
+ evault,
840
+ underlying: asset
841
+ }
842
+ };
881
843
  }
882
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
883
- maxFeePerGas,
884
- maxPriorityFeePerGas,
885
- latestBaseFeeWei
886
- ));
887
- const ser = serializeTransaction({
888
- type: "eip1559",
889
- to: v.to,
890
- data: v.data,
891
- value: v.value,
892
- gas: gasLimitI,
893
- maxFeePerGas,
894
- maxPriorityFeePerGas,
895
- nonce: currentNonce,
896
- chainId: args.chainId
897
- });
898
- const h = keccak256(ser);
899
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
900
- messageRawBatch.push(ser);
901
- proposalTxParamsBatch.push({
902
- nonce: currentNonce,
903
- gasLimit: gasLimitI.toString(),
904
- txType: "eip1559",
905
- maxFeePerGas: maxFeePerGas.toString(),
906
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
907
- });
908
- if (i === 0) {
909
- firstTxFeePayload = {
910
- txNonce: currentNonce,
911
- txGasLimit: gasLimitI.toString(),
912
- txMaxFeePerGas: maxFeePerGas.toString(),
913
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
844
+ if (s.kind === "approve") {
845
+ return {
846
+ signatureText: JSON.stringify({
847
+ kind: "EulerV2",
848
+ name: "ERC20.approve",
849
+ to: "Euler eVault",
850
+ function: "approve(address spender, uint256 amount)",
851
+ evault,
852
+ amountHuman: args.amountHuman,
853
+ note: "Allowance for this deposit amount only (not unlimited)."
854
+ }),
855
+ evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
856
+ eulerV2: { vaultMarket: vaultLabel, amountHuman: args.amountHuman, evault, underlying: asset }
914
857
  };
915
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
916
858
  }
917
- }
918
- if (s.kind === "weth_deposit") {
919
- batchMeta.push({
920
- destinationAddress: weth,
921
- signatureText: JSON.stringify({
922
- kind: "EulerV2",
923
- name: "WETH.deposit",
924
- function: "deposit()",
925
- valueWei: amountWei.toString(),
926
- vaultMarket: vaultLabel,
927
- note: "Wrap native for Euler v2 isolated vault deposit (same batch)."
928
- }),
929
- evm: { type: "euler_v2_weth_deposit", version: 1, chainId: String(args.chainId) },
930
- eulerV2: { step: "weth_deposit", vaultMarket: vaultLabel, amountHuman: args.amountHuman, evault, underlying: asset }
931
- });
932
- } else if (s.kind === "approve") {
933
- batchMeta.push({
934
- destinationAddress: s.to,
935
- signatureText: JSON.stringify({
936
- kind: "EulerV2",
937
- name: "ERC20.approve",
938
- to: "Euler eVault",
939
- function: "approve(address spender, uint256 amount)",
940
- evault,
941
- amountHuman: args.amountHuman,
942
- note: "Allowance for this deposit amount only (not unlimited)."
943
- }),
944
- evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
945
- eulerV2: { vaultMarket: vaultLabel, amountHuman: args.amountHuman, evault, underlying: asset }
946
- });
947
- } else {
948
- batchMeta.push({
949
- destinationAddress: evault,
859
+ return {
950
860
  signatureText: JSON.stringify({
951
861
  kind: "EulerV2",
952
862
  name: "EVault.deposit",
@@ -964,54 +874,11 @@ async function buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch(args) {
964
874
  evault,
965
875
  underlying: asset,
966
876
  receiver,
967
- gasBuildDeposit: { baseGasUnits: gasLimitI.toString() }
877
+ gasBuildDeposit: { baseGasUnits: gasLimit.toString() }
968
878
  }
969
- });
970
- }
971
- }
972
- const extraPayload = { batchMeta };
973
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
974
- extraPayload.customGasChainDetails = args.customGasChainDetails;
975
- }
976
- const extraJSON = JSON.stringify(extraPayload);
977
- const firstSigText = batchMeta[0].signatureText;
978
- const n = steps.length;
979
- const hasWrap = args.isNativeIn;
980
- const purposeSuffix = (() => {
981
- if (hasWrap) {
982
- return `Euler v2: ${n}-tx batch \u2014 wrap native to WETH (if needed), approve eVault for the exact amount, then ERC-4626 deposit into isolated vault "${vaultLabel}".`;
983
- }
984
- if (n === 1) {
985
- return `Euler v2: 1-tx \u2014 eVault.deposit (allowance already sufficient) into "${vaultLabel}".`;
879
+ };
986
880
  }
987
- return `Euler v2: ${n}-tx batch \u2014 approve eVault for the exact amount, then ERC-4626 deposit into "${vaultLabel}".`;
988
- })();
989
- const firstValue = steps[0].value;
990
- const bodyForSign = {
991
- keyList,
992
- pubKey: ph,
993
- msgHash: messageHashes[0],
994
- msgRaw: firstDataNo0x,
995
- messageHashes,
996
- messageRawBatch,
997
- destinationChainID: String(args.chainId),
998
- destinationAddress: steps[0].to,
999
- extraJSON,
1000
- signatureText: firstSigText,
1001
- purpose: (() => {
1002
- const t = args.purposeText.trim();
1003
- return (t ? `${t}
1004
-
1005
- ` : "") + purposeSuffix;
1006
- })(),
1007
- ...firstTxFeePayload,
1008
- proposalTxParams: proposalTxParamsBatch
1009
- };
1010
- if (firstValue > 0n) {
1011
- bodyForSign.value = firstValue.toString();
1012
- }
1013
- if (clientId) bodyForSign.clientId = clientId;
1014
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
881
+ });
1015
882
  }
1016
883
  var EULER_BORROW_BATCH_FALLBACK_GAS = 2500000n;
1017
884
  var EULER_BORROW_BATCH_FALLBACK_GAS_PER_ROUND = 350000n;
@@ -1030,21 +897,7 @@ var evaultBorrowAbi = parseAbi(["function borrow(uint256 amount, address receive
1030
897
  var evcAbi = parseAbi([
1031
898
  "function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
1032
899
  ]);
1033
- function gweiToDecimalString3(n) {
1034
- if (!Number.isFinite(n)) return "0";
1035
- if (n === 0) return "0";
1036
- const s = String(n);
1037
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
1038
- return s;
1039
- }
1040
- function txToViemStep2(tx) {
1041
- return { to: getAddress(tx.to), data: tx.data, value: tx.value };
1042
- }
1043
900
  async function buildEvmMultisignBodyEulerV2IsolatedBorrowBatch(args) {
1044
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
1045
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1046
- const keyList = args.keyGen.keylist ?? [];
1047
- const clientId = firstClientIdFromKeyGen(args.keyGen);
1048
901
  const evc = getAddress(args.evc);
1049
902
  const borrowVault = getAddress(args.borrowVault);
1050
903
  const collateralVault = getAddress(args.collateralVault);
@@ -1062,8 +915,8 @@ async function buildEvmMultisignBodyEulerV2IsolatedBorrowBatch(args) {
1062
915
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1063
916
  rpcUrls: { default: { http: [args.rpcUrl] } }
1064
917
  });
1065
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
1066
- const cDecRaw = await publicClient.readContract({
918
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
919
+ const cDecRaw = await publicClient2.readContract({
1067
920
  address: collateralAsset,
1068
921
  abi: erc20AllowanceAbi2,
1069
922
  functionName: "decimals"
@@ -1071,7 +924,7 @@ async function buildEvmMultisignBodyEulerV2IsolatedBorrowBatch(args) {
1071
924
  const collateralDecimals = Number(cDecRaw);
1072
925
  const collateralWei = parseUnits(args.collateralAmountHuman, collateralDecimals);
1073
926
  if (collateralWei === 0n) throw new Error("Collateral amount is zero after converting with token decimals.");
1074
- await publicClient.readContract({
927
+ await publicClient2.readContract({
1075
928
  address: borrowAsset,
1076
929
  abi: erc20AllowanceAbi2,
1077
930
  functionName: "decimals"
@@ -1094,7 +947,7 @@ async function buildEvmMultisignBodyEulerV2IsolatedBorrowBatch(args) {
1094
947
  if (args.isNativeCollateralIn) {
1095
948
  const dataDeposit = encodeFunctionData({ abi: wethDepositAbi2, functionName: "deposit", args: [] });
1096
949
  steps.push({ kind: "weth_deposit", to: weth, data: dataDeposit, value: collateralWei });
1097
- const wethAllowance = await publicClient.readContract({
950
+ const wethAllowance = await publicClient2.readContract({
1098
951
  address: weth,
1099
952
  abi: erc20AllowanceAbi2,
1100
953
  functionName: "allowance",
@@ -1117,7 +970,7 @@ async function buildEvmMultisignBodyEulerV2IsolatedBorrowBatch(args) {
1117
970
  steps.push({ kind: "approve", to: weth, data: dataApprove, value: 0n });
1118
971
  }
1119
972
  } else {
1120
- const currentAllowance = await publicClient.readContract({
973
+ const currentAllowance = await publicClient2.readContract({
1121
974
  address: collateralAsset,
1122
975
  abi: erc20AllowanceAbi2,
1123
976
  functionName: "allowance",
@@ -1193,156 +1046,73 @@ async function buildEvmMultisignBodyEulerV2IsolatedBorrowBatch(args) {
1193
1046
  });
1194
1047
  steps.push({ kind: "evc_batch", to: evc, data: batchData, value: 0n });
1195
1048
  const borrowRoundsExtraGas = EULER_BORROW_BATCH_FALLBACK_GAS_PER_ROUND * BigInt(Math.max(0, loops.length - 1 + (args.redepositBorrowedToCollateral ? loops.length : 0)));
1196
- const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
1197
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
1198
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
1199
- const useCustomGas = args.useCustomGas;
1200
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
1201
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
1202
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
1203
- const messageHashes = [];
1204
- const messageRawBatch = [];
1205
- const proposalTxParamsBatch = [];
1206
- const batchMeta = [];
1207
- let firstTxFeePayload = {};
1208
- let firstDataNo0x = "";
1209
1049
  const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
1210
- for (let i = 0; i < steps.length; i++) {
1211
- const s = steps[i];
1212
- const v = txToViemStep2(s);
1213
- const currentNonce = baseNonce + i;
1214
- let estimatedGas;
1215
- try {
1216
- estimatedGas = await publicClient.estimateGas({
1217
- to: v.to,
1218
- data: v.data,
1219
- value: v.value,
1220
- account: executor
1221
- });
1222
- } catch {
1223
- if (s.kind === "weth_deposit") estimatedGas = EULER_WETH_DEPOSIT_FALLBACK2;
1224
- else if (s.kind === "approve") estimatedGas = EULER_ERC20_APPROVE_FALLBACK2;
1225
- else estimatedGas = EULER_BORROW_BATCH_FALLBACK_GAS + borrowRoundsExtraGas;
1050
+ const evmSteps = steps.map((s) => ({
1051
+ to: s.to,
1052
+ data: s.data,
1053
+ value: s.value,
1054
+ fallbackGas: s.kind === "weth_deposit" ? EULER_WETH_DEPOSIT_FALLBACK2 : s.kind === "approve" ? EULER_ERC20_APPROVE_FALLBACK2 : EULER_BORROW_BATCH_FALLBACK_GAS + borrowRoundsExtraGas
1055
+ }));
1056
+ const n = steps.length;
1057
+ const hasWrap = args.isNativeCollateralIn;
1058
+ const purposeSuffix = (() => {
1059
+ const tail = args.redepositBorrowedToCollateral ? `deposit collateral, enableCollateral, enableController, then ${loops.length}\xD7 borrow+redeposit on "${vaultLabel}" (same-asset target LTV loop).` : `deposit collateral, enableCollateral, enableController, borrow from "${vaultLabel}".`;
1060
+ if (hasWrap) {
1061
+ return `Euler v2: ${n}-tx batch \u2014 wrap native collateral (if needed), approve collateral eVault (buffered for all deposits), then EVC batch: ${tail}`;
1226
1062
  }
1227
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
1228
- if (legacy) {
1229
- let gasPriceWei = await publicClient.getGasPrice();
1230
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1231
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
1063
+ return `Euler v2: ${n}-tx batch \u2014 approve collateral (if needed) with buffer for all deposits, then EVC batch: ${tail}`;
1064
+ })();
1065
+ const firstDataNo0x = evmSteps[0].data.startsWith("0x") ? evmSteps[0].data.slice(2) : evmSteps[0].data;
1066
+ return buildEvmMultisignBatch({
1067
+ context: {
1068
+ chainCategory: "evm",
1069
+ keyGen: args.keyGen,
1070
+ purposeText: args.purposeText,
1071
+ chainId: args.chainId,
1072
+ rpcUrl: args.rpcUrl,
1073
+ executorAddress: executor,
1074
+ chainDetail: args.chainDetail,
1075
+ useCustomGas: args.useCustomGas,
1076
+ customGasChainDetails: args.customGasChainDetails
1077
+ },
1078
+ steps: evmSteps,
1079
+ purposeSuffix,
1080
+ firstMsgRawNo0x: firstDataNo0x,
1081
+ destinationAddress: steps[0].to,
1082
+ buildBatchMeta: ({ index }) => {
1083
+ const s = steps[index];
1084
+ if (s.kind === "weth_deposit") {
1085
+ return {
1086
+ signatureText: JSON.stringify({
1087
+ kind: "EulerV2",
1088
+ name: "WETH.deposit",
1089
+ function: "deposit()",
1090
+ valueWei: collateralWei.toString(),
1091
+ vaultMarket: vaultLabel,
1092
+ note: "Wrap native for Euler v2 isolated borrow collateral (same batch as borrow flow)."
1093
+ }),
1094
+ evm: { type: "euler_v2_weth_deposit", version: 1, chainId: String(args.chainId) },
1095
+ eulerV2: { step: "weth_deposit", vaultMarket: vaultLabel, flow: "borrow" }
1096
+ };
1232
1097
  }
1233
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
1234
- const configured = parseGwei(gweiToDecimalString3(Number(args.chainDetail.gasPrice)));
1235
- if (configured > gasPriceWei) gasPriceWei = configured;
1236
- }
1237
- const ser = serializeTransaction({
1238
- type: "legacy",
1239
- to: v.to,
1240
- data: v.data,
1241
- value: v.value,
1242
- gas: gasLimitI,
1243
- gasPrice: gasPriceWei,
1244
- nonce: currentNonce,
1245
- chainId: args.chainId
1246
- });
1247
- const h = keccak256(ser);
1248
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1249
- messageRawBatch.push(ser);
1250
- proposalTxParamsBatch.push({
1251
- nonce: currentNonce,
1252
- gasLimit: gasLimitI.toString(),
1253
- txType: "legacy",
1254
- gasPrice: gasPriceWei.toString()
1255
- });
1256
- if (i === 0) {
1257
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
1258
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1259
- }
1260
- } else {
1261
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
1262
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
1263
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
1264
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
1265
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
1266
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
1267
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
1268
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
1269
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
1270
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString3(effectivePriorityFeeGwei)) : parseGwei("1");
1271
- let maxFeePerGas = parseGwei(gweiToDecimalString3(maxFeePerGasGwei));
1272
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1273
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1274
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1275
- }
1276
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
1277
- maxFeePerGas,
1278
- maxPriorityFeePerGas,
1279
- latestBaseFeeWei
1280
- ));
1281
- const ser = serializeTransaction({
1282
- type: "eip1559",
1283
- to: v.to,
1284
- data: v.data,
1285
- value: v.value,
1286
- gas: gasLimitI,
1287
- maxFeePerGas,
1288
- maxPriorityFeePerGas,
1289
- nonce: currentNonce,
1290
- chainId: args.chainId
1291
- });
1292
- const h = keccak256(ser);
1293
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1294
- messageRawBatch.push(ser);
1295
- proposalTxParamsBatch.push({
1296
- nonce: currentNonce,
1297
- gasLimit: gasLimitI.toString(),
1298
- txType: "eip1559",
1299
- maxFeePerGas: maxFeePerGas.toString(),
1300
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1301
- });
1302
- if (i === 0) {
1303
- firstTxFeePayload = {
1304
- txNonce: currentNonce,
1305
- txGasLimit: gasLimitI.toString(),
1306
- txMaxFeePerGas: maxFeePerGas.toString(),
1307
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1098
+ if (s.kind === "approve") {
1099
+ return {
1100
+ signatureText: JSON.stringify({
1101
+ kind: "EulerV2",
1102
+ name: "ERC20.approve",
1103
+ to: "Euler collateral eVault",
1104
+ function: "approve(address spender, uint256 amount)",
1105
+ collateralVault,
1106
+ amountHuman: args.collateralAmountHuman,
1107
+ note: "Allowance for initial and follow-on collateral deposits (buffered).",
1108
+ approveTotalWei: approveTargetWei.toString()
1109
+ }),
1110
+ evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
1111
+ eulerV2: { vaultMarket: vaultLabel, flow: "borrow_collateral_approve" }
1308
1112
  };
1309
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1310
1113
  }
1311
- }
1312
- if (s.kind === "weth_deposit") {
1313
- batchMeta.push({
1314
- destinationAddress: weth,
1315
- signatureText: JSON.stringify({
1316
- kind: "EulerV2",
1317
- name: "WETH.deposit",
1318
- function: "deposit()",
1319
- valueWei: collateralWei.toString(),
1320
- vaultMarket: vaultLabel,
1321
- note: "Wrap native for Euler v2 isolated borrow collateral (same batch as borrow flow)."
1322
- }),
1323
- evm: { type: "euler_v2_weth_deposit", version: 1, chainId: String(args.chainId) },
1324
- eulerV2: { step: "weth_deposit", vaultMarket: vaultLabel, flow: "borrow" }
1325
- });
1326
- } else if (s.kind === "approve") {
1327
- batchMeta.push({
1328
- destinationAddress: s.to,
1329
- signatureText: JSON.stringify({
1330
- kind: "EulerV2",
1331
- name: "ERC20.approve",
1332
- to: "Euler collateral eVault",
1333
- function: "approve(address spender, uint256 amount)",
1334
- collateralVault,
1335
- amountHuman: args.collateralAmountHuman,
1336
- note: "Allowance for initial and follow-on collateral deposits (buffered).",
1337
- approveTotalWei: approveTargetWei.toString()
1338
- }),
1339
- evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
1340
- eulerV2: { vaultMarket: vaultLabel, flow: "borrow_collateral_approve" }
1341
- });
1342
- } else {
1343
1114
  const borrowNote = args.redepositBorrowedToCollateral ? `Same-asset leverage: ${loops.length} borrow\u2192deposit round(s); total borrow wei ${borrowWeiTotal.toString()}.` : "Deposit collateral, enableCollateral, enableController, borrow in one EVC batch.";
1344
- batchMeta.push({
1345
- destinationAddress: evc,
1115
+ return {
1346
1116
  signatureText: JSON.stringify({
1347
1117
  kind: "EulerV2",
1348
1118
  name: "EVC.batch",
@@ -1370,50 +1140,9 @@ async function buildEvmMultisignBodyEulerV2IsolatedBorrowBatch(args) {
1370
1140
  redepositBorrowedToCollateral: args.redepositBorrowedToCollateral,
1371
1141
  borrowWeiTotal: borrowWeiTotal.toString()
1372
1142
  }
1373
- });
1374
- }
1375
- }
1376
- const extraPayload = { batchMeta };
1377
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
1378
- extraPayload.customGasChainDetails = args.customGasChainDetails;
1379
- }
1380
- const extraJSON = JSON.stringify(extraPayload);
1381
- const firstSigText = batchMeta[0].signatureText;
1382
- const n = steps.length;
1383
- const hasWrap = args.isNativeCollateralIn;
1384
- const purposeSuffix = (() => {
1385
- const tail = args.redepositBorrowedToCollateral ? `deposit collateral, enableCollateral, enableController, then ${loops.length}\xD7 borrow+redeposit on "${vaultLabel}" (same-asset target LTV loop).` : `deposit collateral, enableCollateral, enableController, borrow from "${vaultLabel}".`;
1386
- if (hasWrap) {
1387
- return `Euler v2: ${n}-tx batch \u2014 wrap native collateral (if needed), approve collateral eVault (buffered for all deposits), then EVC batch: ${tail}`;
1143
+ };
1388
1144
  }
1389
- return `Euler v2: ${n}-tx batch \u2014 approve collateral (if needed) with buffer for all deposits, then EVC batch: ${tail}`;
1390
- })();
1391
- const firstValue = steps[0].value;
1392
- const bodyForSign = {
1393
- keyList,
1394
- pubKey: ph,
1395
- msgHash: messageHashes[0],
1396
- msgRaw: firstDataNo0x,
1397
- messageHashes,
1398
- messageRawBatch,
1399
- destinationChainID: String(args.chainId),
1400
- destinationAddress: steps[0].to,
1401
- extraJSON,
1402
- signatureText: firstSigText,
1403
- purpose: (() => {
1404
- const t = args.purposeText.trim();
1405
- return (t ? `${t}
1406
-
1407
- ` : "") + purposeSuffix;
1408
- })(),
1409
- ...firstTxFeePayload,
1410
- proposalTxParams: proposalTxParamsBatch
1411
- };
1412
- if (firstValue > 0n) {
1413
- bodyForSign.value = firstValue.toString();
1414
- }
1415
- if (clientId) bodyForSign.clientId = clientId;
1416
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
1145
+ });
1417
1146
  }
1418
1147
  var EULER_REPAY_BATCH_FALLBACK = 1200000n;
1419
1148
  var EULER_ERC20_APPROVE_FALLBACK3 = 100000n;
@@ -1426,21 +1155,7 @@ var evaultRepayAbi = parseAbi(["function repay(uint256 amount, address receiver)
1426
1155
  var evcAbi2 = parseAbi([
1427
1156
  "function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
1428
1157
  ]);
1429
- function gweiToDecimalString4(n) {
1430
- if (!Number.isFinite(n)) return "0";
1431
- if (n === 0) return "0";
1432
- const s = String(n);
1433
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
1434
- return s;
1435
- }
1436
- function txToViemStep3(tx) {
1437
- return { to: getAddress(tx.to), data: tx.data, value: tx.value };
1438
- }
1439
1158
  async function buildEvmMultisignBodyEulerV2BorrowRepayBatch(args) {
1440
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
1441
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1442
- const keyList = args.keyGen.keylist ?? [];
1443
- const clientId = firstClientIdFromKeyGen(args.keyGen);
1444
1159
  const evc = getAddress(args.evc);
1445
1160
  const borrowVault = getAddress(args.borrowVault);
1446
1161
  const borrowAsset = getAddress(args.borrowUnderlying);
@@ -1452,15 +1167,15 @@ async function buildEvmMultisignBodyEulerV2BorrowRepayBatch(args) {
1452
1167
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1453
1168
  rpcUrls: { default: { http: [args.rpcUrl] } }
1454
1169
  });
1455
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
1456
- const bDecRaw = await publicClient.readContract({
1170
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
1171
+ const bDecRaw = await publicClient2.readContract({
1457
1172
  address: borrowAsset,
1458
1173
  abi: erc20AllowanceAbi3,
1459
1174
  functionName: "decimals"
1460
1175
  });
1461
1176
  const borrowDecimals = Number(bDecRaw);
1462
1177
  const debtAbi = parseAbi(["function debtOf(address account) view returns (uint256)"]);
1463
- const owed = await publicClient.readContract({
1178
+ const owed = await publicClient2.readContract({
1464
1179
  address: borrowVault,
1465
1180
  abi: debtAbi,
1466
1181
  functionName: "debtOf",
@@ -1478,7 +1193,7 @@ async function buildEvmMultisignBodyEulerV2BorrowRepayBatch(args) {
1478
1193
  }
1479
1194
  const steps = [];
1480
1195
  const allowanceTarget = repayWei === maxUint256 ? owed : repayWei;
1481
- const currentAllowance = await publicClient.readContract({
1196
+ const currentAllowance = await publicClient2.readContract({
1482
1197
  address: borrowAsset,
1483
1198
  abi: erc20AllowanceAbi3,
1484
1199
  functionName: "allowance",
@@ -1515,137 +1230,49 @@ async function buildEvmMultisignBodyEulerV2BorrowRepayBatch(args) {
1515
1230
  args: [batchItems]
1516
1231
  });
1517
1232
  steps.push({ kind: "evc_batch", to: evc, data: batchData, value: 0n });
1518
- const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
1519
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
1520
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
1521
- const useCustomGas = args.useCustomGas;
1522
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
1523
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
1524
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
1525
- const messageHashes = [];
1526
- const messageRawBatch = [];
1527
- const proposalTxParamsBatch = [];
1528
- const batchMeta = [];
1529
- let firstTxFeePayload = {};
1530
- let firstDataNo0x = "";
1531
1233
  const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
1532
- for (let i = 0; i < steps.length; i++) {
1533
- const s = steps[i];
1534
- const v = txToViemStep3(s);
1535
- const currentNonce = baseNonce + i;
1536
- let estimatedGas;
1537
- try {
1538
- estimatedGas = await publicClient.estimateGas({
1539
- to: v.to,
1540
- data: v.data,
1541
- value: v.value,
1542
- account: executor
1543
- });
1544
- } catch {
1545
- estimatedGas = s.kind === "approve" ? EULER_ERC20_APPROVE_FALLBACK3 : EULER_REPAY_BATCH_FALLBACK;
1546
- }
1547
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
1548
- if (legacy) {
1549
- let gasPriceWei = await publicClient.getGasPrice();
1550
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1551
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
1552
- }
1553
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
1554
- const configured = parseGwei(gweiToDecimalString4(Number(args.chainDetail.gasPrice)));
1555
- if (configured > gasPriceWei) gasPriceWei = configured;
1556
- }
1557
- const ser = serializeTransaction({
1558
- type: "legacy",
1559
- to: v.to,
1560
- data: v.data,
1561
- value: v.value,
1562
- gas: gasLimitI,
1563
- gasPrice: gasPriceWei,
1564
- nonce: currentNonce,
1565
- chainId: args.chainId
1566
- });
1567
- const h = keccak256(ser);
1568
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1569
- messageRawBatch.push(ser);
1570
- proposalTxParamsBatch.push({
1571
- nonce: currentNonce,
1572
- gasLimit: gasLimitI.toString(),
1573
- txType: "legacy",
1574
- gasPrice: gasPriceWei.toString()
1575
- });
1576
- if (i === 0) {
1577
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
1578
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1579
- }
1580
- } else {
1581
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
1582
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
1583
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
1584
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
1585
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
1586
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
1587
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
1588
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
1589
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
1590
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString4(effectivePriorityFeeGwei)) : parseGwei("1");
1591
- let maxFeePerGas = parseGwei(gweiToDecimalString4(maxFeePerGasGwei));
1592
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1593
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1594
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1595
- }
1596
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
1597
- maxFeePerGas,
1598
- maxPriorityFeePerGas,
1599
- latestBaseFeeWei
1600
- ));
1601
- const ser = serializeTransaction({
1602
- type: "eip1559",
1603
- to: v.to,
1604
- data: v.data,
1605
- value: v.value,
1606
- gas: gasLimitI,
1607
- maxFeePerGas,
1608
- maxPriorityFeePerGas,
1609
- nonce: currentNonce,
1610
- chainId: args.chainId
1611
- });
1612
- const h = keccak256(ser);
1613
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1614
- messageRawBatch.push(ser);
1615
- proposalTxParamsBatch.push({
1616
- nonce: currentNonce,
1617
- gasLimit: gasLimitI.toString(),
1618
- txType: "eip1559",
1619
- maxFeePerGas: maxFeePerGas.toString(),
1620
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1621
- });
1622
- if (i === 0) {
1623
- firstTxFeePayload = {
1624
- txNonce: currentNonce,
1625
- txGasLimit: gasLimitI.toString(),
1626
- txMaxFeePerGas: maxFeePerGas.toString(),
1627
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1234
+ const evmSteps = steps.map((s) => ({
1235
+ to: s.to,
1236
+ data: s.data,
1237
+ value: s.value,
1238
+ fallbackGas: s.kind === "approve" ? EULER_ERC20_APPROVE_FALLBACK3 : EULER_REPAY_BATCH_FALLBACK
1239
+ }));
1240
+ const n = steps.length;
1241
+ const purposeSuffix = `Euler v2: ${n}-tx batch \u2014 repay borrow on "${vaultLabel}" (approve if needed, then EVC repay).`;
1242
+ const firstDataNo0x = evmSteps[0].data.startsWith("0x") ? evmSteps[0].data.slice(2) : evmSteps[0].data;
1243
+ return buildEvmMultisignBatch({
1244
+ context: {
1245
+ chainCategory: "evm",
1246
+ keyGen: args.keyGen,
1247
+ purposeText: args.purposeText,
1248
+ chainId: args.chainId,
1249
+ rpcUrl: args.rpcUrl,
1250
+ executorAddress: executor,
1251
+ chainDetail: args.chainDetail,
1252
+ useCustomGas: args.useCustomGas,
1253
+ customGasChainDetails: args.customGasChainDetails
1254
+ },
1255
+ steps: evmSteps,
1256
+ purposeSuffix,
1257
+ firstMsgRawNo0x: firstDataNo0x,
1258
+ destinationAddress: steps[0].to,
1259
+ buildBatchMeta: ({ index }) => {
1260
+ const s = steps[index];
1261
+ if (s.kind === "approve") {
1262
+ return {
1263
+ signatureText: JSON.stringify({
1264
+ kind: "EulerV2",
1265
+ name: "ERC20.approve",
1266
+ function: "approve(address spender, uint256 amount)",
1267
+ spender: borrowVault,
1268
+ borrowUnderlying: borrowAsset,
1269
+ note: "Allow Euler liability vault to pull assets for repay."
1270
+ }),
1271
+ evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
1272
+ eulerV2: { vaultMarket: vaultLabel, flow: "borrow_repay_approve" }
1628
1273
  };
1629
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1630
1274
  }
1631
- }
1632
- if (s.kind === "approve") {
1633
- batchMeta.push({
1634
- destinationAddress: s.to,
1635
- signatureText: JSON.stringify({
1636
- kind: "EulerV2",
1637
- name: "ERC20.approve",
1638
- function: "approve(address spender, uint256 amount)",
1639
- spender: borrowVault,
1640
- borrowUnderlying: borrowAsset,
1641
- note: "Allow Euler liability vault to pull assets for repay."
1642
- }),
1643
- evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
1644
- eulerV2: { vaultMarket: vaultLabel, flow: "borrow_repay_approve" }
1645
- });
1646
- } else {
1647
- batchMeta.push({
1648
- destinationAddress: evc,
1275
+ return {
1649
1276
  signatureText: JSON.stringify({
1650
1277
  kind: "EulerV2",
1651
1278
  name: "EVC.batch",
@@ -1666,39 +1293,9 @@ async function buildEvmMultisignBodyEulerV2BorrowRepayBatch(args) {
1666
1293
  repayAll: args.repayAll,
1667
1294
  amountHuman: args.amountHuman
1668
1295
  }
1669
- });
1296
+ };
1670
1297
  }
1671
- }
1672
- const extraPayload = { batchMeta };
1673
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
1674
- extraPayload.customGasChainDetails = args.customGasChainDetails;
1675
- }
1676
- const extraJSON = JSON.stringify(extraPayload);
1677
- const firstSigText = batchMeta[0].signatureText;
1678
- const n = steps.length;
1679
- const purposeSuffix = `Euler v2: ${n}-tx batch \u2014 repay borrow on "${vaultLabel}" (approve if needed, then EVC repay).`;
1680
- const bodyForSign = {
1681
- keyList,
1682
- pubKey: ph,
1683
- msgHash: messageHashes[0],
1684
- msgRaw: firstDataNo0x,
1685
- messageHashes,
1686
- messageRawBatch,
1687
- destinationChainID: String(args.chainId),
1688
- destinationAddress: steps[0].to,
1689
- extraJSON,
1690
- signatureText: firstSigText,
1691
- purpose: (() => {
1692
- const t = args.purposeText.trim();
1693
- return (t ? `${t}
1694
-
1695
- ` : "") + purposeSuffix;
1696
- })(),
1697
- ...firstTxFeePayload,
1698
- proposalTxParams: proposalTxParamsBatch
1699
- };
1700
- if (clientId) bodyForSign.clientId = clientId;
1701
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
1298
+ });
1702
1299
  }
1703
1300
  var EULER_COLLATERAL_DEPOSIT_BATCH_FALLBACK = 1600000n;
1704
1301
  var EULER_ERC20_APPROVE_FALLBACK4 = 100000n;
@@ -1712,21 +1309,7 @@ var erc4626DepositAbi3 = parseAbi([
1712
1309
  var evcAbi3 = parseAbi([
1713
1310
  "function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
1714
1311
  ]);
1715
- function gweiToDecimalString5(n) {
1716
- if (!Number.isFinite(n)) return "0";
1717
- if (n === 0) return "0";
1718
- const s = String(n);
1719
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
1720
- return s;
1721
- }
1722
- function txToViemStep4(tx) {
1723
- return { to: getAddress(tx.to), data: tx.data, value: tx.value };
1724
- }
1725
1312
  async function buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch(args) {
1726
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
1727
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1728
- const keyList = args.keyGen.keylist ?? [];
1729
- const clientId = firstClientIdFromKeyGen(args.keyGen);
1730
1313
  const evc = getAddress(args.evc);
1731
1314
  const collateralVault = getAddress(args.collateralVault);
1732
1315
  const collateralAsset = getAddress(args.collateralUnderlying);
@@ -1738,7 +1321,7 @@ async function buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch(args) {
1738
1321
  nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1739
1322
  rpcUrls: { default: { http: [args.rpcUrl] } }
1740
1323
  });
1741
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
1324
+ const publicClient2 = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
1742
1325
  const dec = await fetchEulerVaultAssetDecimals({
1743
1326
  rpcUrl: args.rpcUrl,
1744
1327
  chainId: args.chainId,
@@ -1747,7 +1330,7 @@ async function buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch(args) {
1747
1330
  const amountWei = parseUnits(args.amountHuman, dec);
1748
1331
  if (amountWei === 0n) throw new Error("Deposit amount is zero after converting with token decimals.");
1749
1332
  const steps = [];
1750
- const currentAllowance = await publicClient.readContract({
1333
+ const currentAllowance = await publicClient2.readContract({
1751
1334
  address: collateralAsset,
1752
1335
  abi: erc20AllowanceAbi4,
1753
1336
  functionName: "allowance",
@@ -1788,137 +1371,49 @@ async function buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch(args) {
1788
1371
  args: [batchItems]
1789
1372
  });
1790
1373
  steps.push({ kind: "evc_batch", to: evc, data: batchData, value: 0n });
1791
- const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
1792
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
1793
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
1794
- const useCustomGas = args.useCustomGas;
1795
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
1796
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
1797
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
1798
- const messageHashes = [];
1799
- const messageRawBatch = [];
1800
- const proposalTxParamsBatch = [];
1801
- const batchMeta = [];
1802
- let firstTxFeePayload = {};
1803
- let firstDataNo0x = "";
1804
1374
  const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
1805
- for (let i = 0; i < steps.length; i++) {
1806
- const s = steps[i];
1807
- const v = txToViemStep4(s);
1808
- const currentNonce = baseNonce + i;
1809
- let estimatedGas;
1810
- try {
1811
- estimatedGas = await publicClient.estimateGas({
1812
- to: v.to,
1813
- data: v.data,
1814
- value: v.value,
1815
- account: executor
1816
- });
1817
- } catch {
1818
- estimatedGas = s.kind === "approve" ? EULER_ERC20_APPROVE_FALLBACK4 : EULER_COLLATERAL_DEPOSIT_BATCH_FALLBACK;
1819
- }
1820
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
1821
- if (legacy) {
1822
- let gasPriceWei = await publicClient.getGasPrice();
1823
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1824
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
1825
- }
1826
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
1827
- const configured = parseGwei(gweiToDecimalString5(Number(args.chainDetail.gasPrice)));
1828
- if (configured > gasPriceWei) gasPriceWei = configured;
1829
- }
1830
- const ser = serializeTransaction({
1831
- type: "legacy",
1832
- to: v.to,
1833
- data: v.data,
1834
- value: v.value,
1835
- gas: gasLimitI,
1836
- gasPrice: gasPriceWei,
1837
- nonce: currentNonce,
1838
- chainId: args.chainId
1839
- });
1840
- const h = keccak256(ser);
1841
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1842
- messageRawBatch.push(ser);
1843
- proposalTxParamsBatch.push({
1844
- nonce: currentNonce,
1845
- gasLimit: gasLimitI.toString(),
1846
- txType: "legacy",
1847
- gasPrice: gasPriceWei.toString()
1848
- });
1849
- if (i === 0) {
1850
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
1851
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1852
- }
1853
- } else {
1854
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
1855
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
1856
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
1857
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
1858
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
1859
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
1860
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
1861
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
1862
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
1863
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString5(effectivePriorityFeeGwei)) : parseGwei("1");
1864
- let maxFeePerGas = parseGwei(gweiToDecimalString5(maxFeePerGasGwei));
1865
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1866
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1867
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1868
- }
1869
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
1870
- maxFeePerGas,
1871
- maxPriorityFeePerGas,
1872
- latestBaseFeeWei
1873
- ));
1874
- const ser = serializeTransaction({
1875
- type: "eip1559",
1876
- to: v.to,
1877
- data: v.data,
1878
- value: v.value,
1879
- gas: gasLimitI,
1880
- maxFeePerGas,
1881
- maxPriorityFeePerGas,
1882
- nonce: currentNonce,
1883
- chainId: args.chainId
1884
- });
1885
- const h = keccak256(ser);
1886
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1887
- messageRawBatch.push(ser);
1888
- proposalTxParamsBatch.push({
1889
- nonce: currentNonce,
1890
- gasLimit: gasLimitI.toString(),
1891
- txType: "eip1559",
1892
- maxFeePerGas: maxFeePerGas.toString(),
1893
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1894
- });
1895
- if (i === 0) {
1896
- firstTxFeePayload = {
1897
- txNonce: currentNonce,
1898
- txGasLimit: gasLimitI.toString(),
1899
- txMaxFeePerGas: maxFeePerGas.toString(),
1900
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1375
+ const evmSteps = steps.map((s) => ({
1376
+ to: s.to,
1377
+ data: s.data,
1378
+ value: s.value,
1379
+ fallbackGas: s.kind === "approve" ? EULER_ERC20_APPROVE_FALLBACK4 : EULER_COLLATERAL_DEPOSIT_BATCH_FALLBACK
1380
+ }));
1381
+ const n = steps.length;
1382
+ const purposeSuffix = `Euler v2: ${n}-tx batch \u2014 deposit ${args.amountHuman} collateral into "${vaultLabel}" (approve if needed, then EVC deposit).`;
1383
+ const firstDataNo0x = evmSteps[0].data.startsWith("0x") ? evmSteps[0].data.slice(2) : evmSteps[0].data;
1384
+ return buildEvmMultisignBatch({
1385
+ context: {
1386
+ chainCategory: "evm",
1387
+ keyGen: args.keyGen,
1388
+ purposeText: args.purposeText,
1389
+ chainId: args.chainId,
1390
+ rpcUrl: args.rpcUrl,
1391
+ executorAddress: executor,
1392
+ chainDetail: args.chainDetail,
1393
+ useCustomGas: args.useCustomGas,
1394
+ customGasChainDetails: args.customGasChainDetails
1395
+ },
1396
+ steps: evmSteps,
1397
+ purposeSuffix,
1398
+ firstMsgRawNo0x: firstDataNo0x,
1399
+ destinationAddress: steps[0].to,
1400
+ buildBatchMeta: ({ index }) => {
1401
+ const s = steps[index];
1402
+ if (s.kind === "approve") {
1403
+ return {
1404
+ signatureText: JSON.stringify({
1405
+ kind: "EulerV2",
1406
+ name: "ERC20.approve",
1407
+ function: "approve(address spender, uint256 amount)",
1408
+ spender: collateralVault,
1409
+ collateralUnderlying: collateralAsset,
1410
+ note: "Allow Euler collateral eVault to pull assets for collateral deposit."
1411
+ }),
1412
+ evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
1413
+ eulerV2: { vaultMarket: vaultLabel, flow: "borrow_collateral_deposit_approve" }
1901
1414
  };
1902
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1903
1415
  }
1904
- }
1905
- if (s.kind === "approve") {
1906
- batchMeta.push({
1907
- destinationAddress: s.to,
1908
- signatureText: JSON.stringify({
1909
- kind: "EulerV2",
1910
- name: "ERC20.approve",
1911
- function: "approve(address spender, uint256 amount)",
1912
- spender: collateralVault,
1913
- collateralUnderlying: collateralAsset,
1914
- note: "Allow Euler collateral eVault to pull assets for collateral deposit."
1915
- }),
1916
- evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
1917
- eulerV2: { vaultMarket: vaultLabel, flow: "borrow_collateral_deposit_approve" }
1918
- });
1919
- } else {
1920
- batchMeta.push({
1921
- destinationAddress: evc,
1416
+ return {
1922
1417
  signatureText: JSON.stringify({
1923
1418
  kind: "EulerV2",
1924
1419
  name: "EVC.batch",
@@ -1938,39 +1433,9 @@ async function buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch(args) {
1938
1433
  subAccount,
1939
1434
  amountHuman: args.amountHuman
1940
1435
  }
1941
- });
1436
+ };
1942
1437
  }
1943
- }
1944
- const extraPayload = { batchMeta };
1945
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
1946
- extraPayload.customGasChainDetails = args.customGasChainDetails;
1947
- }
1948
- const extraJSON = JSON.stringify(extraPayload);
1949
- const firstSigText = batchMeta[0].signatureText;
1950
- const n = steps.length;
1951
- const purposeSuffix = `Euler v2: ${n}-tx batch \u2014 deposit ${args.amountHuman} collateral into "${vaultLabel}" (approve if needed, then EVC deposit).`;
1952
- const bodyForSign = {
1953
- keyList,
1954
- pubKey: ph,
1955
- msgHash: messageHashes[0],
1956
- msgRaw: firstDataNo0x,
1957
- messageHashes,
1958
- messageRawBatch,
1959
- destinationChainID: String(args.chainId),
1960
- destinationAddress: steps[0].to,
1961
- extraJSON,
1962
- signatureText: firstSigText,
1963
- purpose: (() => {
1964
- const t = args.purposeText.trim();
1965
- return (t ? `${t}
1966
-
1967
- ` : "") + purposeSuffix;
1968
- })(),
1969
- ...firstTxFeePayload,
1970
- proposalTxParams: proposalTxParamsBatch
1971
- };
1972
- if (clientId) bodyForSign.clientId = clientId;
1973
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
1438
+ });
1974
1439
  }
1975
1440
  var EULER_COLLATERAL_WITHDRAW_BATCH_FALLBACK = 1400000n;
1976
1441
  var erc4626WithdrawAbi3 = parseAbi([
@@ -1979,33 +1444,12 @@ var erc4626WithdrawAbi3 = parseAbi([
1979
1444
  var evcAbi4 = parseAbi([
1980
1445
  "function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
1981
1446
  ]);
1982
- function gweiToDecimalString6(n) {
1983
- if (!Number.isFinite(n)) return "0";
1984
- if (n === 0) return "0";
1985
- const s = String(n);
1986
- if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
1987
- return s;
1988
- }
1989
- function txToViemStep5(tx) {
1990
- return { to: getAddress(tx.to), data: tx.data, value: tx.value };
1991
- }
1992
1447
  async function buildEvmMultisignBodyEulerV2BorrowCollateralWithdrawBatch(args) {
1993
- const ph = (args.keyGen.pubkeyhex ?? "").trim();
1994
- if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1995
- const keyList = args.keyGen.keylist ?? [];
1996
- const clientId = firstClientIdFromKeyGen(args.keyGen);
1997
1448
  const evc = getAddress(args.evc);
1998
1449
  const collateralVault = getAddress(args.collateralVault);
1999
1450
  const subAccount = getAddress(args.subAccount);
2000
1451
  const receiver = getAddress(args.receiver);
2001
1452
  const executor = getAddress(args.executorAddress);
2002
- const ch = defineChain({
2003
- id: args.chainId,
2004
- name: "EulerColWithdraw",
2005
- nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
2006
- rpcUrls: { default: { http: [args.rpcUrl] } }
2007
- });
2008
- const publicClient = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
2009
1453
  const dec = await fetchEulerVaultAssetDecimals({ rpcUrl: args.rpcUrl, chainId: args.chainId, evault: collateralVault });
2010
1454
  const amountWei = parseUnits(args.amountHuman, dec);
2011
1455
  if (amountWei === 0n) throw new Error("Withdraw amount is zero after converting with token decimals.");
@@ -2041,122 +1485,29 @@ async function buildEvmMultisignBodyEulerV2BorrowCollateralWithdrawBatch(args) {
2041
1485
  args: [batchItems]
2042
1486
  });
2043
1487
  const steps = [{ kind: "evc_batch", to: evc, data: batchData, value: 0n }];
2044
- const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
2045
- const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
2046
- const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
2047
- const useCustomGas = args.useCustomGas;
2048
- const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
2049
- const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
2050
- const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
2051
- const messageHashes = [];
2052
- const messageRawBatch = [];
2053
- const proposalTxParamsBatch = [];
2054
- const batchMeta = [];
2055
- let firstTxFeePayload = {};
2056
- let firstDataNo0x = "";
2057
1488
  const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
2058
- for (let i = 0; i < steps.length; i++) {
2059
- const s = steps[i];
2060
- const v = txToViemStep5(s);
2061
- const currentNonce = baseNonce + i;
2062
- let estimatedGas;
2063
- try {
2064
- estimatedGas = await publicClient.estimateGas({
2065
- to: v.to,
2066
- data: v.data,
2067
- value: v.value,
2068
- account: executor
2069
- });
2070
- } catch {
2071
- estimatedGas = EULER_COLLATERAL_WITHDRAW_BATCH_FALLBACK;
2072
- }
2073
- const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
2074
- if (legacy) {
2075
- let gasPriceWei = await publicClient.getGasPrice();
2076
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
2077
- gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
2078
- }
2079
- if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
2080
- const configured = parseGwei(gweiToDecimalString6(Number(args.chainDetail.gasPrice)));
2081
- if (configured > gasPriceWei) gasPriceWei = configured;
2082
- }
2083
- const ser = serializeTransaction({
2084
- type: "legacy",
2085
- to: v.to,
2086
- data: v.data,
2087
- value: v.value,
2088
- gas: gasLimitI,
2089
- gasPrice: gasPriceWei,
2090
- nonce: currentNonce,
2091
- chainId: args.chainId
2092
- });
2093
- const h = keccak256(ser);
2094
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
2095
- messageRawBatch.push(ser);
2096
- proposalTxParamsBatch.push({
2097
- nonce: currentNonce,
2098
- gasLimit: gasLimitI.toString(),
2099
- txType: "legacy",
2100
- gasPrice: gasPriceWei.toString()
2101
- });
2102
- if (i === 0) {
2103
- firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
2104
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
2105
- }
2106
- } else {
2107
- const fetchedBase = feeParams.baseFeeGwei ?? 0;
2108
- const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
2109
- const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
2110
- const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
2111
- const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
2112
- const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
2113
- const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
2114
- const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
2115
- const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
2116
- let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString6(effectivePriorityFeeGwei)) : parseGwei("1");
2117
- let maxFeePerGas = parseGwei(gweiToDecimalString6(maxFeePerGasGwei));
2118
- if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
2119
- maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
2120
- maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
2121
- }
2122
- ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
2123
- maxFeePerGas,
2124
- maxPriorityFeePerGas,
2125
- latestBaseFeeWei
2126
- ));
2127
- const ser = serializeTransaction({
2128
- type: "eip1559",
2129
- to: v.to,
2130
- data: v.data,
2131
- value: v.value,
2132
- gas: gasLimitI,
2133
- maxFeePerGas,
2134
- maxPriorityFeePerGas,
2135
- nonce: currentNonce,
2136
- chainId: args.chainId
2137
- });
2138
- const h = keccak256(ser);
2139
- messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
2140
- messageRawBatch.push(ser);
2141
- proposalTxParamsBatch.push({
2142
- nonce: currentNonce,
2143
- gasLimit: gasLimitI.toString(),
2144
- txType: "eip1559",
2145
- maxFeePerGas: maxFeePerGas.toString(),
2146
- maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
2147
- });
2148
- if (i === 0) {
2149
- firstTxFeePayload = {
2150
- txNonce: currentNonce,
2151
- txGasLimit: gasLimitI.toString(),
2152
- txMaxFeePerGas: maxFeePerGas.toString(),
2153
- txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
2154
- };
2155
- firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
2156
- }
2157
- }
2158
- batchMeta.push({
2159
- destinationAddress: evc,
1489
+ const purposeSuffix = `Euler v2: 1-tx \u2014 withdraw ${args.amountHuman} collateral from "${vaultLabel}" via EVC (sub-account).`;
1490
+ const firstDataNo0x = batchData.startsWith("0x") ? batchData.slice(2) : batchData;
1491
+ const evmSteps = [
1492
+ { to: evc, data: batchData, value: 0n, fallbackGas: EULER_COLLATERAL_WITHDRAW_BATCH_FALLBACK }
1493
+ ];
1494
+ return buildEvmMultisignBatch({
1495
+ context: {
1496
+ chainCategory: "evm",
1497
+ keyGen: args.keyGen,
1498
+ purposeText: args.purposeText,
1499
+ chainId: args.chainId,
1500
+ rpcUrl: args.rpcUrl,
1501
+ executorAddress: executor,
1502
+ chainDetail: args.chainDetail,
1503
+ useCustomGas: args.useCustomGas,
1504
+ customGasChainDetails: args.customGasChainDetails
1505
+ },
1506
+ steps: evmSteps,
1507
+ purposeSuffix,
1508
+ firstMsgRawNo0x: firstDataNo0x,
1509
+ destinationAddress: steps[0].to,
1510
+ buildBatchMeta: () => ({
2160
1511
  signatureText: JSON.stringify({
2161
1512
  kind: "EulerV2",
2162
1513
  name: "EVC.batch",
@@ -2178,37 +1529,2255 @@ async function buildEvmMultisignBodyEulerV2BorrowCollateralWithdrawBatch(args) {
2178
1529
  receiver,
2179
1530
  amountHuman: args.amountHuman
2180
1531
  }
1532
+ })
1533
+ });
1534
+ }
1535
+
1536
+ // src/core/defiProxy.ts
1537
+ var aaveGraphqlProxyUrl;
1538
+ var eulerGraphqlProxyUrl;
1539
+ var coingeckoProxyUrl;
1540
+ function getAaveGraphqlProxyUrl() {
1541
+ return aaveGraphqlProxyUrl;
1542
+ }
1543
+ function getEulerGraphqlProxyUrl() {
1544
+ return eulerGraphqlProxyUrl;
1545
+ }
1546
+ function getCoingeckoProxyUrl() {
1547
+ return coingeckoProxyUrl;
1548
+ }
1549
+ async function postJsonViaOptionalProxy(args) {
1550
+ const proxy = args.proxyUrl?.trim();
1551
+ if (proxy) {
1552
+ const r2 = await fetch(proxy, {
1553
+ method: "POST",
1554
+ headers: { "content-type": "application/json" },
1555
+ body: JSON.stringify(args.proxyEnvelope ?? args.body)
2181
1556
  });
1557
+ if (!r2.ok) {
1558
+ const t = await r2.text().catch(() => "");
1559
+ throw new Error(t ? `Proxy HTTP ${r2.status}: ${t.slice(0, 200)}` : `Proxy HTTP ${r2.status}`);
1560
+ }
1561
+ return await r2.json();
2182
1562
  }
2183
- const extraPayload = { batchMeta };
2184
- if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
2185
- extraPayload.customGasChainDetails = args.customGasChainDetails;
1563
+ const r = await fetch(args.directUrl, {
1564
+ method: "POST",
1565
+ headers: { "content-type": "application/json" },
1566
+ body: JSON.stringify(args.body)
1567
+ });
1568
+ if (!r.ok) {
1569
+ const t = await r.text().catch(() => "");
1570
+ throw new Error(t ? `HTTP ${r.status}: ${t.slice(0, 200)}` : `HTTP ${r.status}`);
2186
1571
  }
2187
- const extraJSON = JSON.stringify(extraPayload);
2188
- const firstSigText = batchMeta[0].signatureText;
2189
- const purposeSuffix = `Euler v2: 1-tx \u2014 withdraw ${args.amountHuman} collateral from "${vaultLabel}" via EVC (sub-account).`;
2190
- const bodyForSign = {
2191
- keyList,
2192
- pubKey: ph,
2193
- msgHash: messageHashes[0],
2194
- msgRaw: firstDataNo0x,
2195
- messageHashes,
2196
- messageRawBatch,
2197
- destinationChainID: String(args.chainId),
2198
- destinationAddress: steps[0].to,
2199
- extraJSON,
2200
- signatureText: firstSigText,
2201
- purpose: (() => {
2202
- const t = args.purposeText.trim();
2203
- return (t ? `${t}
1572
+ return await r.json();
1573
+ }
2204
1574
 
2205
- ` : "") + purposeSuffix;
2206
- })(),
2207
- ...firstTxFeePayload,
2208
- proposalTxParams: proposalTxParamsBatch
1575
+ // src/protocols/evm/aave-v4/api.ts
1576
+ var AAVE_V4_GRAPHQL_URL = "https://api.v4.aave.com/graphql";
1577
+ async function aaveV4Gql(query, variables) {
1578
+ const body = { query, variables: variables ?? {} };
1579
+ const j = await postJsonViaOptionalProxy({
1580
+ directUrl: AAVE_V4_GRAPHQL_URL,
1581
+ body,
1582
+ proxyUrl: getAaveGraphqlProxyUrl(),
1583
+ proxyEnvelope: body
1584
+ });
1585
+ if (j.errors?.length) {
1586
+ const msg = j.errors.map((e) => e.message ?? "Unknown").join("; ");
1587
+ throw new Error(msg);
1588
+ }
1589
+ if (j.data == null) {
1590
+ throw new Error("Aave V4 API: empty response");
1591
+ }
1592
+ return j.data;
1593
+ }
1594
+ async function fetchAaveV4Chains() {
1595
+ const d = await aaveV4Gql(`
1596
+ query C($c: ChainsRequest!) { chains(request: $c) { chainId name nativeWrappedToken } }
1597
+ `, { c: { query: { filter: "ALL" } } });
1598
+ return d.chains ?? [];
1599
+ }
1600
+ async function fetchAaveV4NativeWrappedToken(chainId) {
1601
+ const all = await fetchAaveV4Chains();
1602
+ const c = all.find((x) => x.chainId === chainId);
1603
+ const t = (c?.nativeWrappedToken ?? "").trim();
1604
+ if (t && isAddress(t)) return getAddress(t);
1605
+ return null;
1606
+ }
1607
+ var EULER_EVAULT_CAP_UNLIMITED_WEI = (1n << 256n) - 1n;
1608
+ var UINT256_MAX = EULER_EVAULT_CAP_UNLIMITED_WEI;
1609
+ var EULER_EVAULT_SUPPLY_CAP_UNLIMITED_WEI = EULER_EVAULT_CAP_UNLIMITED_WEI;
1610
+ var EULER_V2_PREFER_ON_CHAIN_LEND_METRICS = false;
1611
+ var eulerVaultLendAbi = parseAbi([
1612
+ "function caps() view returns (uint16 supplyCap, uint16 borrowCap)",
1613
+ "function cash() view returns (uint256)",
1614
+ "function totalBorrows() view returns (uint256)",
1615
+ "function totalAssets() view returns (uint256)"
1616
+ ]);
1617
+ function resolveEulerAmountCapToWei(rawCap16) {
1618
+ const amountCap = BigInt(rawCap16) & 0xffffn;
1619
+ if (amountCap === 0n) return UINT256_MAX;
1620
+ const exp = amountCap & 63n;
1621
+ const mantissa = amountCap >> 6n;
1622
+ return 10n ** exp * mantissa / 100n;
1623
+ }
1624
+ var resolveEulerAmountCapToSupplyWei = resolveEulerAmountCapToWei;
1625
+ function eulerSubgraphVaultCapToUnderlyingWei(raw) {
1626
+ try {
1627
+ const v = BigInt((raw ?? "").trim() || "0");
1628
+ if (v >= UINT256_MAX - 3n) return v;
1629
+ if (v <= 0xffffn) return resolveEulerAmountCapToWei(v);
1630
+ return v;
1631
+ } catch {
1632
+ return 0n;
1633
+ }
1634
+ }
1635
+ function eulerVaultLendMetricsFromIndexerVaultState(args) {
1636
+ let rawSupplyCap16 = 0;
1637
+ let rawBorrowCap16 = 0;
1638
+ try {
1639
+ rawSupplyCap16 = Number(BigInt((args.supplyCapRaw ?? "").trim() || "0") & 0xffffn);
1640
+ } catch {
1641
+ rawSupplyCap16 = 0;
1642
+ }
1643
+ try {
1644
+ rawBorrowCap16 = Number(BigInt((args.borrowCapRaw ?? "").trim() || "0") & 0xffffn);
1645
+ } catch {
1646
+ rawBorrowCap16 = 0;
1647
+ }
1648
+ return {
1649
+ rawSupplyCap16,
1650
+ rawBorrowCap16,
1651
+ supplyCapWei: eulerSubgraphVaultCapToUnderlyingWei(args.supplyCapRaw),
1652
+ borrowCapWei: eulerSubgraphVaultCapToUnderlyingWei(args.borrowCapRaw),
1653
+ cashWei: args.cashWei,
1654
+ totalBorrowsWei: args.totalBorrowsWei,
1655
+ totalAssetsWei: args.totalAssetsWei
2209
1656
  };
2210
- if (clientId) bodyForSign.clientId = clientId;
2211
- return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
1657
+ }
1658
+ function formatEulerResolvedCapOrCashForUi(wei, decimals, symbolSuffix) {
1659
+ if (wei >= EULER_EVAULT_CAP_UNLIMITED_WEI - 3n) return "Unlimited";
1660
+ if (wei === 0n) return symbolSuffix ? `0 ${symbolSuffix}` : "0";
1661
+ try {
1662
+ const s = formatUnits(wei, decimals);
1663
+ const n = Number(s);
1664
+ if (Number.isFinite(n) && Math.abs(n) >= 1e9) return `${n.toExponential(2)}${symbolSuffix ? ` ${symbolSuffix}` : ""}`;
1665
+ return symbolSuffix ? `${s} ${symbolSuffix}` : s;
1666
+ } catch {
1667
+ return wei.toString();
1668
+ }
1669
+ }
1670
+ function eulerVaultMaxNewSupplyWei(metrics) {
1671
+ const { supplyCapWei, totalAssetsWei } = metrics;
1672
+ if (supplyCapWei === 0n) return 0n;
1673
+ if (supplyCapWei >= EULER_EVAULT_CAP_UNLIMITED_WEI - 3n) return EULER_EVAULT_CAP_UNLIMITED_WEI;
1674
+ if (totalAssetsWei >= supplyCapWei) return 0n;
1675
+ return supplyCapWei - totalAssetsWei;
1676
+ }
1677
+ function eulerVaultMaxNewBorrowWei(metrics) {
1678
+ const { cashWei, borrowCapWei, totalBorrowsWei } = metrics;
1679
+ if (borrowCapWei === 0n) return 0n;
1680
+ if (borrowCapWei >= EULER_EVAULT_CAP_UNLIMITED_WEI - 3n) return cashWei;
1681
+ const capLeft = borrowCapWei > totalBorrowsWei ? borrowCapWei - totalBorrowsWei : 0n;
1682
+ return cashWei < capLeft ? cashWei : capLeft;
1683
+ }
1684
+ async function fetchEulerVaultOnChainLendMetrics(args) {
1685
+ const ch = defineChain({
1686
+ id: args.chainId,
1687
+ name: "EulerVaultRead",
1688
+ nativeCurrency: { decimals: 18, name: "x", symbol: "x" },
1689
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1690
+ });
1691
+ const client = createPublicClient({ chain: ch, transport: http(args.rpcUrl) });
1692
+ const addr = getAddress(args.evault);
1693
+ const [[supplyRaw, borrowRaw], cashWei, totalBorrowsWei, totalAssetsWei] = await Promise.all([
1694
+ client.readContract({
1695
+ address: addr,
1696
+ abi: eulerVaultLendAbi,
1697
+ functionName: "caps"
1698
+ }),
1699
+ client.readContract({
1700
+ address: addr,
1701
+ abi: eulerVaultLendAbi,
1702
+ functionName: "cash"
1703
+ }),
1704
+ client.readContract({
1705
+ address: addr,
1706
+ abi: eulerVaultLendAbi,
1707
+ functionName: "totalBorrows"
1708
+ }),
1709
+ client.readContract({
1710
+ address: addr,
1711
+ abi: eulerVaultLendAbi,
1712
+ functionName: "totalAssets"
1713
+ })
1714
+ ]);
1715
+ const rs = Number(supplyRaw);
1716
+ const rb = Number(borrowRaw);
1717
+ return {
1718
+ rawSupplyCap16: rs,
1719
+ rawBorrowCap16: rb,
1720
+ supplyCapWei: resolveEulerAmountCapToWei(rs),
1721
+ borrowCapWei: resolveEulerAmountCapToWei(rb),
1722
+ cashWei,
1723
+ totalBorrowsWei,
1724
+ totalAssetsWei
1725
+ };
1726
+ }
1727
+ async function fetchEulerVaultOnChainLendMetricsForVaults(args) {
1728
+ const rpc = (args.rpcUrl ?? "").trim();
1729
+ const out = /* @__PURE__ */ new Map();
1730
+ if (!rpc || !args.vaults.length) return out;
1731
+ const concurrency = 8;
1732
+ let idx = 0;
1733
+ const list = [...args.vaults];
1734
+ async function worker() {
1735
+ while (idx < list.length) {
1736
+ const i = idx++;
1737
+ const row = list[i];
1738
+ try {
1739
+ const ev = getAddress(row.evault.trim());
1740
+ const data = await fetchEulerVaultOnChainLendMetrics({
1741
+ rpcUrl: rpc,
1742
+ chainId: args.chainId,
1743
+ evault: ev
1744
+ });
1745
+ out.set(row.key, { ok: true, data });
1746
+ } catch (e) {
1747
+ out.set(row.key, {
1748
+ ok: false,
1749
+ message: e instanceof Error ? e.message : "On-chain read failed"
1750
+ });
1751
+ }
1752
+ }
1753
+ }
1754
+ await Promise.all(Array.from({ length: Math.min(concurrency, list.length) }, () => worker()));
1755
+ return out;
1756
+ }
1757
+
1758
+ // src/protocols/evm/euler-v2/eulerV2Subgraph.ts
1759
+ var EULER_V2_GOLDSKY_SUBGRAPH_URL_BY_CHAIN_ID = {
1760
+ 1: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-mainnet/latest/gn",
1761
+ 8453: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-base/latest/gn",
1762
+ 1923: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-swell/latest/gn",
1763
+ 146: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-sonic/latest/gn",
1764
+ 60808: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-bob/latest/gn",
1765
+ 80094: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-berachain/latest/gn",
1766
+ 43114: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-avalanche/latest/gn",
1767
+ 42161: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-arbitrum/latest/gn",
1768
+ 130: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-unichain/latest/gn",
1769
+ 57073: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-ink/latest/gn",
1770
+ 56: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-bsc/latest/gn",
1771
+ 999: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-hyperevm/latest/gn",
1772
+ 10: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-optimism/latest/gn",
1773
+ 100: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-gnosis/latest/gn",
1774
+ 480: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-worldchain/latest/gn",
1775
+ 239: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-tac/latest/gn",
1776
+ 9745: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-plasma/latest/gn",
1777
+ 5e3: "https://api.goldsky.com/api/public/project_cm4iagnemt1wp01xn4gh1agft/subgraphs/euler-v2-mantle/latest/gn"
1778
+ };
1779
+ var WRAPPED_NATIVE_FALLBACK = {
1780
+ 1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
1781
+ 10: "0x4200000000000000000000000000000000000006",
1782
+ 56: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c",
1783
+ 100: "0xe91d153e0b41518a2ce8dd3d7944fa863463a97d",
1784
+ 130: "0x4200000000000000000000000000000000000006",
1785
+ 146: "0x03980325872071166574bcd94670450917897468",
1786
+ 239: "0xB63B9f0eb4A6E6f191529D71d4D88cc8900Df2C9",
1787
+ // TAC: WTAC (https://docs.tac.build/ecosystem/token-list)
1788
+ 480: "0x4200000000000000000000000000000000000006",
1789
+ 999: "0x5555555555555555555555555555555555555555",
1790
+ // HyperEVM: WHYPE (Hyperliquid docs)
1791
+ 1923: "0x4200000000000000000000000000000000000006",
1792
+ 42161: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
1793
+ 43114: "0xB31f66AA3C1e785363F0875A1B74D27b85FE0459",
1794
+ 5e3: "0x78c1b0C915c4FAA5FffA6CAbf0219DA63d7f4cb8",
1795
+ // Mantle: WMNT
1796
+ 57073: "0x4200000000000000000000000000000000000006",
1797
+ 60808: "0x4200000000000000000000000000000000000006",
1798
+ 80094: "0x6969696969696969696969696969696969696969",
1799
+ // Berachain: WBERA (https://docs.berachain.com)
1800
+ 8453: "0x4200000000000000000000000000000000000006",
1801
+ 9745: "0x6100E367285b01F48D07953803A2d8dCA5D19873"
1802
+ // Plasma: WXPL
1803
+ };
1804
+ for (const id of Object.keys(EULER_V2_GOLDSKY_SUBGRAPH_URL_BY_CHAIN_ID).map(Number)) {
1805
+ if (WRAPPED_NATIVE_FALLBACK[id] == null) {
1806
+ throw new Error(`eulerV2Subgraph: add WRAPPED_NATIVE_FALLBACK for Euler Goldsky chain ${id}`);
1807
+ }
1808
+ }
1809
+ function eulerV2GoldskyUrlForChain(chainId) {
1810
+ return EULER_V2_GOLDSKY_SUBGRAPH_URL_BY_CHAIN_ID[chainId];
1811
+ }
1812
+ async function resolveEulerWrappedNativeToken(chainId) {
1813
+ try {
1814
+ const fromAave = await fetchAaveV4NativeWrappedToken(chainId);
1815
+ if (fromAave) return fromAave;
1816
+ } catch {
1817
+ }
1818
+ const fb = WRAPPED_NATIVE_FALLBACK[chainId];
1819
+ if (fb && isAddress(fb)) return getAddress(fb);
1820
+ return null;
1821
+ }
1822
+ async function eulerWrappedGasTokenAliasesLower(chainId) {
1823
+ const s = /* @__PURE__ */ new Set();
1824
+ const fb = WRAPPED_NATIVE_FALLBACK[chainId];
1825
+ if (fb && isAddress(fb)) {
1826
+ try {
1827
+ s.add(getAddress(fb).toLowerCase());
1828
+ } catch {
1829
+ }
1830
+ }
1831
+ try {
1832
+ const aave = await fetchAaveV4NativeWrappedToken(chainId);
1833
+ if (aave && isAddress(aave)) s.add(getAddress(aave).toLowerCase());
1834
+ } catch {
1835
+ }
1836
+ return s;
1837
+ }
1838
+ function eulerV2KeyForNodeAssetRow(args) {
1839
+ const raw = (args.contractAddress ?? "").trim();
1840
+ try {
1841
+ const a = getAddress(raw);
1842
+ if (a.toLowerCase() === "0x0000000000000000000000000000000000000000" && args.nativeWrapped) {
1843
+ return getAddress(args.nativeWrapped.trim()).toLowerCase();
1844
+ }
1845
+ return a.toLowerCase();
1846
+ } catch {
1847
+ return null;
1848
+ }
1849
+ }
1850
+ async function eulerSubgraphGql(endpoint, query, variables, chainId) {
1851
+ const body = { query, variables: variables ?? {} };
1852
+ const proxy = getEulerGraphqlProxyUrl();
1853
+ const j = await postJsonViaOptionalProxy({
1854
+ directUrl: endpoint,
1855
+ body,
1856
+ proxyUrl: proxy,
1857
+ proxyEnvelope: chainId != null ? { chainId, query, variables: variables ?? {} } : body
1858
+ });
1859
+ if (j.errors?.length) {
1860
+ throw new Error(j.errors.map((e) => e.message ?? "Unknown").join("; "));
1861
+ }
1862
+ if (j.data == null) throw new Error("Euler subgraph: empty response");
1863
+ return j.data;
1864
+ }
1865
+ async function eulerV2QueryGraphQl(chainId, query, variables) {
1866
+ const endpoint = eulerV2GoldskyUrlForChain(chainId);
1867
+ if (!endpoint) {
1868
+ throw new Error(`Euler v2 subgraph is not configured for chain ${chainId}`);
1869
+ }
1870
+ return eulerSubgraphGql(endpoint, query, variables, chainId);
1871
+ }
1872
+ var EULER_TRACKING_DEPOSITS_GQL = `
1873
+ query EulerTrackingDeposits($id: Bytes!) {
1874
+ trackingActiveAccount(id: $id) {
1875
+ deposits
1876
+ }
1877
+ }`;
1878
+ async function fetchEulerV2TrackingDepositEntries(args) {
1879
+ if (!eulerV2GoldskyUrlForChain(args.chainId)) return [];
1880
+ const id = args.mainAddress.toLowerCase();
1881
+ const data = await eulerV2QueryGraphQl(
1882
+ args.chainId,
1883
+ EULER_TRACKING_DEPOSITS_GQL,
1884
+ { id }
1885
+ );
1886
+ const raw = data.trackingActiveAccount?.deposits ?? [];
1887
+ return raw.filter((x) => typeof x === "string" && x.trim().length > 0);
1888
+ }
1889
+ function normAssetAddr(raw) {
1890
+ const s = (raw ?? "").trim();
1891
+ if (!s || !isAddress(s)) return null;
1892
+ return getAddress(s).toLowerCase();
1893
+ }
1894
+ function vaultShowsBorrowTab(borrowCap, totalBorrows) {
1895
+ let borrows = 0n;
1896
+ try {
1897
+ borrows = BigInt((totalBorrows ?? "").trim() || "0");
1898
+ } catch {
1899
+ borrows = 0n;
1900
+ }
1901
+ if (borrows > 0n) return true;
1902
+ const w = eulerSubgraphVaultCapToUnderlyingWei(borrowCap);
1903
+ if (w >= EULER_EVAULT_CAP_UNLIMITED_WEI - 3n) return true;
1904
+ return w > 0n;
1905
+ }
1906
+ var VAULT_PAGE = `
1907
+ query V($first: Int!, $skip: Int!) {
1908
+ eulerVaults(first: $first, skip: $skip) {
1909
+ asset
1910
+ borrowCap
1911
+ state { totalBorrows }
1912
+ }
1913
+ }
1914
+ `;
1915
+ var EARN_PAGE = `
1916
+ query E($first: Int!, $skip: Int!) {
1917
+ eulerEarnVaults(first: $first, skip: $skip) {
1918
+ asset
1919
+ }
1920
+ }
1921
+ `;
1922
+ async function paginateEulerVaultRows(endpoint) {
1923
+ const out = [];
1924
+ let skip = 0;
1925
+ const first = 1e3;
1926
+ while (true) {
1927
+ const d = await eulerSubgraphGql(endpoint, VAULT_PAGE, { first, skip });
1928
+ const rows = d.eulerVaults ?? [];
1929
+ if (!rows.length) break;
1930
+ for (const r of rows) {
1931
+ const tb = (r.state?.totalBorrows ?? "0").toString();
1932
+ out.push({ asset: r.asset ?? null, borrowCap: r.borrowCap, totalBorrows: tb });
1933
+ }
1934
+ if (rows.length < first) break;
1935
+ skip += first;
1936
+ }
1937
+ return out;
1938
+ }
1939
+ async function paginateEulerEarnAssets(endpoint) {
1940
+ const assets = [];
1941
+ let skip = 0;
1942
+ const first = 1e3;
1943
+ while (true) {
1944
+ const d = await eulerSubgraphGql(endpoint, EARN_PAGE, {
1945
+ first,
1946
+ skip
1947
+ });
1948
+ const rows = d.eulerEarnVaults ?? [];
1949
+ if (!rows.length) break;
1950
+ for (const r of rows) {
1951
+ const a = normAssetAddr(r.asset ?? void 0);
1952
+ if (a) assets.push(a);
1953
+ }
1954
+ if (rows.length < first) break;
1955
+ skip += first;
1956
+ }
1957
+ return assets;
1958
+ }
1959
+ var eulerV2ChainAssetCache = /* @__PURE__ */ new Map();
1960
+ async function fetchEulerV2ChainAssetCache(chainId) {
1961
+ const endpoint = eulerV2GoldskyUrlForChain(chainId);
1962
+ if (!endpoint) {
1963
+ return { nativeWrapped: null, modesByUnderlying: /* @__PURE__ */ new Map() };
1964
+ }
1965
+ const [nativeWrapped, vaultRows, earnAssets] = await Promise.all([
1966
+ resolveEulerWrappedNativeToken(chainId),
1967
+ paginateEulerVaultRows(endpoint),
1968
+ paginateEulerEarnAssets(endpoint)
1969
+ ]);
1970
+ const modes = /* @__PURE__ */ new Map();
1971
+ const bump = (addr, patch) => {
1972
+ const cur = modes.get(addr) ?? { lend: false, borrow: false, earn: false };
1973
+ modes.set(addr, {
1974
+ lend: cur.lend || !!patch.lend,
1975
+ borrow: cur.borrow || !!patch.borrow,
1976
+ earn: cur.earn || !!patch.earn
1977
+ });
1978
+ };
1979
+ for (const e of earnAssets) bump(e, { earn: true });
1980
+ for (const row of vaultRows) {
1981
+ const a = normAssetAddr(row.asset);
1982
+ if (!a) continue;
1983
+ bump(a, {
1984
+ lend: true,
1985
+ borrow: vaultShowsBorrowTab(row.borrowCap, row.totalBorrows)
1986
+ });
1987
+ }
1988
+ return { nativeWrapped, modesByUnderlying: modes };
1989
+ }
1990
+ function ensureEulerV2ChainAssetCache(chainId) {
1991
+ const hit = eulerV2ChainAssetCache.get(chainId);
1992
+ if (hit) return hit;
1993
+ const p = fetchEulerV2ChainAssetCache(chainId);
1994
+ eulerV2ChainAssetCache.set(chainId, p);
1995
+ return p;
1996
+ }
1997
+ var LABELS_PRODUCTS_URL = (chainId) => `https://raw.githubusercontent.com/euler-xyz/euler-labels/master/${chainId}/products.json`;
1998
+ function normAddrKey(raw) {
1999
+ const s = (raw ?? "").trim();
2000
+ if (!s) return null;
2001
+ try {
2002
+ return getAddress(s.startsWith("0x") ? s : `0x${s}`).toLowerCase();
2003
+ } catch {
2004
+ return null;
2005
+ }
2006
+ }
2007
+ function eulerLabelsAppendUnderlyingIfDistinct(base, underlyingSymbol) {
2008
+ const b = (base ?? "").trim();
2009
+ const sym = (underlyingSymbol ?? "").trim();
2010
+ if (!b) return "\u2014";
2011
+ if (!sym) return b;
2012
+ if (b.toLowerCase().includes(sym.toLowerCase())) return b;
2013
+ return `${b} ${sym}`;
2014
+ }
2015
+ var labelMapCache = /* @__PURE__ */ new Map();
2016
+ function fetchEulerLabelsVaultNameMap(chainId) {
2017
+ let hit = labelMapCache.get(chainId);
2018
+ if (!hit) {
2019
+ hit = (async () => {
2020
+ const m = /* @__PURE__ */ new Map();
2021
+ try {
2022
+ const res = await fetch(LABELS_PRODUCTS_URL(chainId), { cache: "force-cache" });
2023
+ if (!res.ok) return m;
2024
+ const j = await res.json();
2025
+ if (!j || typeof j !== "object") return m;
2026
+ for (const prod of Object.values(j)) {
2027
+ const productName = (prod?.name ?? "").trim();
2028
+ const vaults = prod?.vaults ?? [];
2029
+ const overrides = prod?.vaultOverrides ?? {};
2030
+ const overrideNameByVault = /* @__PURE__ */ new Map();
2031
+ for (const [addr, ov] of Object.entries(overrides)) {
2032
+ const k = normAddrKey(addr);
2033
+ const n = (ov?.name ?? "").trim();
2034
+ if (k && n) overrideNameByVault.set(k, n);
2035
+ }
2036
+ for (const vAddr of vaults) {
2037
+ const k = normAddrKey(vAddr);
2038
+ if (!k) continue;
2039
+ const overrideName = overrideNameByVault.get(k);
2040
+ const base = overrideName || productName;
2041
+ if (!base) continue;
2042
+ m.set(k, base);
2043
+ }
2044
+ }
2045
+ } catch {
2046
+ }
2047
+ return m;
2048
+ })();
2049
+ labelMapCache.set(chainId, hit);
2050
+ }
2051
+ return hit;
2052
+ }
2053
+ function eulerLabelsLookupLabelForVault(labelMap, evaultAddress) {
2054
+ const k = normAddrKey(evaultAddress);
2055
+ if (!k) return void 0;
2056
+ const v = labelMap.get(k);
2057
+ return typeof v === "string" && v.trim() ? v.trim() : void 0;
2058
+ }
2059
+
2060
+ // src/protocols/evm/euler-v2/eulerV2LendMarkets.ts
2061
+ var erc20StringMetaAbi = parseAbi([
2062
+ "function name() view returns (string)",
2063
+ "function symbol() view returns (string)"
2064
+ ]);
2065
+ var LEND_VAULT_PAGE = `
2066
+ query EulerLendVaults($first: Int!, $skip: Int!, $asset: Bytes!) {
2067
+ eulerVaults(
2068
+ first: $first
2069
+ skip: $skip
2070
+ where: { asset: $asset }
2071
+ orderBy: state__supplyApy
2072
+ orderDirection: desc
2073
+ ) {
2074
+ id
2075
+ evc
2076
+ evault
2077
+ name
2078
+ symbol
2079
+ asset
2080
+ decimals
2081
+ supplyCap
2082
+ borrowCap
2083
+ interestFee
2084
+ perspectives
2085
+ collaterals
2086
+ oracle
2087
+ unitOfAccount
2088
+ governonAdmin
2089
+ state {
2090
+ cash
2091
+ totalBorrows
2092
+ totalShares
2093
+ supplyApy
2094
+ borrowApy
2095
+ timestamp
2096
+ }
2097
+ }
2098
+ }
2099
+ `;
2100
+ var COLLATERAL_META = `
2101
+ query EulerCollateralVaults($first: Int!, $ids: [Bytes!]!) {
2102
+ eulerVaults(first: $first, where: { id_in: $ids }) {
2103
+ id
2104
+ evault
2105
+ name
2106
+ symbol
2107
+ asset
2108
+ decimals
2109
+ state {
2110
+ supplyApy
2111
+ }
2112
+ }
2113
+ }
2114
+ `;
2115
+ function normId(raw) {
2116
+ const s = (raw ?? "").trim().toLowerCase();
2117
+ return s.length ? s : null;
2118
+ }
2119
+ function normAssetLower(raw) {
2120
+ const s = (raw ?? "").trim();
2121
+ if (!s || !isAddress(s)) return null;
2122
+ return getAddress(s).toLowerCase();
2123
+ }
2124
+ function checksumHex(raw) {
2125
+ const s = (raw ?? "").trim();
2126
+ if (!s || !isAddress(s)) return null;
2127
+ try {
2128
+ return getAddress(s);
2129
+ } catch {
2130
+ return null;
2131
+ }
2132
+ }
2133
+ function eulerMetaPublicClient(chainId, rpcUrl) {
2134
+ const chain = defineChain({
2135
+ id: chainId,
2136
+ name: "EulerCollateralMeta",
2137
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
2138
+ rpcUrls: { default: { http: [rpcUrl] } }
2139
+ });
2140
+ return createPublicClient({ chain, transport: http(rpcUrl) });
2141
+ }
2142
+ async function readErc20StringMeta(client, addrLower) {
2143
+ try {
2144
+ const addr = getAddress(addrLower);
2145
+ const [nameResult, symbolResult] = await Promise.all([
2146
+ client.readContract({ address: addr, abi: erc20StringMetaAbi, functionName: "name" }),
2147
+ client.readContract({ address: addr, abi: erc20StringMetaAbi, functionName: "symbol" })
2148
+ ]);
2149
+ return {
2150
+ name: typeof nameResult === "string" ? nameResult.trim() : String(nameResult ?? "").trim(),
2151
+ symbol: typeof symbolResult === "string" ? symbolResult.trim() : String(symbolResult ?? "").trim()
2152
+ };
2153
+ } catch {
2154
+ return null;
2155
+ }
2156
+ }
2157
+ async function resolveUnderlyingTokenLabels(args) {
2158
+ const out = /* @__PURE__ */ new Map();
2159
+ const uniq = [...new Set(args.assetAddresses.map((a) => normAssetLower(a)).filter(Boolean))];
2160
+ const missingFromNode = [];
2161
+ const node = args.nodeErc20ByAddress;
2162
+ for (const low of uniq) {
2163
+ const hit = node?.get(low);
2164
+ if (hit && ((hit.symbol ?? "").trim() || (hit.name ?? "").trim())) {
2165
+ out.set(low, { name: (hit.name ?? "").trim(), symbol: (hit.symbol ?? "").trim() });
2166
+ } else {
2167
+ missingFromNode.push(low);
2168
+ }
2169
+ }
2170
+ const rpc = (args.rpcUrl ?? "").trim();
2171
+ if (!missingFromNode.length || !rpc) return out;
2172
+ const client = eulerMetaPublicClient(args.chainId, rpc);
2173
+ const batchSize = 10;
2174
+ for (let i = 0; i < missingFromNode.length; i += batchSize) {
2175
+ const chunk = missingFromNode.slice(i, i + batchSize);
2176
+ const settled = await Promise.all(chunk.map((low) => readErc20StringMeta(client, low)));
2177
+ chunk.forEach((low, j) => {
2178
+ const m = settled[j];
2179
+ if (m && (m.symbol || m.name)) out.set(low, m);
2180
+ });
2181
+ }
2182
+ return out;
2183
+ }
2184
+ function formatEulerRayApyPercent(apyRay) {
2185
+ if (apyRay === 0n) return "0.00%";
2186
+ const r = Number(formatUnits(apyRay, 27));
2187
+ if (!Number.isFinite(r)) return "\u2014";
2188
+ const pct = r * 100;
2189
+ if (pct >= 100) return `${pct.toFixed(1)}%`;
2190
+ if (pct >= 10) return `${pct.toFixed(2)}%`;
2191
+ return `${pct.toFixed(2)}%`;
2192
+ }
2193
+ var formatEulerRaySupplyApyPercent = formatEulerRayApyPercent;
2194
+ function formatUnderlyingAmount(raw, decimals) {
2195
+ try {
2196
+ const v = BigInt((raw ?? "").trim() || "0");
2197
+ const s = formatUnits(v, decimals);
2198
+ const n = Number(s);
2199
+ if (!Number.isFinite(n)) return s;
2200
+ if (n === 0) return "0";
2201
+ const abs = Math.abs(n);
2202
+ if (abs >= 1e9) return n.toExponential(2);
2203
+ if (abs >= 1e6) return n.toLocaleString(void 0, { maximumFractionDigits: 0 });
2204
+ if (abs >= 1) return n.toLocaleString(void 0, { maximumFractionDigits: 2 });
2205
+ return n.toLocaleString(void 0, { maximumFractionDigits: 6 });
2206
+ } catch {
2207
+ return "\u2014";
2208
+ }
2209
+ }
2210
+ function formatEulerVaultCapDisplay(raw, decimals) {
2211
+ try {
2212
+ const wei = eulerSubgraphVaultCapToUnderlyingWei(raw);
2213
+ if (wei >= EULER_EVAULT_CAP_UNLIMITED_WEI - 3n) return "Unlimited";
2214
+ if (wei === 0n) return "0";
2215
+ return formatUnderlyingAmount(wei.toString(), decimals);
2216
+ } catch {
2217
+ return "\u2014";
2218
+ }
2219
+ }
2220
+ function formatEulerInterestFee(raw) {
2221
+ try {
2222
+ const fee = BigInt((raw ?? "").trim() || "0");
2223
+ if (fee === 0n) return "0%";
2224
+ const pct = Number(fee) / 100;
2225
+ return `${pct.toFixed(2)}%`;
2226
+ } catch {
2227
+ return "\u2014";
2228
+ }
2229
+ }
2230
+ function formatEulerIndexedTimestamp(raw) {
2231
+ try {
2232
+ const sec = BigInt((raw ?? "").trim() || "0");
2233
+ const n = Number(sec);
2234
+ if (!Number.isFinite(n) || n <= 0) return "\u2014";
2235
+ return new Date(n * 1e3).toLocaleString(void 0, {
2236
+ dateStyle: "medium",
2237
+ timeStyle: "short"
2238
+ });
2239
+ } catch {
2240
+ return "\u2014";
2241
+ }
2242
+ }
2243
+ function utilizationPercentLabel(cash, borrows) {
2244
+ const denom = cash + borrows;
2245
+ if (denom === 0n) return "0.00%";
2246
+ const scaled = borrows * 1000000n / denom;
2247
+ const pct = Number(scaled) / 1e4;
2248
+ return `${pct.toFixed(2)}%`;
2249
+ }
2250
+ function collateralLabelFromVault(name, symbol, id) {
2251
+ const n = (name ?? "").trim();
2252
+ const sym = (symbol ?? "").trim();
2253
+ if (n && sym && n.toLowerCase() !== sym.toLowerCase()) return `${n} (${sym})`;
2254
+ if (n) return n;
2255
+ if (sym) return sym;
2256
+ return id.slice(0, 10);
2257
+ }
2258
+ function marketLabelFromSubgraphRow(r, evaultChecksum) {
2259
+ const n = (r.name ?? "").trim();
2260
+ if (n) return n;
2261
+ const sym = (r.symbol ?? "").trim();
2262
+ if (sym) return sym;
2263
+ const addr = (evaultChecksum ?? "").trim();
2264
+ if (addr.startsWith("0x") && addr.length >= 14) return `${addr.slice(0, 8)}\u2026${addr.slice(-6)}`;
2265
+ return addr || "\u2014";
2266
+ }
2267
+ function normalizePerspectives(raw) {
2268
+ const out = [];
2269
+ for (const p of raw ?? []) {
2270
+ const s = (p ?? "").trim();
2271
+ if (s) out.push(s);
2272
+ }
2273
+ return out;
2274
+ }
2275
+ function vaultSupplyBorrowApyRays(r) {
2276
+ let supplyApyRay = 0n;
2277
+ let borrowApyRay = 0n;
2278
+ try {
2279
+ supplyApyRay = BigInt((r.state?.supplyApy ?? "0").toString().trim() || "0");
2280
+ } catch {
2281
+ supplyApyRay = 0n;
2282
+ }
2283
+ try {
2284
+ borrowApyRay = BigInt((r.state?.borrowApy ?? "0").toString().trim() || "0");
2285
+ } catch {
2286
+ borrowApyRay = 0n;
2287
+ }
2288
+ return { supplyApyRay, borrowApyRay };
2289
+ }
2290
+ function vaultCashAndBorrows(r) {
2291
+ let cash = 0n;
2292
+ let borrows = 0n;
2293
+ try {
2294
+ cash = BigInt((r.state?.cash ?? "0").toString().trim() || "0");
2295
+ } catch {
2296
+ cash = 0n;
2297
+ }
2298
+ try {
2299
+ borrows = BigInt((r.state?.totalBorrows ?? "0").toString().trim() || "0");
2300
+ } catch {
2301
+ borrows = 0n;
2302
+ }
2303
+ return { cash, borrows };
2304
+ }
2305
+ async function paginateLendVaultRows(chainId, assetLower) {
2306
+ const out = [];
2307
+ let skip = 0;
2308
+ const first = 500;
2309
+ while (true) {
2310
+ const d = await eulerV2QueryGraphQl(chainId, LEND_VAULT_PAGE, {
2311
+ first,
2312
+ skip,
2313
+ asset: assetLower
2314
+ });
2315
+ const rows = d.eulerVaults ?? [];
2316
+ if (!rows.length) break;
2317
+ out.push(...rows);
2318
+ if (rows.length < first) break;
2319
+ skip += first;
2320
+ }
2321
+ return out;
2322
+ }
2323
+ async function fetchCollateralVaultMeta(chainId, collateralIds) {
2324
+ const map = /* @__PURE__ */ new Map();
2325
+ const uniq = [...new Set(collateralIds.map((x) => x.toLowerCase()))].filter(Boolean);
2326
+ const chunkSize = 400;
2327
+ for (let i = 0; i < uniq.length; i += chunkSize) {
2328
+ const chunk = uniq.slice(i, i + chunkSize);
2329
+ const d = await eulerV2QueryGraphQl(chainId, COLLATERAL_META, { first: chunk.length, ids: chunk });
2330
+ for (const r of d.eulerVaults ?? []) {
2331
+ const id = normId(r.id ?? void 0);
2332
+ if (!id) continue;
2333
+ let dec = 18;
2334
+ try {
2335
+ dec = Number.parseInt((r.decimals ?? "18").trim(), 10);
2336
+ if (!Number.isFinite(dec) || dec < 0 || dec > 36) dec = 18;
2337
+ } catch {
2338
+ dec = 18;
2339
+ }
2340
+ let supplyApyRay = 0n;
2341
+ try {
2342
+ supplyApyRay = BigInt((r.state?.supplyApy ?? "0").toString().trim() || "0");
2343
+ } catch {
2344
+ supplyApyRay = 0n;
2345
+ }
2346
+ map.set(id, {
2347
+ name: (r.name ?? "").trim(),
2348
+ symbol: (r.symbol ?? "").trim(),
2349
+ assetLower: normAssetLower(r.asset ?? void 0),
2350
+ evaultChecksum: checksumHex(r.evault ?? void 0) ?? checksumHex(r.id ?? void 0),
2351
+ decimals: dec,
2352
+ supplyApyRay
2353
+ });
2354
+ }
2355
+ }
2356
+ return map;
2357
+ }
2358
+ function flattenEulerV2BorrowCollateralRows(markets) {
2359
+ const out = [];
2360
+ for (const m of markets) {
2361
+ if (m.borrowApyRay === 0n) continue;
2362
+ const borrowNum = Number.parseFloat(m.borrowApyPercentLabel.replace(/%/g, "").trim()) || 0;
2363
+ for (const c of m.collateralTokens) {
2364
+ if (!c.collateralEvaultAddress) continue;
2365
+ const supplyNum = Number.parseFloat(c.collateralSupplyApyPercentLabel.replace(/%/g, "").trim()) || 0;
2366
+ const net = supplyNum - borrowNum;
2367
+ const netLabel = `${net.toFixed(2)}%`;
2368
+ out.push({
2369
+ pairKey: `${m.vaultId}:${c.vaultId}`,
2370
+ borrowVaultId: m.vaultId,
2371
+ borrowEvaultAddress: m.evaultAddress,
2372
+ borrowMarketName: m.marketName,
2373
+ borrowDecimals: m.decimals,
2374
+ borrowApyPercentLabel: m.borrowApyPercentLabel,
2375
+ borrowCash: m.cash,
2376
+ borrowTotalBorrows: m.totalBorrows,
2377
+ netApyPercentLabel: netLabel,
2378
+ utilizationPercentLabel: m.utilizationPercentLabel,
2379
+ availableLiquidityFormatted: m.availableLiquidityFormatted,
2380
+ evcAddress: m.evcAddress,
2381
+ oracleAddress: m.oracleAddress,
2382
+ unitOfAccountAddress: m.unitOfAccountAddress,
2383
+ borrowAssetAddressLower: m.borrowAssetAddressLower,
2384
+ governonAdminChecksum: m.governonAdminChecksum,
2385
+ collateralVaultId: c.vaultId,
2386
+ collateralEvaultAddress: c.collateralEvaultAddress,
2387
+ collateralSymbol: c.underlyingSymbol,
2388
+ collateralName: c.underlyingName,
2389
+ collateralVaultLabel: c.vaultMarketLabel,
2390
+ collateralDecimals: c.collateralDecimals,
2391
+ collateralAssetAddressLower: c.collateralAssetAddressLower,
2392
+ collateralSupplyApyPercentLabel: c.collateralSupplyApyPercentLabel,
2393
+ borrowIndexerLendMetrics: m.indexerLendMetrics
2394
+ });
2395
+ }
2396
+ }
2397
+ return out;
2398
+ }
2399
+ async function fetchEulerV2IsolatedLendMarketsForRowAsset(args) {
2400
+ const cache = await ensureEulerV2ChainAssetCache(args.chainId);
2401
+ const underlying = eulerV2KeyForNodeAssetRow({
2402
+ contractAddress: args.contractAddress,
2403
+ nativeWrapped: cache.nativeWrapped
2404
+ });
2405
+ if (!underlying) return [];
2406
+ const [gqlRows, labelMap] = await Promise.all([
2407
+ paginateLendVaultRows(args.chainId, underlying),
2408
+ fetchEulerLabelsVaultNameMap(args.chainId)
2409
+ ]);
2410
+ const eligibleRows = gqlRows.filter((r) => {
2411
+ const { supplyApyRay, borrowApyRay } = vaultSupplyBorrowApyRays(r);
2412
+ if (supplyApyRay === 0n && borrowApyRay === 0n) return false;
2413
+ const { cash, borrows } = vaultCashAndBorrows(r);
2414
+ return cash + borrows !== 0n;
2415
+ });
2416
+ if (typeof process !== "undefined" && process.env.NODE_ENV === "development") {
2417
+ let matched = 0;
2418
+ for (const r of eligibleRows) {
2419
+ const evRaw = checksumHex(r.evault ?? void 0) ?? checksumHex(r.id ?? void 0);
2420
+ if (!evRaw) continue;
2421
+ if (eulerLabelsLookupLabelForVault(labelMap, evRaw)) matched += 1;
2422
+ }
2423
+ console.debug("[Euler labels]", {
2424
+ chainId: args.chainId,
2425
+ matchedEligibleVaults: matched,
2426
+ eligibleVaults: eligibleRows.length,
2427
+ labelMapEntries: labelMap.size
2428
+ });
2429
+ }
2430
+ const collateralIds = [];
2431
+ for (const r of eligibleRows) {
2432
+ for (const c of r.collaterals ?? []) {
2433
+ const id = normId(c ?? void 0);
2434
+ if (id) collateralIds.push(id);
2435
+ }
2436
+ }
2437
+ const collateralMeta = await fetchCollateralVaultMeta(args.chainId, collateralIds);
2438
+ const underlyingAssetAddrs = [];
2439
+ for (const m of collateralMeta.values()) {
2440
+ if (m.assetLower) underlyingAssetAddrs.push(m.assetLower);
2441
+ }
2442
+ const underlyingLabels = await resolveUnderlyingTokenLabels({
2443
+ chainId: args.chainId,
2444
+ rpcUrl: args.rpcUrl,
2445
+ nodeErc20ByAddress: args.nodeErc20ByAddress,
2446
+ assetAddresses: underlyingAssetAddrs
2447
+ });
2448
+ const rows = [];
2449
+ for (const r of eligibleRows) {
2450
+ const vaultId = normId(r.id ?? void 0);
2451
+ if (!vaultId) continue;
2452
+ const evaultAddress = checksumHex(r.evault ?? void 0) ?? checksumHex(r.id ?? void 0) ?? vaultId;
2453
+ const subgraphTitle = marketLabelFromSubgraphRow(r, evaultAddress);
2454
+ const labelBase = eulerLabelsLookupLabelForVault(labelMap, evaultAddress);
2455
+ const assetSym = (args.suppliedAssetSymbol ?? "").trim();
2456
+ const marketName = labelBase ? eulerLabelsAppendUnderlyingIfDistinct(labelBase, assetSym) : subgraphTitle;
2457
+ let dec = 18;
2458
+ try {
2459
+ dec = Number.parseInt((r.decimals ?? "18").trim(), 10);
2460
+ if (!Number.isFinite(dec) || dec < 0 || dec > 36) dec = 18;
2461
+ } catch {
2462
+ dec = 18;
2463
+ }
2464
+ const { cash, borrows } = vaultCashAndBorrows(r);
2465
+ const { supplyApyRay, borrowApyRay } = vaultSupplyBorrowApyRays(r);
2466
+ const total = cash + borrows;
2467
+ const cIds = [...new Set((r.collaterals ?? []).map((x) => normId(x ?? void 0)).filter(Boolean))];
2468
+ const collateralTokens = cIds.map((cid) => {
2469
+ const vm = collateralMeta.get(cid);
2470
+ const vaultMarketLabel = vm ? collateralLabelFromVault(vm.name, vm.symbol, cid) : cid.slice(0, 10);
2471
+ const assetLow = vm?.assetLower ?? null;
2472
+ const und = assetLow ? underlyingLabels.get(assetLow) : void 0;
2473
+ const sym = (und?.symbol ?? "").trim();
2474
+ const nam = (und?.name ?? "").trim();
2475
+ let underlyingSymbol;
2476
+ let underlyingName;
2477
+ if (sym || nam) {
2478
+ underlyingSymbol = sym || nam;
2479
+ underlyingName = sym && nam && nam.toLowerCase() !== sym.toLowerCase() ? nam : null;
2480
+ } else {
2481
+ underlyingSymbol = vaultMarketLabel;
2482
+ underlyingName = null;
2483
+ }
2484
+ const collSupplyRay = vm?.supplyApyRay ?? 0n;
2485
+ return {
2486
+ vaultId: cid,
2487
+ collateralEvaultAddress: vm?.evaultChecksum ?? null,
2488
+ collateralDecimals: vm?.decimals ?? 18,
2489
+ collateralAssetAddressLower: assetLow,
2490
+ collateralSupplyApyPercentLabel: formatEulerRayApyPercent(collSupplyRay),
2491
+ underlyingSymbol,
2492
+ underlyingName,
2493
+ vaultMarketLabel
2494
+ };
2495
+ });
2496
+ const indexerLendMetrics = eulerVaultLendMetricsFromIndexerVaultState({
2497
+ supplyCapRaw: r.supplyCap,
2498
+ borrowCapRaw: r.borrowCap,
2499
+ cashWei: cash,
2500
+ totalBorrowsWei: borrows,
2501
+ totalAssetsWei: total
2502
+ });
2503
+ rows.push({
2504
+ vaultId,
2505
+ evaultAddress,
2506
+ marketName,
2507
+ decimals: dec,
2508
+ cash,
2509
+ totalBorrows: borrows,
2510
+ supplyApyPercentLabel: formatEulerRayApyPercent(supplyApyRay),
2511
+ borrowApyPercentLabel: formatEulerRayApyPercent(borrowApyRay),
2512
+ totalSupplyFormatted: formatUnderlyingAmount(total.toString(), dec),
2513
+ availableLiquidityFormatted: formatUnderlyingAmount(cash.toString(), dec),
2514
+ utilizationPercentLabel: utilizationPercentLabel(cash, borrows),
2515
+ supplyCapFormatted: formatEulerVaultCapDisplay(r.supplyCap, dec),
2516
+ borrowCapFormatted: formatEulerVaultCapDisplay(r.borrowCap, dec),
2517
+ interestFeeLabel: formatEulerInterestFee(r.interestFee),
2518
+ stateIndexedDisplay: formatEulerIndexedTimestamp(r.state?.timestamp),
2519
+ perspectives: normalizePerspectives(r.perspectives),
2520
+ evcAddress: checksumHex(r.evc ?? void 0),
2521
+ oracleAddress: checksumHex(r.oracle ?? void 0),
2522
+ unitOfAccountAddress: checksumHex(r.unitOfAccount ?? void 0),
2523
+ borrowAssetAddressLower: normAssetLower(r.asset ?? void 0),
2524
+ governonAdminChecksum: checksumHex(r.governonAdmin ?? void 0),
2525
+ borrowApyRay,
2526
+ indexerLendMetrics,
2527
+ collateralTokens
2528
+ });
2529
+ }
2530
+ return rows;
2531
+ }
2532
+ var UINT256_MAX2 = (1n << 256n) - 1n;
2533
+ var EARN_STRATEGY_ALLOC_CAP_SUBGRAPH_UNBOUNDED = 87112285931760246646623899502532662132735n;
2534
+ var EARN_VAULTS_QUERY = `
2535
+ query EulerEarnForAsset($asset: Bytes!, $ids: [Bytes!]!) {
2536
+ eulerEarnVaults(first: 500, where: { asset: $asset, id_in: $ids }) {
2537
+ id
2538
+ name
2539
+ curator
2540
+ performanceFee
2541
+ totalAssets
2542
+ totalAllocated
2543
+ asset
2544
+ strategies(first: 200) {
2545
+ strategy
2546
+ allocatedAssets
2547
+ availableAssets
2548
+ currentAllocationCap
2549
+ }
2550
+ }
2551
+ }
2552
+ `;
2553
+ var STRATEGY_VAULT_APY = `
2554
+ query EulerStrategyVaults($ids: [Bytes!]!) {
2555
+ eulerVaults(first: 500, where: { id_in: $ids }) {
2556
+ id
2557
+ asset
2558
+ symbol
2559
+ state {
2560
+ supplyApy
2561
+ }
2562
+ }
2563
+ }
2564
+ `;
2565
+ function normLowerId(raw) {
2566
+ const s = (raw ?? "").trim().toLowerCase();
2567
+ if (!s.startsWith("0x") || s.length < 4) return null;
2568
+ return s;
2569
+ }
2570
+ function big(raw) {
2571
+ try {
2572
+ return BigInt((raw ?? "0").toString().trim() || "0");
2573
+ } catch {
2574
+ return 0n;
2575
+ }
2576
+ }
2577
+ async function fetchEulerLabelsEarnVaultAllowlist(chainId) {
2578
+ const url = `https://raw.githubusercontent.com/euler-xyz/euler-labels/master/${chainId}/earn-vaults.json`;
2579
+ const r = await fetch(url, { cache: "no-store" });
2580
+ if (!r.ok) return /* @__PURE__ */ new Set();
2581
+ let j;
2582
+ try {
2583
+ j = await r.json();
2584
+ } catch {
2585
+ return /* @__PURE__ */ new Set();
2586
+ }
2587
+ const out = /* @__PURE__ */ new Set();
2588
+ if (!Array.isArray(j)) return out;
2589
+ for (const entry of j) {
2590
+ if (typeof entry === "string") {
2591
+ const lo = normLowerId(entry);
2592
+ if (lo) out.add(lo);
2593
+ continue;
2594
+ }
2595
+ if (entry && typeof entry === "object" && !Array.isArray(entry)) {
2596
+ const o = entry;
2597
+ if (o.deprecated === true) continue;
2598
+ const addr = typeof o.address === "string" ? o.address : typeof o.id === "string" ? o.id : null;
2599
+ const lo = normLowerId(addr);
2600
+ if (lo) out.add(lo);
2601
+ }
2602
+ }
2603
+ return out;
2604
+ }
2605
+ async function fetchEulerLabelsEntityNameByAddress(chainId) {
2606
+ const url = `https://raw.githubusercontent.com/euler-xyz/euler-labels/master/${chainId}/entities.json`;
2607
+ const r = await fetch(url, { cache: "no-store" });
2608
+ const m = /* @__PURE__ */ new Map();
2609
+ if (!r.ok) return m;
2610
+ let j;
2611
+ try {
2612
+ j = await r.json();
2613
+ } catch {
2614
+ return m;
2615
+ }
2616
+ if (!j || typeof j !== "object") return m;
2617
+ for (const ent of Object.values(j)) {
2618
+ if (!ent || typeof ent !== "object") continue;
2619
+ const name = ent.name;
2620
+ const groupName = typeof name === "string" && name.trim() ? name.trim() : null;
2621
+ if (!groupName) continue;
2622
+ const addrs = ent.addresses;
2623
+ if (!addrs || typeof addrs !== "object") continue;
2624
+ for (const k of Object.keys(addrs)) {
2625
+ const lo = normLowerId(k);
2626
+ if (lo) m.set(lo, groupName);
2627
+ }
2628
+ }
2629
+ return m;
2630
+ }
2631
+ function formatPerformanceFeePercent(feeWad) {
2632
+ if (feeWad === 0n) return "0%";
2633
+ const x = Number(formatUnits(feeWad, 18));
2634
+ if (!Number.isFinite(x)) return "\u2014";
2635
+ const pct = x * 100;
2636
+ if (pct >= 10) return `${pct.toFixed(1)}%`;
2637
+ if (pct >= 1) return `${pct.toFixed(2)}%`;
2638
+ return `${pct.toFixed(2)}%`;
2639
+ }
2640
+ function isMaxUintCap(cap) {
2641
+ return cap >= UINT256_MAX2 - 3n;
2642
+ }
2643
+ function isUnboundedEarnStrategyAllocationCap(cap) {
2644
+ return isMaxUintCap(cap) || cap === EARN_STRATEGY_ALLOC_CAP_SUBGRAPH_UNBOUNDED;
2645
+ }
2646
+ async function fetchEulerV2EarnOfferingsForRowAsset(args) {
2647
+ const { chainId, underlyingDecimals } = args;
2648
+ const cache = await ensureEulerV2ChainAssetCache(chainId);
2649
+ const key = eulerV2KeyForNodeAssetRow({
2650
+ contractAddress: args.contractAddress,
2651
+ nativeWrapped: cache.nativeWrapped
2652
+ });
2653
+ if (!key) return [];
2654
+ const [allowlist, entityByAddr] = await Promise.all([
2655
+ fetchEulerLabelsEarnVaultAllowlist(chainId),
2656
+ fetchEulerLabelsEntityNameByAddress(chainId)
2657
+ ]);
2658
+ if (!allowlist.size) return [];
2659
+ const assetBytes = key;
2660
+ const ids = [...allowlist];
2661
+ const data = await eulerV2QueryGraphQl(chainId, EARN_VAULTS_QUERY, {
2662
+ asset: assetBytes,
2663
+ ids
2664
+ });
2665
+ const rawRows = data.eulerEarnVaults ?? [];
2666
+ const strategyIds = /* @__PURE__ */ new Set();
2667
+ for (const v of rawRows) {
2668
+ for (const s of v.strategies ?? []) {
2669
+ const sid = normLowerId(s.strategy ?? void 0);
2670
+ if (sid) strategyIds.add(sid);
2671
+ }
2672
+ }
2673
+ const apyByVault = /* @__PURE__ */ new Map();
2674
+ const sidList = [...strategyIds];
2675
+ const batch = 200;
2676
+ for (let i = 0; i < sidList.length; i += batch) {
2677
+ const chunk = sidList.slice(i, i + batch);
2678
+ const d2 = await eulerV2QueryGraphQl(chainId, STRATEGY_VAULT_APY, {
2679
+ ids: chunk
2680
+ });
2681
+ for (const row of d2.eulerVaults ?? []) {
2682
+ const id = normLowerId(row.id ?? void 0);
2683
+ if (!id) continue;
2684
+ const ray = big(row.state?.supplyApy ?? "0");
2685
+ const sym = (row.symbol ?? "").trim() || "\u2014";
2686
+ const al = normLowerId(row.asset ?? void 0);
2687
+ apyByVault.set(id, { apyRay: ray, symbol: sym, assetLower: al });
2688
+ }
2689
+ }
2690
+ const dec = Math.max(0, Math.min(36, Math.floor(underlyingDecimals)));
2691
+ const tic = (args.suppliedAssetSymbol ?? "").trim();
2692
+ const out = [];
2693
+ for (const v of rawRows) {
2694
+ const vid = normLowerId(v.id ?? void 0);
2695
+ if (!vid || !allowlist.has(vid)) continue;
2696
+ const curatorLo = normLowerId(v.curator ?? void 0);
2697
+ const curatorName = curatorLo && entityByAddr.has(curatorLo) ? entityByAddr.get(curatorLo) : curatorLo ? `${curatorLo.slice(0, 6)}\u2026${curatorLo.slice(-4)}` : "\u2014";
2698
+ const strategiesRaw = [...v.strategies ?? []];
2699
+ const sumStratAlloc = strategiesRaw.reduce((a, s) => a + big(s.allocatedAssets), 0n);
2700
+ const totalAssets = big(v.totalAssets);
2701
+ const totalAllocatedField = big(v.totalAllocated);
2702
+ let denomForApy = sumStratAlloc;
2703
+ if (denomForApy === 0n) denomForApy = totalAllocatedField;
2704
+ let weightedApyRay = 0n;
2705
+ if (denomForApy > 0n) {
2706
+ let acc = 0n;
2707
+ for (const s of strategiesRaw) {
2708
+ const alloc = big(s.allocatedAssets);
2709
+ const sid = normLowerId(s.strategy ?? void 0);
2710
+ const meta = sid ? apyByVault.get(sid) : void 0;
2711
+ const ray = meta?.apyRay ?? 0n;
2712
+ acc += ray * alloc;
2713
+ }
2714
+ weightedApyRay = acc / denomForApy;
2715
+ }
2716
+ const supplyApyPercentLabel = denomForApy > 0n ? formatEulerRayApyPercent(weightedApyRay) : "0.00%";
2717
+ const deployedForLiquidity = sumStratAlloc > 0n ? sumStratAlloc : totalAllocatedField > 0n ? totalAllocatedField : 0n;
2718
+ const availableWei = totalAssets > deployedForLiquidity ? totalAssets - deployedForLiquidity : 0n;
2719
+ const performanceFee = big(v.performanceFee);
2720
+ const performanceFeePercentLabel = formatPerformanceFeePercent(performanceFee);
2721
+ const vaultName = (v.name ?? "").trim() || "Euler Earn";
2722
+ const stratRows = strategiesRaw.map((s) => {
2723
+ const sid = normLowerId(s.strategy ?? void 0) ?? "";
2724
+ const meta = sid ? apyByVault.get(sid) : void 0;
2725
+ const alloc = big(s.allocatedAssets);
2726
+ const cap = big(s.currentAllocationCap);
2727
+ const pctOfTotal = totalAssets > 0n ? `${(Number(alloc * 10000n / totalAssets) / 100).toFixed(1)}%` : "0.0%";
2728
+ const capUnlimited = isUnboundedEarnStrategyAllocationCap(cap);
2729
+ let capLabel;
2730
+ if (capUnlimited) {
2731
+ capLabel = "\u221E";
2732
+ } else if (cap === 0n) {
2733
+ capLabel = `0${tic ? ` ${tic}` : ""}`;
2734
+ } else {
2735
+ try {
2736
+ capLabel = `${formatUnits(cap, dec)}${tic ? ` ${tic}` : ""}`;
2737
+ } catch {
2738
+ capLabel = cap.toString();
2739
+ }
2740
+ }
2741
+ let allocHuman;
2742
+ try {
2743
+ allocHuman = `${formatUnits(alloc, dec)}${tic ? ` ${tic}` : ""}`;
2744
+ } catch {
2745
+ allocHuman = alloc.toString();
2746
+ }
2747
+ return {
2748
+ strategyVaultIdLower: sid,
2749
+ strategySymbol: meta?.symbol ?? "\u2014",
2750
+ supplyApyPercentLabel: meta ? formatEulerRayApyPercent(meta.apyRay) : "\u2014",
2751
+ allocatedAssetsWei: alloc,
2752
+ allocatedHumanLabel: allocHuman,
2753
+ allocationPercentOfTvlLabel: pctOfTotal,
2754
+ allocationCapHumanLabel: capLabel,
2755
+ allocationCapUnlimited: capUnlimited
2756
+ };
2757
+ });
2758
+ const cs = checksumHex2(v.id);
2759
+ if (!cs) continue;
2760
+ let totalHuman;
2761
+ let availHuman;
2762
+ try {
2763
+ totalHuman = `${formatUnits(totalAssets, dec)}${tic ? ` ${tic}` : ""}`;
2764
+ availHuman = `${formatUnits(availableWei, dec)}${tic ? ` ${tic}` : ""}`;
2765
+ } catch {
2766
+ totalHuman = totalAssets.toString();
2767
+ availHuman = availableWei.toString();
2768
+ }
2769
+ out.push({
2770
+ vaultIdLower: vid,
2771
+ evaultAddress: cs,
2772
+ vaultName,
2773
+ curatorDisplayName: curatorName,
2774
+ strategyCount: strategiesRaw.length,
2775
+ supplyApyPercentLabel,
2776
+ totalAssetsWei: totalAssets,
2777
+ totalAssetsHumanLabel: totalHuman,
2778
+ availableLiquidityWei: availableWei,
2779
+ availableLiquidityHumanLabel: availHuman,
2780
+ performanceFeePercentLabel,
2781
+ underlyingDecimals: dec,
2782
+ strategies: stratRows
2783
+ });
2784
+ }
2785
+ out.sort((a, b) => {
2786
+ const ap = Number.parseFloat(a.supplyApyPercentLabel.replace("%", "")) || 0;
2787
+ const bp = Number.parseFloat(b.supplyApyPercentLabel.replace("%", "")) || 0;
2788
+ if (bp !== ap) return bp - ap;
2789
+ return a.vaultName.localeCompare(b.vaultName);
2790
+ });
2791
+ return out;
2792
+ }
2793
+ function checksumHex2(raw) {
2794
+ const s = (raw ?? "").trim();
2795
+ if (!s || !isAddress(s)) return null;
2796
+ try {
2797
+ return getAddress(s);
2798
+ } catch {
2799
+ return null;
2800
+ }
2801
+ }
2802
+ var eulerVaultRiskAbi = parseAbi([
2803
+ "function LTVBorrow(address collateral) view returns (uint16)",
2804
+ "function LTVLiquidation(address collateral) view returns (uint16)"
2805
+ ]);
2806
+ var priceOracleAbi = parseAbi([
2807
+ "function getQuote(uint256 inAmount, address base, address quote) view returns (uint256 outAmount)"
2808
+ ]);
2809
+ function metaClient(chainId, rpcUrl) {
2810
+ const chain = defineChain({
2811
+ id: chainId,
2812
+ name: "EulerBorrowRisk",
2813
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
2814
+ rpcUrls: { default: { http: [rpcUrl] } }
2815
+ });
2816
+ return createPublicClient({ chain, transport: http(rpcUrl) });
2817
+ }
2818
+ async function fetchEulerV2BorrowPairLtvs(args) {
2819
+ const rpc = (args.rpcUrl ?? "").trim();
2820
+ if (!rpc) return null;
2821
+ try {
2822
+ const client = metaClient(args.chainId, rpc);
2823
+ const coll = getAddress(args.collateralEvault);
2824
+ const bor = getAddress(args.borrowEvault);
2825
+ const [maxLt, liqLt] = await Promise.all([
2826
+ client.readContract({ address: bor, abi: eulerVaultRiskAbi, functionName: "LTVBorrow", args: [coll] }),
2827
+ client.readContract({ address: bor, abi: eulerVaultRiskAbi, functionName: "LTVLiquidation", args: [coll] })
2828
+ ]);
2829
+ return { maxLtvBps: Number(maxLt), liquidationLtvBps: Number(liqLt) };
2830
+ } catch {
2831
+ return null;
2832
+ }
2833
+ }
2834
+ async function fetchEulerV2BorrowOracleQuoteSnapshot(args) {
2835
+ const rpc = (args.rpcUrl ?? "").trim();
2836
+ if (!rpc) return null;
2837
+ if (!isAddress(args.oracle) || !isAddress(args.unitOfAccount)) return null;
2838
+ try {
2839
+ const client = metaClient(args.chainId, rpc);
2840
+ const oracle = getAddress(args.oracle);
2841
+ const uoa = getAddress(args.unitOfAccount);
2842
+ const bAss = getAddress(args.borrowAsset);
2843
+ const cAss = getAddress(args.collateralAsset);
2844
+ const oneBorrow = parseUnits("1", args.borrowDecimals);
2845
+ const collWei = parseUnits(args.collateralAmountHuman, args.collateralDecimals);
2846
+ const [oneBorrowUnitUoAWei, collateralUoAWei] = await Promise.all([
2847
+ client.readContract({
2848
+ address: oracle,
2849
+ abi: priceOracleAbi,
2850
+ functionName: "getQuote",
2851
+ args: [oneBorrow, bAss, uoa]
2852
+ }),
2853
+ collWei > 0n ? client.readContract({
2854
+ address: oracle,
2855
+ abi: priceOracleAbi,
2856
+ functionName: "getQuote",
2857
+ args: [collWei, cAss, uoa]
2858
+ }) : Promise.resolve(0n)
2859
+ ]);
2860
+ return { oneBorrowUnitUoAWei, collateralUoAWei };
2861
+ } catch {
2862
+ return null;
2863
+ }
2864
+ }
2865
+ function estimateBorrowHumanFromLtv(args) {
2866
+ if (args.collateralUoAWei <= 0n || args.oneBorrowUnitUoAWei <= 0n || !(args.ltvPercent > 0)) return "0";
2867
+ const bps = Math.min(1e4, Math.max(0, Math.round(args.ltvPercent * 100)));
2868
+ const debtUoA = args.collateralUoAWei * BigInt(bps) / 10000n;
2869
+ const borrowWei = debtUoA * parseUnits("1", args.borrowDecimals) / args.oneBorrowUnitUoAWei;
2870
+ if (borrowWei <= 0n) return "0";
2871
+ return formatUnits(borrowWei, args.borrowDecimals);
2872
+ }
2873
+ function maxCollateralWeiCappedByBorrowLiquidity(args) {
2874
+ const { walletBalWei, maxBorrowWei, collateralFullUoAWei, oneBorrowUnitUoAWei, ltvPercent, borrowDecimals } = args;
2875
+ if (walletBalWei <= 0n) return 0n;
2876
+ if (maxBorrowWei <= 0n) return 0n;
2877
+ if (!(ltvPercent > 0)) return walletBalWei;
2878
+ if (collateralFullUoAWei <= 0n || oneBorrowUnitUoAWei <= 0n) return walletBalWei;
2879
+ const bps = Math.min(1e4, Math.max(0, Math.round(ltvPercent * 100)));
2880
+ const debtUoA = collateralFullUoAWei * BigInt(bps) / 10000n;
2881
+ const borrowWeiFull = debtUoA * parseUnits("1", borrowDecimals) / oneBorrowUnitUoAWei;
2882
+ if (borrowWeiFull <= 0n) return walletBalWei;
2883
+ if (borrowWeiFull <= maxBorrowWei) return walletBalWei;
2884
+ return walletBalWei * maxBorrowWei / borrowWeiFull;
2885
+ }
2886
+
2887
+ // src/protocols/evm/euler-v2/eulerV2BorrowPositionsLoad.ts
2888
+ var TRACKING_BORROWS = `
2889
+ query EulerTrackingBorrows($id: Bytes!) {
2890
+ trackingActiveAccount(id: $id) {
2891
+ borrows
2892
+ }
2893
+ }`;
2894
+ var VAULTS_FOR_EVAULTS = `
2895
+ query EulerVaultsForBorrows($ids: [ID!]!) {
2896
+ eulerVaults(where: { id_in: $ids }) {
2897
+ id
2898
+ evault
2899
+ evc
2900
+ name
2901
+ symbol
2902
+ asset
2903
+ decimals
2904
+ collaterals
2905
+ }
2906
+ }`;
2907
+ var erc20DecimalsAbi2 = parseAbi(["function decimals() view returns (uint8)"]);
2908
+ var erc4626BalanceAbi = parseAbi(["function balanceOf(address account) view returns (uint256)"]);
2909
+ var erc4626MaxWithdrawAbi2 = parseAbi(["function maxWithdraw(address owner) view returns (uint256)"]);
2910
+ var eulerRiskLtAbi = parseAbi(["function LTVBorrow(address collateral) view returns (uint16)"]);
2911
+ var eulerLiquidityAbi = parseAbi([
2912
+ "function accountLiquidity(address account, bool liquidation) view returns (uint256 collateralValue, uint256 liabilityValue)"
2913
+ ]);
2914
+ function metaClient2(chainId, rpcUrl) {
2915
+ const chain = defineChain({
2916
+ id: chainId,
2917
+ name: "EulerBorrowPos",
2918
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
2919
+ rpcUrls: { default: { http: [rpcUrl] } }
2920
+ });
2921
+ return createPublicClient({ chain, transport: http(rpcUrl) });
2922
+ }
2923
+ function parseEulerTrackingBorrowEntry(entry) {
2924
+ const s = (entry ?? "").trim().toLowerCase();
2925
+ if (!s.startsWith("0x")) return null;
2926
+ const hex = s.slice(2);
2927
+ if (hex.length < 80) return null;
2928
+ try {
2929
+ const subAccount = getAddress(`0x${hex.slice(0, 40)}`);
2930
+ const borrowVault = getAddress(`0x${hex.slice(40, 80)}`);
2931
+ return { subAccount, borrowVault };
2932
+ } catch {
2933
+ return null;
2934
+ }
2935
+ }
2936
+ async function fetchEulerV2TrackingBorrowEntries(args) {
2937
+ const id = args.mainAddress.toLowerCase();
2938
+ const data = await eulerV2QueryGraphQl(
2939
+ args.chainId,
2940
+ TRACKING_BORROWS,
2941
+ { id }
2942
+ );
2943
+ const raw = data.trackingActiveAccount?.borrows ?? [];
2944
+ return raw.filter((x) => typeof x === "string" && x.trim().length > 0);
2945
+ }
2946
+ async function pickCollateralEvault(args) {
2947
+ const cands = (args.collaterals ?? []).map((c) => (c ?? "").trim().toLowerCase()).filter((c) => c.startsWith("0x") && isAddress(c));
2948
+ const uniq = [...new Set(cands)];
2949
+ for (const low of uniq) {
2950
+ try {
2951
+ const coll = getAddress(low);
2952
+ const lt = await args.client.readContract({
2953
+ address: args.borrowVault,
2954
+ abi: eulerRiskLtAbi,
2955
+ functionName: "LTVBorrow",
2956
+ args: [coll]
2957
+ });
2958
+ if (Number(lt) === 0) continue;
2959
+ const bal = await args.client.readContract({
2960
+ address: coll,
2961
+ abi: erc4626BalanceAbi,
2962
+ functionName: "balanceOf",
2963
+ args: [args.subAccount]
2964
+ });
2965
+ if (bal > 0n) return coll;
2966
+ } catch {
2967
+ }
2968
+ }
2969
+ for (const low of uniq) {
2970
+ try {
2971
+ const coll = getAddress(low);
2972
+ const lt = await args.client.readContract({
2973
+ address: args.borrowVault,
2974
+ abi: eulerRiskLtAbi,
2975
+ functionName: "LTVBorrow",
2976
+ args: [coll]
2977
+ });
2978
+ if (Number(lt) > 0) return coll;
2979
+ } catch {
2980
+ }
2981
+ }
2982
+ if (uniq.length) {
2983
+ try {
2984
+ return getAddress(uniq[0]);
2985
+ } catch {
2986
+ return null;
2987
+ }
2988
+ }
2989
+ return null;
2990
+ }
2991
+ async function loadEulerV2BorrowPositionsForOwner(args) {
2992
+ const rpc = args.rpcUrl.trim();
2993
+ if (!rpc) return [];
2994
+ const [entries, depositEntries] = await Promise.all([
2995
+ fetchEulerV2TrackingBorrowEntries({ chainId: args.chainId, mainAddress: args.mainAddress }),
2996
+ fetchEulerV2TrackingDepositEntries({ chainId: args.chainId, mainAddress: args.mainAddress })
2997
+ ]);
2998
+ const pairKey = (sub, vault) => `${sub.toLowerCase()}-${vault.toLowerCase()}`;
2999
+ const seenPairs = /* @__PURE__ */ new Set();
3000
+ const pairs = [];
3001
+ const pushPair = (sub, vault) => {
3002
+ const k = pairKey(sub, vault);
3003
+ if (seenPairs.has(k)) return;
3004
+ seenPairs.add(k);
3005
+ pairs.push({ subAccount: sub, borrowVault: vault });
3006
+ };
3007
+ for (const e of entries) {
3008
+ const p = parseEulerTrackingBorrowEntry(e);
3009
+ if (p) pushPair(p.subAccount, p.borrowVault);
3010
+ }
3011
+ const client = metaClient2(args.chainId, rpc);
3012
+ const debtAbi = parseAbi(["function debtOf(address account) view returns (uint256)"]);
3013
+ for (const e of depositEntries) {
3014
+ const p = parseEulerTrackingBorrowEntry(e);
3015
+ if (!p) continue;
3016
+ if (seenPairs.has(pairKey(p.subAccount, p.borrowVault))) continue;
3017
+ let debtWei = 0n;
3018
+ try {
3019
+ debtWei = await client.readContract({
3020
+ address: p.borrowVault,
3021
+ abi: debtAbi,
3022
+ functionName: "debtOf",
3023
+ args: [p.subAccount]
3024
+ });
3025
+ } catch {
3026
+ continue;
3027
+ }
3028
+ if (debtWei === 0n) continue;
3029
+ pushPair(p.subAccount, p.borrowVault);
3030
+ }
3031
+ if (!pairs.length) return [];
3032
+ const vaultIds = [...new Set(pairs.map((x) => x.borrowVault.toLowerCase()))];
3033
+ const vaultData = await eulerV2QueryGraphQl(args.chainId, VAULTS_FOR_EVAULTS, { ids: vaultIds });
3034
+ const byEvault = /* @__PURE__ */ new Map();
3035
+ for (const v of vaultData.eulerVaults ?? []) {
3036
+ const ev = (v.evault ?? "").trim().toLowerCase();
3037
+ if (ev) byEvault.set(ev, v);
3038
+ }
3039
+ const evcAbi5 = parseAbi(["function EVC() view returns (address)"]);
3040
+ const out = [];
3041
+ const t = Date.now();
3042
+ for (const { subAccount, borrowVault } of pairs) {
3043
+ let debtWei = 0n;
3044
+ try {
3045
+ debtWei = await client.readContract({
3046
+ address: borrowVault,
3047
+ abi: debtAbi,
3048
+ functionName: "debtOf",
3049
+ args: [subAccount]
3050
+ });
3051
+ } catch {
3052
+ }
3053
+ const vrow = byEvault.get(borrowVault.toLowerCase());
3054
+ const decRaw = Number((vrow?.decimals ?? "18").toString().trim() || "18");
3055
+ const decimals = Number.isFinite(decRaw) && decRaw >= 0 && decRaw <= 36 ? decRaw : 18;
3056
+ let assetLow = (vrow?.asset ?? "").trim().toLowerCase();
3057
+ if (!assetLow || !isAddress(assetLow)) {
3058
+ try {
3059
+ const a = await client.readContract({
3060
+ address: borrowVault,
3061
+ abi: parseAbi(["function asset() view returns (address)"]),
3062
+ functionName: "asset"
3063
+ });
3064
+ assetLow = getAddress(a).toLowerCase();
3065
+ } catch {
3066
+ assetLow = "";
3067
+ }
3068
+ }
3069
+ let decFinal = decimals;
3070
+ if (assetLow && isAddress(assetLow)) {
3071
+ try {
3072
+ const d = await client.readContract({
3073
+ address: getAddress(assetLow),
3074
+ abi: erc20DecimalsAbi2,
3075
+ functionName: "decimals"
3076
+ });
3077
+ const n = Number(d);
3078
+ if (Number.isFinite(n) && n >= 0 && n <= 36) decFinal = n;
3079
+ } catch {
3080
+ }
3081
+ }
3082
+ const debtHuman = formatUnits(debtWei, decFinal);
3083
+ const collateralVault = await pickCollateralEvault({
3084
+ client,
3085
+ borrowVault,
3086
+ subAccount,
3087
+ collaterals: vrow?.collaterals ?? null
3088
+ });
3089
+ if (debtWei === 0n) {
3090
+ if (!collateralVault) continue;
3091
+ let maxWithdrawLeft = 0n;
3092
+ try {
3093
+ maxWithdrawLeft = await client.readContract({
3094
+ address: collateralVault,
3095
+ abi: erc4626MaxWithdrawAbi2,
3096
+ functionName: "maxWithdraw",
3097
+ args: [subAccount]
3098
+ });
3099
+ } catch {
3100
+ maxWithdrawLeft = 0n;
3101
+ }
3102
+ let collateralShares = 0n;
3103
+ if (maxWithdrawLeft === 0n) {
3104
+ try {
3105
+ collateralShares = await client.readContract({
3106
+ address: collateralVault,
3107
+ abi: erc4626BalanceAbi,
3108
+ functionName: "balanceOf",
3109
+ args: [subAccount]
3110
+ });
3111
+ } catch {
3112
+ collateralShares = 0n;
3113
+ }
3114
+ }
3115
+ if (maxWithdrawLeft === 0n && collateralShares === 0n) continue;
3116
+ }
3117
+ let collateralAssetAddressLower = null;
3118
+ let collateralAssetDecimals;
3119
+ let collateralUnderlyingSymbol = null;
3120
+ if (collateralVault) {
3121
+ try {
3122
+ const assetAddr = await client.readContract({
3123
+ address: collateralVault,
3124
+ abi: parseAbi(["function asset() view returns (address)"]),
3125
+ functionName: "asset"
3126
+ });
3127
+ const cLow = getAddress(assetAddr).toLowerCase();
3128
+ collateralAssetAddressLower = cLow;
3129
+ const cd = await client.readContract({
3130
+ address: getAddress(cLow),
3131
+ abi: erc20DecimalsAbi2,
3132
+ functionName: "decimals"
3133
+ });
3134
+ const cn = Number(cd);
3135
+ if (Number.isFinite(cn) && cn >= 0 && cn <= 36) collateralAssetDecimals = cn;
3136
+ try {
3137
+ const sym = await client.readContract({
3138
+ address: getAddress(cLow),
3139
+ abi: parseAbi(["function symbol() view returns (string)"]),
3140
+ functionName: "symbol"
3141
+ });
3142
+ const s = typeof sym === "string" ? sym.trim() : "";
3143
+ if (s) collateralUnderlyingSymbol = s;
3144
+ } catch {
3145
+ }
3146
+ } catch {
3147
+ }
3148
+ }
3149
+ let maxLtvBps = null;
3150
+ let liquidationLtvBps = null;
3151
+ if (collateralVault) {
3152
+ const risk = await fetchEulerV2BorrowPairLtvs({
3153
+ chainId: args.chainId,
3154
+ rpcUrl: rpc,
3155
+ borrowEvault: borrowVault,
3156
+ collateralEvault: collateralVault
3157
+ });
3158
+ if (risk) {
3159
+ maxLtvBps = risk.maxLtvBps;
3160
+ liquidationLtvBps = risk.liquidationLtvBps;
3161
+ }
3162
+ }
3163
+ let currentLtvPercent = null;
3164
+ try {
3165
+ const [collV, liabV] = await client.readContract({
3166
+ address: borrowVault,
3167
+ abi: eulerLiquidityAbi,
3168
+ functionName: "accountLiquidity",
3169
+ args: [subAccount, false]
3170
+ });
3171
+ if (collV > 0n && liabV > 0n) {
3172
+ currentLtvPercent = Number(liabV * 10000n / collV) / 100;
3173
+ }
3174
+ } catch {
3175
+ }
3176
+ let evc = (vrow?.evc ?? "").trim() ? getAddress(vrow.evc.trim()) : null;
3177
+ if (!evc) {
3178
+ try {
3179
+ const e = await client.readContract({
3180
+ address: borrowVault,
3181
+ abi: evcAbi5,
3182
+ functionName: "EVC"
3183
+ });
3184
+ evc = getAddress(e);
3185
+ } catch {
3186
+ evc = null;
3187
+ }
3188
+ }
3189
+ const marketName = [(vrow?.symbol ?? "").trim(), (vrow?.name ?? "").trim()].filter(Boolean).join(" \xB7 ") || `Borrow ${borrowVault.slice(0, 10)}\u2026`;
3190
+ out.push({
3191
+ id: `borrow-${args.chainId}-${subAccount.toLowerCase()}-${borrowVault.toLowerCase()}`,
3192
+ chainId: args.chainId,
3193
+ vaultId: (vrow?.id ?? borrowVault).toString(),
3194
+ evaultAddress: borrowVault,
3195
+ marketName,
3196
+ underlyingSymbol: (vrow?.symbol ?? "\u2014").toString().trim() || "\u2014",
3197
+ amountHuman: debtHuman,
3198
+ createdAtMs: t,
3199
+ signRequestId: null,
3200
+ positionKind: "borrow",
3201
+ subAccountAddress: subAccount,
3202
+ collateralEvaultAddress: collateralVault,
3203
+ evcAddress: evc,
3204
+ borrowAssetAddressLower: assetLow || null,
3205
+ borrowAssetDecimals: decFinal,
3206
+ borrowDebtHuman: debtHuman,
3207
+ collateralAssetAddressLower,
3208
+ collateralAssetDecimals,
3209
+ collateralUnderlyingSymbol,
3210
+ currentLtvPercent,
3211
+ maxLtvBps,
3212
+ liquidationLtvBps
3213
+ });
3214
+ }
3215
+ return out.sort((a, b) => (a.marketName || "").localeCompare(b.marketName || ""));
3216
+ }
3217
+ async function fetchEulerV2BorrowDebtWei(args) {
3218
+ const rpc = args.rpcUrl.trim();
3219
+ if (!rpc) return 0n;
3220
+ const client = metaClient2(args.chainId, rpc);
3221
+ const debtAbi = parseAbi(["function debtOf(address account) view returns (uint256)"]);
3222
+ try {
3223
+ return await client.readContract({
3224
+ address: args.borrowVault,
3225
+ abi: debtAbi,
3226
+ functionName: "debtOf",
3227
+ args: [args.subAccount]
3228
+ });
3229
+ } catch {
3230
+ return 0n;
3231
+ }
3232
+ }
3233
+ var VAULTS_FOR_EVAULTS2 = `
3234
+ query EulerVaultsForSupply($ids: [ID!]!) {
3235
+ eulerVaults(where: { id_in: $ids }) {
3236
+ id
3237
+ evault
3238
+ name
3239
+ symbol
3240
+ asset
3241
+ decimals
3242
+ }
3243
+ }`;
3244
+ var vaultSupplyAbi = parseAbi([
3245
+ "function balanceOf(address account) view returns (uint256)",
3246
+ "function convertToAssets(uint256 shares) view returns (uint256)",
3247
+ "function asset() view returns (address)"
3248
+ ]);
3249
+ var debtOfAbi = parseAbi(["function debtOf(address account) view returns (uint256)"]);
3250
+ function metaClient3(chainId, rpcUrl) {
3251
+ const chain = defineChain({
3252
+ id: chainId,
3253
+ name: "EulerLendSupply",
3254
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
3255
+ rpcUrls: { default: { http: [rpcUrl] } }
3256
+ });
3257
+ return createPublicClient({ chain, transport: http(rpcUrl) });
3258
+ }
3259
+ async function readSupplyUnderlyingWei(client, vault, shareOwner) {
3260
+ try {
3261
+ const shares = await client.readContract({
3262
+ address: vault,
3263
+ abi: vaultSupplyAbi,
3264
+ functionName: "balanceOf",
3265
+ args: [shareOwner]
3266
+ });
3267
+ if (shares === 0n) return null;
3268
+ const assets = await client.readContract({
3269
+ address: vault,
3270
+ abi: vaultSupplyAbi,
3271
+ functionName: "convertToAssets",
3272
+ args: [shares]
3273
+ });
3274
+ if (assets === 0n) return null;
3275
+ const asset = await client.readContract({
3276
+ address: vault,
3277
+ abi: vaultSupplyAbi,
3278
+ functionName: "asset"
3279
+ });
3280
+ return { asset: getAddress(asset), wei: assets };
3281
+ } catch {
3282
+ return null;
3283
+ }
3284
+ }
3285
+ async function loadEulerV2LendSupplyPositionsForOwner(args) {
3286
+ const rpc = args.rpcUrl.trim();
3287
+ if (!rpc) return [];
3288
+ if (!eulerV2GoldskyUrlForChain(args.chainId)) return [];
3289
+ const [entries, earnVaultAllowlist, gasAliases] = await Promise.all([
3290
+ fetchEulerV2TrackingDepositEntries({ chainId: args.chainId, mainAddress: args.mainAddress }),
3291
+ fetchEulerLabelsEarnVaultAllowlist(args.chainId),
3292
+ eulerWrappedGasTokenAliasesLower(args.chainId)
3293
+ ]);
3294
+ const pairs = [];
3295
+ for (const e of entries) {
3296
+ const p = parseEulerTrackingBorrowEntry(e);
3297
+ if (p) pairs.push({ subAccount: p.subAccount, evault: p.borrowVault });
3298
+ }
3299
+ if (!pairs.length) return [];
3300
+ const vaultIds = [...new Set(pairs.map((x) => x.evault.toLowerCase()))];
3301
+ const vaultData = await eulerV2QueryGraphQl(args.chainId, VAULTS_FOR_EVAULTS2, { ids: vaultIds });
3302
+ const byEvault = /* @__PURE__ */ new Map();
3303
+ for (const v of vaultData.eulerVaults ?? []) {
3304
+ const ev = (v.evault ?? "").trim().toLowerCase();
3305
+ if (ev) byEvault.set(ev, v);
3306
+ }
3307
+ const client = metaClient3(args.chainId, rpc);
3308
+ const t = Date.now();
3309
+ const out = [];
3310
+ const seenPair = /* @__PURE__ */ new Set();
3311
+ for (const { subAccount, evault } of pairs) {
3312
+ const pairKey = `${subAccount.toLowerCase()}-${evault.toLowerCase()}`;
3313
+ if (seenPair.has(pairKey)) continue;
3314
+ seenPair.add(pairKey);
3315
+ const supply = await readSupplyUnderlyingWei(client, evault, subAccount);
3316
+ if (!supply) continue;
3317
+ let liabilityDebtWei = 0n;
3318
+ try {
3319
+ liabilityDebtWei = await client.readContract({
3320
+ address: evault,
3321
+ abi: debtOfAbi,
3322
+ functionName: "debtOf",
3323
+ args: [subAccount]
3324
+ });
3325
+ } catch {
3326
+ liabilityDebtWei = 0n;
3327
+ }
3328
+ if (liabilityDebtWei > 0n) continue;
3329
+ const vrow = byEvault.get(evault.toLowerCase());
3330
+ const isEarn = earnVaultAllowlist.has(evault.toLowerCase());
3331
+ const positionKind = isEarn ? "earn" : "lend";
3332
+ const underlyingSymbol = (vrow?.symbol ?? "\u2014").toString().trim() || "\u2014";
3333
+ const marketName = [(vrow?.symbol ?? "").trim(), (vrow?.name ?? "").trim()].filter(Boolean).join(" \xB7 ") || `${isEarn ? "Earn" : "Lend"} ${evault.slice(0, 10)}\u2026`;
3334
+ let evChecksummed;
3335
+ try {
3336
+ evChecksummed = getAddress(evault);
3337
+ } catch {
3338
+ continue;
3339
+ }
3340
+ let vaultMeta;
3341
+ try {
3342
+ vaultMeta = await fetchEulerVaultUnderlyingMeta({
3343
+ rpcUrl: rpc,
3344
+ chainId: args.chainId,
3345
+ evault: evChecksummed
3346
+ });
3347
+ } catch {
3348
+ continue;
3349
+ }
3350
+ const decimals = clampEulerUnderlyingDecimalsForEulerUi({
3351
+ fetchedDecimals: vaultMeta.decimals,
3352
+ underlyingAssetLower: vaultMeta.asset.toLowerCase(),
3353
+ marketName,
3354
+ underlyingSymbol,
3355
+ wrappedGasAliasesLower: gasAliases
3356
+ });
3357
+ const amountHuman = formatUnits(supply.wei, decimals);
3358
+ out.push({
3359
+ id: `${positionKind}-supply-${args.chainId}-${subAccount.toLowerCase()}-${evault.toLowerCase()}`,
3360
+ chainId: args.chainId,
3361
+ vaultId: (vrow?.id ?? evault).toString(),
3362
+ evaultAddress: evault,
3363
+ marketName,
3364
+ underlyingSymbol,
3365
+ amountHuman,
3366
+ underlyingAssetsWei: supply.wei.toString(),
3367
+ underlyingAssetAddressLower: vaultMeta.asset.toLowerCase(),
3368
+ createdAtMs: t,
3369
+ signRequestId: null,
3370
+ positionKind,
3371
+ subAccountAddress: subAccount,
3372
+ underlyingAssetDecimals: decimals
3373
+ });
3374
+ }
3375
+ return out.sort((a, b) => (a.marketName || "").localeCompare(b.marketName || ""));
3376
+ }
3377
+ function mergeEulerV2PositionsTabRows(args) {
3378
+ const main = args.mainAddressLower.trim().toLowerCase();
3379
+ const keyOf = (p) => {
3380
+ const kind = p.positionKind ?? "lend";
3381
+ if (kind === "borrow") return p.id;
3382
+ const ev = p.evaultAddress.trim().toLowerCase();
3383
+ const sub = (p.subAccountAddress ?? main).trim().toLowerCase();
3384
+ return `supply|${ev}|${sub}`;
3385
+ };
3386
+ const chainKeys = new Set(args.chainSupply.map(keyOf));
3387
+ const sessionFiltered = args.session.filter((s) => !chainKeys.has(keyOf(s)));
3388
+ return [...args.borrow, ...args.chainSupply, ...sessionFiltered];
3389
+ }
3390
+ var REUL_LOCK_DAY_SEC = 86400n;
3391
+ var REUL_FULL_VEST_SEC = 180n * REUL_LOCK_DAY_SEC;
3392
+ var rewardTokenReadAbi = parseAbi([
3393
+ "function getLockedAmounts(address account) view returns (uint256[] lockTimestamps, uint256[] amounts)",
3394
+ "function getWithdrawAmountsByLockTimestamp(address account, uint256 lockTimestamp) view returns (uint256 accountAmount, uint256 remainderAmount)"
3395
+ ]);
3396
+ var eulerReulUnlockAbi = parseAbi([
3397
+ "function withdrawToByLockTimestamps(address account, uint256[] lockTimestamps, bool allowRemainderLoss) returns (bool)"
3398
+ ]);
3399
+ async function fetchEulerReulVestingTranches(args) {
3400
+ const rpc = args.rpcUrl.trim();
3401
+ if (!rpc) return [];
3402
+ const reul = getAddress(args.reulToken);
3403
+ const user = getAddress(args.user);
3404
+ const ch = defineChain({
3405
+ id: args.chainId,
3406
+ name: "ReulVest",
3407
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
3408
+ rpcUrls: { default: { http: [rpc] } }
3409
+ });
3410
+ const client = createPublicClient({ chain: ch, transport: http(rpc) });
3411
+ const [timestamps, amounts] = await client.readContract({
3412
+ address: reul,
3413
+ abi: rewardTokenReadAbi,
3414
+ functionName: "getLockedAmounts",
3415
+ args: [user]
3416
+ });
3417
+ const out = [];
3418
+ for (let i = 0; i < timestamps.length; i++) {
3419
+ const lockTs = timestamps[i];
3420
+ const lockedAmt = amounts[i];
3421
+ if (lockedAmt === 0n) continue;
3422
+ const [unlockToWalletWei, remainderWei] = await client.readContract({
3423
+ address: reul,
3424
+ abi: rewardTokenReadAbi,
3425
+ functionName: "getWithdrawAmountsByLockTimestamp",
3426
+ args: [user, lockTs]
3427
+ });
3428
+ out.push({
3429
+ reulToken: reul,
3430
+ lockTimestampSec: lockTs,
3431
+ lockedAmountWei: lockedAmt,
3432
+ unlockToWalletWei,
3433
+ remainderWei,
3434
+ fullVestTimestampSec: lockTs + REUL_FULL_VEST_SEC,
3435
+ isFullyVested: remainderWei === 0n
3436
+ });
3437
+ }
3438
+ return out;
3439
+ }
3440
+ function encodeEulerReulWithdrawToByLockTimestampsData(args) {
3441
+ const recipient = getAddress(args.recipient);
3442
+ if (args.lockTimestampsSec.length === 0) throw new Error("No rEUL lock timestamps to unlock.");
3443
+ return encodeFunctionData({
3444
+ abi: eulerReulUnlockAbi,
3445
+ functionName: "withdrawToByLockTimestamps",
3446
+ args: [recipient, [...args.lockTimestampsSec], args.allowRemainderLoss]
3447
+ });
3448
+ }
3449
+ var EULER_REUL_UNLOCK_GAS_FALLBACK = 500000n;
3450
+
3451
+ // src/chains/evm/coingecko.ts
3452
+ var COINGECKO_PLATFORM_BY_CHAIN_ID = {
3453
+ "1": "ethereum",
3454
+ "56": "binance-smart-chain",
3455
+ "137": "polygon-pos",
3456
+ "42161": "arbitrum-one",
3457
+ "10": "optimistic-ethereum",
3458
+ "43114": "avalanche",
3459
+ "8453": "base",
3460
+ "324": "zk-sync-era",
3461
+ "42220": "celo",
3462
+ "250": "fantom",
3463
+ "100": "gnosis",
3464
+ "204": "op-bnb",
3465
+ "534352": "scroll",
3466
+ "5000": "mantle",
3467
+ "169": "manta-pacific",
3468
+ "1116": "core",
3469
+ "30": "rootstock",
3470
+ "288": "boba",
3471
+ "1088": "metis-andromeda",
3472
+ "34443": "mode",
3473
+ "80084": "berachain",
3474
+ "146": "sonic",
3475
+ "60808": "bob-network",
3476
+ "80094": "berachain",
3477
+ "130": "unichain",
3478
+ "57073": "ink",
3479
+ "999": "hyperevm",
3480
+ "239": "tac",
3481
+ "9745": "plasma",
3482
+ "1923": "swellchain",
3483
+ "59144": "linea",
3484
+ "81457": "blast",
3485
+ "7777777": "zora"
3486
+ };
3487
+
3488
+ // src/protocols/evm/euler-v2/eulerV2PortfolioUsd.ts
3489
+ var TRACKING_ACCOUNT = `
3490
+ query EulerTrackingAccount($id: Bytes!) {
3491
+ trackingActiveAccount(id: $id) {
3492
+ deposits
3493
+ borrows
3494
+ }
3495
+ }`;
3496
+ var vaultSupplyAbi2 = parseAbi([
3497
+ "function balanceOf(address account) view returns (uint256)",
3498
+ "function convertToAssets(uint256 shares) view returns (uint256)",
3499
+ "function asset() view returns (address)"
3500
+ ]);
3501
+ var erc20DecimalsAbi3 = parseAbi(["function decimals() view returns (uint8)"]);
3502
+ var debtOfAbi2 = parseAbi(["function debtOf(address account) view returns (uint256)"]);
3503
+ function publicClient(chainId, rpcUrl) {
3504
+ const ch = defineChain({
3505
+ id: chainId,
3506
+ name: "EulerPortfolio",
3507
+ nativeCurrency: { decimals: 18, name: "x", symbol: "x" },
3508
+ rpcUrls: { default: { http: [rpcUrl] } }
3509
+ });
3510
+ return createPublicClient({ chain: ch, transport: http(rpcUrl) });
3511
+ }
3512
+ async function gqlTrackingAccount(chainId, mainLower) {
3513
+ if (!eulerV2GoldskyUrlForChain(chainId)) {
3514
+ return { deposits: [], borrows: [] };
3515
+ }
3516
+ const data = await eulerV2QueryGraphQl(chainId, TRACKING_ACCOUNT, { id: mainLower });
3517
+ const a = data.trackingActiveAccount;
3518
+ const dep = (a?.deposits ?? []).filter((x) => typeof x === "string" && x.trim().length > 0);
3519
+ const bor = (a?.borrows ?? []).filter((x) => typeof x === "string" && x.trim().length > 0);
3520
+ return { deposits: dep, borrows: bor };
3521
+ }
3522
+ async function decimalsForAsset(client, asset) {
3523
+ try {
3524
+ const d = await client.readContract({
3525
+ address: asset,
3526
+ abi: erc20DecimalsAbi3,
3527
+ functionName: "decimals"
3528
+ });
3529
+ const n = Number(d);
3530
+ if (Number.isFinite(n) && n >= 0 && n <= 36) return n;
3531
+ } catch {
3532
+ }
3533
+ return 18;
3534
+ }
3535
+ async function readSupplyUnderlyingWei2(client, vault, subAccount) {
3536
+ try {
3537
+ const shares = await client.readContract({
3538
+ address: vault,
3539
+ abi: vaultSupplyAbi2,
3540
+ functionName: "balanceOf",
3541
+ args: [subAccount]
3542
+ });
3543
+ if (shares === 0n) return null;
3544
+ const assets = await client.readContract({
3545
+ address: vault,
3546
+ abi: vaultSupplyAbi2,
3547
+ functionName: "convertToAssets",
3548
+ args: [shares]
3549
+ });
3550
+ if (assets === 0n) return null;
3551
+ const asset = await client.readContract({
3552
+ address: vault,
3553
+ abi: vaultSupplyAbi2,
3554
+ functionName: "asset"
3555
+ });
3556
+ return { asset: getAddress(asset), wei: assets };
3557
+ } catch {
3558
+ return null;
3559
+ }
3560
+ }
3561
+ async function readBorrowDebtWei(client, borrowVault, subAccount) {
3562
+ try {
3563
+ const debt = await client.readContract({
3564
+ address: borrowVault,
3565
+ abi: debtOfAbi2,
3566
+ functionName: "debtOf",
3567
+ args: [subAccount]
3568
+ });
3569
+ if (debt === 0n) return null;
3570
+ const asset = await client.readContract({
3571
+ address: borrowVault,
3572
+ abi: vaultSupplyAbi2,
3573
+ functionName: "asset"
3574
+ });
3575
+ return { asset: getAddress(asset), wei: debt };
3576
+ } catch {
3577
+ return null;
3578
+ }
3579
+ }
3580
+ function mergeWei(into, asset, wei) {
3581
+ const k = asset.toLowerCase();
3582
+ into.set(k, (into.get(k) ?? 0n) + wei);
3583
+ }
3584
+ async function fetchCoingeckoContractUsdFromDetail(args) {
3585
+ const a = args.contractAddressLower.trim().toLowerCase();
3586
+ if (!a.startsWith("0x")) return null;
3587
+ const url = `https://api.coingecko.com/api/v3/coins/${encodeURIComponent(args.platform)}/contract/${a}`;
3588
+ try {
3589
+ const r = await fetch(url, { cache: "no-store" });
3590
+ if (!r.ok) return null;
3591
+ const j = await r.json();
3592
+ const u = j.market_data?.current_price?.usd;
3593
+ if (typeof u !== "number" || !Number.isFinite(u) || u < 0) return null;
3594
+ return u;
3595
+ } catch {
3596
+ return null;
3597
+ }
3598
+ }
3599
+ async function augmentMissingTokenPrices(args) {
3600
+ const missing = args.addressesLower.map((x) => x.trim().toLowerCase()).filter((x) => x.startsWith("0x") && !args.prices.has(x));
3601
+ for (const addr of missing.slice(0, 12)) {
3602
+ const u = await fetchCoingeckoContractUsdFromDetail({ platform: args.platform, contractAddressLower: addr });
3603
+ if (u != null) args.prices.set(addr, u);
3604
+ }
3605
+ }
3606
+ var COINGECKO_CHUNK = 45;
3607
+ async function fetchCoingeckoTokenUsdByContract(args) {
3608
+ const platform = COINGECKO_PLATFORM_BY_CHAIN_ID[String(args.chainId)];
3609
+ const out = /* @__PURE__ */ new Map();
3610
+ if (!platform || args.contractAddressesLower.length === 0) return out;
3611
+ const uniq = [...new Set(args.contractAddressesLower.map((a) => a.trim().toLowerCase()).filter((a) => a.startsWith("0x")))];
3612
+ for (let i = 0; i < uniq.length; i += COINGECKO_CHUNK) {
3613
+ const chunk = uniq.slice(i, i + COINGECKO_CHUNK);
3614
+ const url = `https://api.coingecko.com/api/v3/simple/token_price/${encodeURIComponent(platform)}?contract_addresses=${chunk.join(",")}&vs_currencies=usd`;
3615
+ try {
3616
+ const proxy = getCoingeckoProxyUrl();
3617
+ const r = proxy ? await fetch(proxy, {
3618
+ method: "POST",
3619
+ headers: { "content-type": "application/json" },
3620
+ body: JSON.stringify({ platform, contractAddresses: chunk })
3621
+ }) : await fetch(url, { cache: "no-store" });
3622
+ if (!r.ok) continue;
3623
+ const j = await r.json();
3624
+ for (const [addr, row] of Object.entries(j)) {
3625
+ const u = row?.usd;
3626
+ if (typeof u === "number" && Number.isFinite(u) && u >= 0) out.set(addr.toLowerCase(), u);
3627
+ }
3628
+ } catch {
3629
+ }
3630
+ }
3631
+ return out;
3632
+ }
3633
+ function sumSideUsd(args) {
3634
+ let partial = false;
3635
+ let total = 0;
3636
+ let anyPriced = false;
3637
+ let anyUnpricedNonZero = false;
3638
+ const entries = [...args.byAssetWei.entries()].filter(([, w]) => w > 0n);
3639
+ if (entries.length === 0) return { usd: 0, partial: false };
3640
+ for (const [assetLow, wei] of entries) {
3641
+ const dec = args.decimalsByAsset.get(assetLow) ?? 18;
3642
+ const p = args.prices.get(assetLow);
3643
+ if (p == null) {
3644
+ partial = true;
3645
+ anyUnpricedNonZero = true;
3646
+ continue;
3647
+ }
3648
+ let humanAmt;
3649
+ try {
3650
+ humanAmt = Number.parseFloat(formatUnits(wei, dec));
3651
+ } catch {
3652
+ partial = true;
3653
+ anyUnpricedNonZero = true;
3654
+ continue;
3655
+ }
3656
+ const slice = humanAmt * p;
3657
+ if (!Number.isFinite(humanAmt) || !Number.isFinite(p) || !Number.isFinite(slice)) {
3658
+ partial = true;
3659
+ anyUnpricedNonZero = true;
3660
+ continue;
3661
+ }
3662
+ anyPriced = true;
3663
+ total += slice;
3664
+ }
3665
+ if (anyUnpricedNonZero && !anyPriced) return { usd: null, partial: true };
3666
+ if (anyUnpricedNonZero) partial = true;
3667
+ if (!Number.isFinite(total)) return { usd: null, partial: true };
3668
+ return { usd: total, partial };
3669
+ }
3670
+ async function loadEulerV2PortfolioUsdSnapshot(args) {
3671
+ const rpc = args.rpcUrl.trim();
3672
+ const empty = {
3673
+ totalSuppliedUsd: null,
3674
+ totalBorrowedUsd: null,
3675
+ netAssetValueUsd: null,
3676
+ partialPricing: false,
3677
+ pricingUnavailable: false,
3678
+ error: null
3679
+ };
3680
+ if (!rpc) {
3681
+ return { ...empty, error: "No RPC URL." };
3682
+ }
3683
+ const platform = COINGECKO_PLATFORM_BY_CHAIN_ID[String(args.chainId)];
3684
+ try {
3685
+ const mainLower = args.mainAddress.toLowerCase();
3686
+ const { deposits, borrows } = await gqlTrackingAccount(args.chainId, mainLower);
3687
+ const client = publicClient(args.chainId, rpc);
3688
+ const suppliedWeiByAsset = /* @__PURE__ */ new Map();
3689
+ const borrowedWeiByAsset = /* @__PURE__ */ new Map();
3690
+ const borrowPairSeen = /* @__PURE__ */ new Set();
3691
+ const borrowPairs = [];
3692
+ const pushBorrowPair = (sub, vault) => {
3693
+ const k = `${sub.toLowerCase()}-${vault.toLowerCase()}`;
3694
+ if (borrowPairSeen.has(k)) return;
3695
+ borrowPairSeen.add(k);
3696
+ borrowPairs.push({ subAccount: sub, vault });
3697
+ };
3698
+ const supplyReads = [];
3699
+ for (const entry of deposits) {
3700
+ const p = parseEulerTrackingBorrowEntry(entry);
3701
+ if (!p) continue;
3702
+ pushBorrowPair(p.subAccount, p.borrowVault);
3703
+ supplyReads.push(
3704
+ (async () => {
3705
+ const r = await readSupplyUnderlyingWei2(client, p.borrowVault, p.subAccount);
3706
+ if (!r) return;
3707
+ mergeWei(suppliedWeiByAsset, r.asset, r.wei);
3708
+ })()
3709
+ );
3710
+ }
3711
+ for (const entry of borrows) {
3712
+ const p = parseEulerTrackingBorrowEntry(entry);
3713
+ if (!p) continue;
3714
+ pushBorrowPair(p.subAccount, p.borrowVault);
3715
+ }
3716
+ const borrowReads = borrowPairs.map(
3717
+ ({ subAccount, vault }) => (async () => {
3718
+ const r = await readBorrowDebtWei(client, vault, subAccount);
3719
+ if (!r) return;
3720
+ mergeWei(borrowedWeiByAsset, r.asset, r.wei);
3721
+ })()
3722
+ );
3723
+ await Promise.all([...supplyReads, ...borrowReads]);
3724
+ const allAssets = /* @__PURE__ */ new Set();
3725
+ for (const [a, w] of suppliedWeiByAsset) if (w > 0n) allAssets.add(a.toLowerCase());
3726
+ for (const [a, w] of borrowedWeiByAsset) if (w > 0n) allAssets.add(a.toLowerCase());
3727
+ const decimalsByAsset = /* @__PURE__ */ new Map();
3728
+ for (const low of allAssets) {
3729
+ if (!isAddress(low)) continue;
3730
+ const dec = await decimalsForAsset(client, getAddress(low));
3731
+ decimalsByAsset.set(low.toLowerCase(), dec);
3732
+ }
3733
+ const prices = platform != null ? await fetchCoingeckoTokenUsdByContract({
3734
+ chainId: args.chainId,
3735
+ contractAddressesLower: [...allAssets]
3736
+ }) : /* @__PURE__ */ new Map();
3737
+ if (platform != null && allAssets.size > 0) {
3738
+ await augmentMissingTokenPrices({
3739
+ platform,
3740
+ addressesLower: [...allAssets],
3741
+ prices
3742
+ });
3743
+ }
3744
+ const s = sumSideUsd({ byAssetWei: suppliedWeiByAsset, decimalsByAsset, prices });
3745
+ const b = sumSideUsd({ byAssetWei: borrowedWeiByAsset, decimalsByAsset, prices });
3746
+ const sUsd = s.usd != null && Number.isFinite(s.usd) ? s.usd : null;
3747
+ const bUsd = b.usd != null && Number.isFinite(b.usd) ? b.usd : null;
3748
+ let nav = null;
3749
+ if (sUsd != null && bUsd != null) nav = sUsd - bUsd;
3750
+ else if (sUsd != null && (borrowedWeiByAsset.size === 0 || [...borrowedWeiByAsset.values()].every((w) => w === 0n))) {
3751
+ nav = sUsd;
3752
+ } else if (bUsd != null && (suppliedWeiByAsset.size === 0 || [...suppliedWeiByAsset.values()].every((w) => w === 0n))) {
3753
+ nav = -bUsd;
3754
+ }
3755
+ if (nav != null && !Number.isFinite(nav)) nav = null;
3756
+ const hasAnyPosition = [...suppliedWeiByAsset.values(), ...borrowedWeiByAsset.values()].some((w) => w > 0n);
3757
+ const partialPricing = s.partial || b.partial;
3758
+ const pricingUnavailable = platform == null && hasAnyPosition;
3759
+ return {
3760
+ totalSuppliedUsd: sUsd,
3761
+ totalBorrowedUsd: bUsd,
3762
+ netAssetValueUsd: nav,
3763
+ partialPricing: platform != null && partialPricing,
3764
+ pricingUnavailable,
3765
+ error: null
3766
+ };
3767
+ } catch (e) {
3768
+ return {
3769
+ ...empty,
3770
+ error: e instanceof Error ? e.message : "Could not load portfolio."
3771
+ };
3772
+ }
3773
+ }
3774
+
3775
+ // src/protocols/evm/euler-v2/loadEulerV2SupportedChainIds.ts
3776
+ var cached = null;
3777
+ function loadEulerV2SupportedChainIds() {
3778
+ if (cached) return Promise.resolve(cached);
3779
+ cached = new Set(Object.keys(EULER_V2_GOLDSKY_SUBGRAPH_URL_BY_CHAIN_ID).map((k) => Number(k)));
3780
+ return Promise.resolve(cached);
2212
3781
  }
2213
3782
 
2214
3783
  // src/protocols/evm/euler-v2/index.ts
@@ -2233,6 +3802,6 @@ var eulerV2ProtocolModule = {
2233
3802
  };
2234
3803
  registerProtocolModule(eulerV2ProtocolModule);
2235
3804
 
2236
- export { EULER_SAME_ASSET_BORROW_APPROVAL_BUFFER_BPS, EULER_SAME_ASSET_BORROW_MAX_ROUNDS, EULER_SAME_ASSET_BORROW_PROTOCOL_HEADROOM_BPS, EULER_SAME_ASSET_BORROW_RATIO_STOP_EPS_BPS, EULER_V2_ISOLATED_VAULT_DEPOSIT_FALLBACK_GAS, EULER_V2_PROTOCOL_ID, EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS, buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch, buildEvmMultisignBodyEulerV2BorrowCollateralWithdrawBatch, buildEvmMultisignBodyEulerV2BorrowRepayBatch, buildEvmMultisignBodyEulerV2IsolatedBorrowBatch, buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch, buildEvmMultisignBodyEulerV2VaultWithdraw, clampEulerUnderlyingDecimalsForEulerUi, eulerBorrowAndCollateralSameAsset, eulerSameAssetApproveAmountWithBuffer, eulerSameAssetTotalCollateralPullWei, eulerV2ProtocolModule, fetchEulerBorrowCollateralMaxWithdrawAssetsWei, fetchEulerLendEarnVaultEffectiveMaxWithdrawWei, fetchEulerVaultAssetDecimals, fetchEulerVaultMaxWithdrawWei, fetchEulerVaultUnderlyingMeta, planSameAssetLeveragedBorrows };
3805
+ export { EULER_EVAULT_CAP_UNLIMITED_WEI, EULER_EVAULT_SUPPLY_CAP_UNLIMITED_WEI, EULER_REUL_UNLOCK_GAS_FALLBACK, EULER_SAME_ASSET_BORROW_APPROVAL_BUFFER_BPS, EULER_SAME_ASSET_BORROW_MAX_ROUNDS, EULER_SAME_ASSET_BORROW_PROTOCOL_HEADROOM_BPS, EULER_SAME_ASSET_BORROW_RATIO_STOP_EPS_BPS, EULER_V2_GOLDSKY_SUBGRAPH_URL_BY_CHAIN_ID, EULER_V2_ISOLATED_VAULT_DEPOSIT_FALLBACK_GAS, EULER_V2_PREFER_ON_CHAIN_LEND_METRICS, EULER_V2_PROTOCOL_ID, EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS, REUL_FULL_VEST_SEC, REUL_LOCK_DAY_SEC, buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch, buildEvmMultisignBodyEulerV2BorrowCollateralWithdrawBatch, buildEvmMultisignBodyEulerV2BorrowRepayBatch, buildEvmMultisignBodyEulerV2IsolatedBorrowBatch, buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch, buildEvmMultisignBodyEulerV2VaultWithdraw, clampEulerUnderlyingDecimalsForEulerUi, encodeEulerReulWithdrawToByLockTimestampsData, ensureEulerV2ChainAssetCache, estimateBorrowHumanFromLtv, eulerBorrowAndCollateralSameAsset, eulerLabelsAppendUnderlyingIfDistinct, eulerLabelsLookupLabelForVault, eulerReulUnlockAbi, eulerSameAssetApproveAmountWithBuffer, eulerSameAssetTotalCollateralPullWei, eulerSubgraphVaultCapToUnderlyingWei, eulerV2GoldskyUrlForChain, eulerV2KeyForNodeAssetRow, eulerV2ProtocolModule, eulerV2QueryGraphQl, eulerVaultLendMetricsFromIndexerVaultState, eulerVaultMaxNewBorrowWei, eulerVaultMaxNewSupplyWei, eulerWrappedGasTokenAliasesLower, fetchCoingeckoTokenUsdByContract, fetchEulerBorrowCollateralMaxWithdrawAssetsWei, fetchEulerLabelsEarnVaultAllowlist, fetchEulerLabelsEntityNameByAddress, fetchEulerLabelsVaultNameMap, fetchEulerLendEarnVaultEffectiveMaxWithdrawWei, fetchEulerReulVestingTranches, fetchEulerV2BorrowDebtWei, fetchEulerV2BorrowOracleQuoteSnapshot, fetchEulerV2BorrowPairLtvs, fetchEulerV2ChainAssetCache, fetchEulerV2EarnOfferingsForRowAsset, fetchEulerV2IsolatedLendMarketsForRowAsset, fetchEulerV2TrackingBorrowEntries, fetchEulerV2TrackingDepositEntries, fetchEulerVaultAssetDecimals, fetchEulerVaultMaxWithdrawWei, fetchEulerVaultOnChainLendMetrics, fetchEulerVaultOnChainLendMetricsForVaults, fetchEulerVaultUnderlyingMeta, flattenEulerV2BorrowCollateralRows, formatEulerRayApyPercent, formatEulerRaySupplyApyPercent, formatEulerResolvedCapOrCashForUi, loadEulerV2BorrowPositionsForOwner, loadEulerV2LendSupplyPositionsForOwner, loadEulerV2PortfolioUsdSnapshot, loadEulerV2SupportedChainIds, maxCollateralWeiCappedByBorrowLiquidity, mergeEulerV2PositionsTabRows, parseEulerTrackingBorrowEntry, planSameAssetLeveragedBorrows, resolveEulerAmountCapToSupplyWei, resolveEulerAmountCapToWei, resolveEulerWrappedNativeToken };
2237
3806
  //# sourceMappingURL=index.js.map
2238
3807
  //# sourceMappingURL=index.js.map