@continuumdao/ctm-mpc-defi 0.1.3 → 0.2.0

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 (68) hide show
  1. package/README.md +16 -1
  2. package/dist/agent/catalog.cjs +913 -142
  3. package/dist/agent/catalog.cjs.map +1 -1
  4. package/dist/agent/catalog.d.cts +834 -12
  5. package/dist/agent/catalog.d.ts +834 -12
  6. package/dist/agent/catalog.js +863 -143
  7. package/dist/agent/catalog.js.map +1 -1
  8. package/dist/chains/evm/index.cjs +13 -0
  9. package/dist/chains/evm/index.cjs.map +1 -1
  10. package/dist/chains/evm/index.d.cts +3 -1
  11. package/dist/chains/evm/index.d.ts +3 -1
  12. package/dist/chains/evm/index.js +13 -1
  13. package/dist/chains/evm/index.js.map +1 -1
  14. package/dist/core/index.cjs +76 -0
  15. package/dist/core/index.cjs.map +1 -1
  16. package/dist/core/index.d.cts +35 -2
  17. package/dist/core/index.d.ts +35 -2
  18. package/dist/core/index.js +70 -1
  19. package/dist/core/index.js.map +1 -1
  20. package/dist/index.cjs +934 -141
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +3 -2
  23. package/dist/index.d.ts +3 -2
  24. package/dist/index.js +927 -142
  25. package/dist/index.js.map +1 -1
  26. package/dist/protocols/evm/aave-v4/index.cjs +1987 -0
  27. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -0
  28. package/dist/protocols/evm/aave-v4/index.d.cts +500 -0
  29. package/dist/protocols/evm/aave-v4/index.d.ts +500 -0
  30. package/dist/protocols/evm/aave-v4/index.js +1943 -0
  31. package/dist/protocols/evm/aave-v4/index.js.map +1 -0
  32. package/dist/protocols/evm/ethena/index.cjs +965 -0
  33. package/dist/protocols/evm/ethena/index.cjs.map +1 -0
  34. package/dist/protocols/evm/ethena/index.d.cts +161 -0
  35. package/dist/protocols/evm/ethena/index.d.ts +161 -0
  36. package/dist/protocols/evm/ethena/index.js +943 -0
  37. package/dist/protocols/evm/ethena/index.js.map +1 -0
  38. package/dist/protocols/evm/euler-v2/index.cjs +2263 -0
  39. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -0
  40. package/dist/protocols/evm/euler-v2/index.d.cts +317 -0
  41. package/dist/protocols/evm/euler-v2/index.d.ts +317 -0
  42. package/dist/protocols/evm/euler-v2/index.js +2238 -0
  43. package/dist/protocols/evm/euler-v2/index.js.map +1 -0
  44. package/dist/protocols/evm/lido/index.cjs +834 -0
  45. package/dist/protocols/evm/lido/index.cjs.map +1 -0
  46. package/dist/protocols/evm/lido/index.d.cts +120 -0
  47. package/dist/protocols/evm/lido/index.d.ts +120 -0
  48. package/dist/protocols/evm/lido/index.js +809 -0
  49. package/dist/protocols/evm/lido/index.js.map +1 -0
  50. package/dist/protocols/evm/maple/index.cjs +707 -0
  51. package/dist/protocols/evm/maple/index.cjs.map +1 -0
  52. package/dist/protocols/evm/maple/index.d.cts +109 -0
  53. package/dist/protocols/evm/maple/index.d.ts +109 -0
  54. package/dist/protocols/evm/maple/index.js +693 -0
  55. package/dist/protocols/evm/maple/index.js.map +1 -0
  56. package/dist/protocols/evm/sky/index.cjs +1254 -0
  57. package/dist/protocols/evm/sky/index.cjs.map +1 -0
  58. package/dist/protocols/evm/sky/index.d.cts +218 -0
  59. package/dist/protocols/evm/sky/index.d.ts +218 -0
  60. package/dist/protocols/evm/sky/index.js +1229 -0
  61. package/dist/protocols/evm/sky/index.js.map +1 -0
  62. package/dist/protocols/evm/uniswap-v4/index.cjs +31 -11
  63. package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
  64. package/dist/protocols/evm/uniswap-v4/index.d.cts +2 -1
  65. package/dist/protocols/evm/uniswap-v4/index.d.ts +2 -1
  66. package/dist/protocols/evm/uniswap-v4/index.js +31 -11
  67. package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
  68. package/package.json +37 -3
@@ -0,0 +1,1987 @@
1
+ 'use strict';
2
+
3
+ var viem = require('viem');
4
+
5
+ // src/core/registry.ts
6
+ var modules = [];
7
+ function registerProtocolModule(mod) {
8
+ const existing = modules.findIndex((m) => m.id === mod.id);
9
+ if (existing >= 0) {
10
+ modules[existing] = mod;
11
+ } else {
12
+ modules.push(mod);
13
+ }
14
+ }
15
+ var AAVE_V4_GRAPHQL_URL = "https://api.v4.aave.com/graphql";
16
+ async function aaveV4Gql(query, variables) {
17
+ const r = await fetch(AAVE_V4_GRAPHQL_URL, {
18
+ method: "POST",
19
+ headers: { "content-type": "application/json" },
20
+ body: JSON.stringify({ query, variables: variables ?? {} })
21
+ });
22
+ if (!r.ok) {
23
+ const t = await r.text().catch(() => "");
24
+ throw new Error(t ? `Aave V4 API HTTP ${r.status}: ${t.slice(0, 200)}` : `Aave V4 API HTTP ${r.status}`);
25
+ }
26
+ const j = await r.json();
27
+ if (j.errors?.length) {
28
+ const msg = j.errors.map((e) => e.message ?? "Unknown").join("; ");
29
+ throw new Error(msg);
30
+ }
31
+ if (j.data == null) {
32
+ throw new Error("Aave V4 API: empty response");
33
+ }
34
+ return j.data;
35
+ }
36
+ async function fetchAaveV4Chains() {
37
+ const d = await aaveV4Gql(`
38
+ query C($c: ChainsRequest!) { chains(request: $c) { chainId name nativeWrappedToken } }
39
+ `, { c: { query: { filter: "ALL" } } });
40
+ return d.chains ?? [];
41
+ }
42
+ async function loadAaveV4SupportedChainIdsFromV4Api() {
43
+ const rows = await fetchAaveV4Chains();
44
+ const s = /* @__PURE__ */ new Set();
45
+ for (const c of rows) s.add(c.chainId);
46
+ return s;
47
+ }
48
+ var RESERVES_ADDRESS_FRAGMENT = `
49
+ asset { underlying { address } }
50
+ `;
51
+ function aggregateV4SupplyDisplay(symbolForDisplay, rows) {
52
+ const sym = (symbolForDisplay ?? "").trim();
53
+ const withSuffix = (n) => n === "\u2014" ? "\u2014" : sym ? `${n} ${sym}` : n;
54
+ if (!rows.length) {
55
+ return { depositedAmount: "\u2014", apy: "\u2014", totalDeposits: "\u2014", availableLiquidity: "\u2014" };
56
+ }
57
+ const apy = formatV4SupplyApy(rows);
58
+ const sumKey = (key) => sumNumberValues(
59
+ rows.map((r) => {
60
+ const v = key === "supplied" ? r.summary?.supplied?.amount : r.summary?.borrowed?.amount;
61
+ return (v?.value ?? "").trim();
62
+ })
63
+ );
64
+ const totalSupN = sumKey("supplied");
65
+ const totalBorN = sumKey("borrowed");
66
+ const totalDeposits = totalSupN == null ? "\u2014" : withSuffix(formatAaveV4TokenAmount(totalSupN));
67
+ const availableLiquidity = (() => {
68
+ if (totalSupN == null || totalBorN == null) return "\u2014";
69
+ const a = totalSupN - totalBorN;
70
+ if (!Number.isFinite(a)) return "\u2014";
71
+ return withSuffix(formatAaveV4TokenAmount(a < 0 ? 0 : a));
72
+ })();
73
+ const userN = userSupplyBalanceNumber(rows);
74
+ const depositedAmount = userN == null ? "\u2014" : withSuffix(formatAaveV4TokenAmount(userN));
75
+ return { depositedAmount, apy, totalDeposits, availableLiquidity };
76
+ }
77
+ function sumNumberValues(values) {
78
+ if (!values.length) return null;
79
+ let s = 0;
80
+ for (const v of values) {
81
+ const n = parseFloat(v);
82
+ if (Number.isFinite(n)) s += n;
83
+ }
84
+ return s;
85
+ }
86
+ function formatAaveV4TokenAmount(n) {
87
+ if (!Number.isFinite(n)) return "\u2014";
88
+ if (n === 0) return "0";
89
+ const sign = n < 0 ? "-" : "";
90
+ const a = Math.abs(n);
91
+ const strip = (s) => s.replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "");
92
+ if (a >= 1e6) {
93
+ return sign + strip((a / 1e6).toFixed(2)) + "M";
94
+ }
95
+ if (a >= 1e3) {
96
+ const k = a / 1e3;
97
+ const d = k >= 100 ? 1 : 2;
98
+ return sign + strip(k.toFixed(d)) + "k";
99
+ }
100
+ if (a >= 1) {
101
+ return sign + strip(a.toFixed(2));
102
+ }
103
+ if (a >= 1e-4) {
104
+ return sign + strip(a.toFixed(4));
105
+ }
106
+ return sign + a.toExponential(2);
107
+ }
108
+ function formatAaveV4AmountHumanFromApiString(raw) {
109
+ const s = (raw ?? "").trim();
110
+ if (!s) return "\u2014";
111
+ const n = parseFloat(s);
112
+ if (!Number.isFinite(n)) return "\u2014";
113
+ return formatAaveV4TokenAmount(n);
114
+ }
115
+ function formatAaveV4ReserveLiquidityFromSummary(args) {
116
+ const a = parseFloat(String(args.suppliedValue ?? "").trim());
117
+ const b = parseFloat(String(args.borrowedValue ?? "").trim());
118
+ if (!Number.isFinite(a) || !Number.isFinite(b)) return "\u2014";
119
+ return formatAaveV4TokenAmount(Math.max(0, a - b));
120
+ }
121
+ function userSupplyBalanceNumber(rows) {
122
+ const vals = rows.map((r) => (r.userState?.balance?.amount?.value ?? "").trim()).filter((v) => v.length > 0);
123
+ if (!vals.length) return null;
124
+ const u = new Set(vals);
125
+ if (u.size === 1) {
126
+ const n = parseFloat(vals[0]);
127
+ return Number.isFinite(n) ? n : null;
128
+ }
129
+ const sum = vals.reduce((acc, b) => acc + (parseFloat(b) || 0), 0);
130
+ return Number.isFinite(sum) ? sum : null;
131
+ }
132
+ function formatV4SupplyApy(rows) {
133
+ if (!rows.length) return "\u2014";
134
+ const weighted = [];
135
+ for (const r of rows) {
136
+ const sup = parseFloat((r.summary?.supplied?.amount?.value ?? "").trim());
137
+ const norm = (r.summary?.supplyApy?.normalized ?? "").trim();
138
+ const n2 = parseFloat(norm);
139
+ if (Number.isFinite(n2) && Number.isFinite(sup) && sup > 0) {
140
+ weighted.push({ w: sup, apy: n2 });
141
+ }
142
+ }
143
+ if (weighted.length) {
144
+ const tw = weighted.reduce((a, x) => a + x.w, 0);
145
+ if (tw > 0) {
146
+ const x = weighted.reduce((a, x2) => a + x2.w * x2.apy, 0) / tw;
147
+ if (x >= 0.01) return `${x.toFixed(2)}%`;
148
+ if (x > 0) return `${x.toFixed(4)}%`;
149
+ return "0%";
150
+ }
151
+ }
152
+ const any = (rows[0]?.summary?.supplyApy?.normalized ?? "").trim();
153
+ const n = parseFloat(any);
154
+ if (!Number.isFinite(n)) return "\u2014";
155
+ if (n >= 0.01) return `${n.toFixed(2)}%`;
156
+ if (n > 0) return `${n.toFixed(4)}%`;
157
+ return "0%";
158
+ }
159
+ async function fetchAaveV4NativeWrappedToken(chainId) {
160
+ const all = await fetchAaveV4Chains();
161
+ const c = all.find((x) => x.chainId === chainId);
162
+ const t = (c?.nativeWrappedToken ?? "").trim();
163
+ if (t && viem.isAddress(t)) return viem.getAddress(t);
164
+ return null;
165
+ }
166
+ async function fetchAaveV4SupportedUnderlyingAddressSet(chainId) {
167
+ const d = await aaveV4Gql(
168
+ `
169
+ query A($r: ReservesRequest!) { reserves(request: $r) { ${RESERVES_ADDRESS_FRAGMENT} } }
170
+ `,
171
+ {
172
+ r: {
173
+ query: { chainIds: [chainId] },
174
+ filter: "ALL",
175
+ orderBy: { supplyApy: "DESC" }
176
+ }
177
+ }
178
+ );
179
+ const s = /* @__PURE__ */ new Set();
180
+ for (const r of d.reserves ?? []) {
181
+ const a = (r.asset?.underlying?.address ?? "").trim();
182
+ if (a && viem.isAddress(a)) s.add(a.toLowerCase());
183
+ }
184
+ return s;
185
+ }
186
+ function erc20Input(address, chainId) {
187
+ return { address, chainId };
188
+ }
189
+ async function fetchAaveV4ReservesForUnderlying(args) {
190
+ const d = await aaveV4Gql(
191
+ `
192
+ query S($r: ReservesRequest!) {
193
+ reserves(request: $r) {
194
+ id
195
+ onChainId
196
+ spoke { address name liquidationConfig { targetHealthFactor healthFactorForMaxBonus } }
197
+ summary {
198
+ supplied { amount { value decimals } }
199
+ borrowed { amount { value decimals } }
200
+ suppliable { amount { value decimals } }
201
+ supplyApy { normalized value }
202
+ }
203
+ userState {
204
+ balance { amount { value decimals } }
205
+ borrowable { amount { value decimals } }
206
+ }
207
+ }
208
+ }
209
+ `,
210
+ {
211
+ r: {
212
+ query: { tokens: [erc20Input(args.underlying, args.chainId)] },
213
+ filter: "ALL",
214
+ orderBy: { supplyApy: "DESC" },
215
+ ...args.user ? { user: args.user } : {}
216
+ }
217
+ }
218
+ );
219
+ return d.reserves ?? [];
220
+ }
221
+ function pickAaveV4ReserveRowForSpoke(rows, spokeAddress) {
222
+ const want = viem.getAddress(spokeAddress).toLowerCase();
223
+ for (const r of rows) {
224
+ const a = (r.spoke?.address ?? "").trim();
225
+ if (!a) continue;
226
+ try {
227
+ if (viem.getAddress(a).toLowerCase() === want) return r;
228
+ } catch {
229
+ continue;
230
+ }
231
+ }
232
+ return null;
233
+ }
234
+ function aaveV4KeyForNodeAssetRow(args) {
235
+ const raw = (args.contractAddress ?? "").trim();
236
+ try {
237
+ const a = viem.getAddress(raw);
238
+ if (a.toLowerCase() === "0x0000000000000000000000000000000000000000" && args.nativeWrapped) {
239
+ return viem.getAddress(args.nativeWrapped.trim()).toLowerCase();
240
+ }
241
+ return a.toLowerCase();
242
+ } catch {
243
+ return null;
244
+ }
245
+ }
246
+ var aaveV4ChainTokenCache = /* @__PURE__ */ new Map();
247
+ function ensureAaveV4ChainTokenCache(chainId) {
248
+ const hit = aaveV4ChainTokenCache.get(chainId);
249
+ if (hit) return hit;
250
+ const p = (async () => {
251
+ const [nativeWrapped, supportedUnderlying] = await Promise.all([
252
+ fetchAaveV4NativeWrappedToken(chainId),
253
+ fetchAaveV4SupportedUnderlyingAddressSet(chainId)
254
+ ]);
255
+ return { supportedUnderlying, nativeWrapped };
256
+ })();
257
+ aaveV4ChainTokenCache.set(chainId, p);
258
+ return p;
259
+ }
260
+ var aaveV4HubsByChain = /* @__PURE__ */ new Map();
261
+ var aaveV4HubReserves = /* @__PURE__ */ new Map();
262
+ function hubReservesKey(chainId, hub) {
263
+ return `v3spoke+summary:${chainId}:${hub.toLowerCase()}`;
264
+ }
265
+ async function fetchAaveV4HubsForChain(chainId) {
266
+ const hit = aaveV4HubsByChain.get(chainId);
267
+ if (hit) return hit;
268
+ const p = (async () => {
269
+ const d = await aaveV4Gql(
270
+ `query F($h: HubsRequest!) { hubs(request: $h) { name address chain { chainId name } } }`,
271
+ { h: { query: { chainIds: [chainId] } } }
272
+ );
273
+ const out = (d.hubs ?? []).map((h) => ({
274
+ name: h.name,
275
+ address: viem.getAddress((h.address ?? "").trim()),
276
+ chain: h.chain
277
+ }));
278
+ return out;
279
+ })();
280
+ aaveV4HubsByChain.set(chainId, p);
281
+ return p;
282
+ }
283
+ function resolveAaveV4HubForUiMarket(hubs, market, chainId) {
284
+ const on = hubs.filter((h) => h.chain.chainId === chainId);
285
+ if (!on.length) return null;
286
+ const find = (names) => {
287
+ const set = new Set(names.map((n) => n.toLowerCase()));
288
+ return on.find((h) => set.has((h.name ?? "").trim().toLowerCase())) ?? null;
289
+ };
290
+ if (market === "core") return find(["core"]);
291
+ if (market === "bluechip") return find(["prime", "bluechip", "institutional"]);
292
+ return find(["plus", "main", "default"]) ?? on[0] ?? null;
293
+ }
294
+ function aaveV4UiMarketIdForHubName(hubName) {
295
+ const n = (hubName ?? "").trim().toLowerCase();
296
+ if (n === "core") return "core";
297
+ if (n === "plus" || n === "main" || n === "default") return "main";
298
+ if (n === "prime" || n === "bluechip" || n === "institutional") return "bluechip";
299
+ return "main";
300
+ }
301
+ function aaveV4HubAutoPickTiebreakOrder(a, b) {
302
+ const rank = (name) => {
303
+ const n = (name ?? "").trim().toLowerCase();
304
+ if (n === "core") return 0;
305
+ if (n === "plus" || n === "main" || n === "default") return 1;
306
+ if (n === "prime" || n === "bluechip" || n === "institutional") return 2;
307
+ return 3;
308
+ };
309
+ const ra = rank(a.name);
310
+ const rb = rank(b.name);
311
+ if (ra !== rb) return ra - rb;
312
+ return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
313
+ }
314
+ function aaveV4HubReserveBorrowApyForSort(r) {
315
+ if (!r) return Number.POSITIVE_INFINITY;
316
+ const norm = parseFloat((r.summary?.borrowApy?.normalized ?? "").trim());
317
+ if (Number.isFinite(norm) && norm >= 0) return norm;
318
+ const v = parseFloat((r.summary?.borrowApy?.value ?? "").trim());
319
+ if (Number.isFinite(v) && v >= 0) {
320
+ if (v > 0 && v <= 1) return v * 100;
321
+ return v;
322
+ }
323
+ return Number.POSITIVE_INFINITY;
324
+ }
325
+ function aaveV4SuppliableHumanFromHubReserve(r) {
326
+ if (!r) return null;
327
+ const n = parseFloat((r.summary?.suppliable?.amount?.value ?? "").trim());
328
+ return Number.isFinite(n) && n >= 0 ? n : null;
329
+ }
330
+ async function findAaveV4HubReserveForChainUnderlying(args) {
331
+ const onChain = args.hubs.filter((h) => h.chain.chainId === args.chainId);
332
+ if (!onChain.length) return null;
333
+ const prefer = viem.getAddress(args.preferHub.address);
334
+ const order = (() => {
335
+ const rest = onChain.filter((h) => viem.getAddress(h.address) !== prefer).sort(aaveV4HubAutoPickTiebreakOrder);
336
+ const first = onChain.find((h) => viem.getAddress(h.address) === prefer);
337
+ return first ? [first, ...rest] : rest;
338
+ })();
339
+ const candidates = [];
340
+ for (const h of order) {
341
+ const rows = await fetchAaveV4HubReserves({ chainId: args.chainId, hubAddress: h.address });
342
+ const c = findHubReserveForUnderlying(rows, args.underlying);
343
+ if (c) candidates.push({ hub: h, hubReserves: rows, reserve: c });
344
+ }
345
+ if (!candidates.length) return null;
346
+ const noDebt = () => candidates[0];
347
+ const rawDebt = (args.debtUnderlying ?? "").trim();
348
+ if (!rawDebt || !viem.isAddress(rawDebt)) {
349
+ return noDebt();
350
+ }
351
+ const debtU = viem.getAddress(rawDebt);
352
+ const withDebt = candidates.map((c) => {
353
+ const dr = findHubReserveForUnderlying(c.hubReserves, debtU);
354
+ return { ...c, debtReserve: dr };
355
+ }).filter(
356
+ (x2) => x2.debtReserve != null && x2.debtReserve.canBorrow === true && x2.debtReserve.settings?.borrowable === true
357
+ );
358
+ if (!withDebt.length) {
359
+ return noDebt();
360
+ }
361
+ const amountRaw = (args.debtBorrowAmountHumanForLiquidity ?? "").trim().replace(/,/g, "");
362
+ const wantN = amountRaw && parseFloat(amountRaw) > 0 ? parseFloat(amountRaw) : null;
363
+ const sortKey = (a, b) => {
364
+ const d = aaveV4HubReserveBorrowApyForSort(a.debtReserve) - aaveV4HubReserveBorrowApyForSort(b.debtReserve);
365
+ if (d !== 0) return d;
366
+ const pa = viem.getAddress(a.hub.address) === prefer ? 0 : 1;
367
+ const pb = viem.getAddress(b.hub.address) === prefer ? 0 : 1;
368
+ if (pa !== pb) return pa - pb;
369
+ return aaveV4HubAutoPickTiebreakOrder(a.hub, b.hub);
370
+ };
371
+ if (wantN == null) {
372
+ withDebt.sort(sortKey);
373
+ const x2 = withDebt[0];
374
+ return { hub: x2.hub, hubReserves: x2.hubReserves, reserve: x2.reserve };
375
+ }
376
+ const sufficient = withDebt.filter((c) => {
377
+ const s = aaveV4SuppliableHumanFromHubReserve(c.debtReserve);
378
+ return s != null && s + 1e-9 >= wantN;
379
+ });
380
+ const pool = sufficient.length ? sufficient : withDebt;
381
+ pool.sort(sortKey);
382
+ const x = pool[0];
383
+ return { hub: x.hub, hubReserves: x.hubReserves, reserve: x.reserve };
384
+ }
385
+ async function fetchAaveV4HubReserves(args) {
386
+ const h = viem.getAddress(args.hubAddress);
387
+ const k = hubReservesKey(args.chainId, h);
388
+ const hit = aaveV4HubReserves.get(k);
389
+ if (hit) return hit;
390
+ const p = (async () => {
391
+ const d = await aaveV4Gql(
392
+ `query R($r: ReservesRequest!) {
393
+ reserves(request: $r) {
394
+ canBorrow
395
+ canSupply
396
+ canUseAsCollateral
397
+ spoke { address }
398
+ settings {
399
+ borrowable
400
+ collateral
401
+ collateralFactor { normalized value }
402
+ collateralRisk { normalized value }
403
+ }
404
+ summary {
405
+ supplied { amount { value decimals } }
406
+ borrowed { amount { value decimals } }
407
+ suppliable { amount { value decimals } }
408
+ supplyApy { normalized value }
409
+ borrowApy { normalized value }
410
+ }
411
+ asset { underlying { address info { name symbol icon } } }
412
+ }
413
+ }`,
414
+ {
415
+ r: {
416
+ query: { hub: { address: h, chainId: args.chainId } },
417
+ filter: "ALL",
418
+ orderBy: { supplyApy: "DESC" }
419
+ }
420
+ }
421
+ );
422
+ return d.reserves ?? [];
423
+ })();
424
+ aaveV4HubReserves.set(k, p);
425
+ return p;
426
+ }
427
+ function formatAaveV4PercentDisplay(normalized, value) {
428
+ const n = (normalized ?? "").trim();
429
+ if (n) {
430
+ const f = parseFloat(n);
431
+ if (!Number.isFinite(f)) return "\u2014";
432
+ if (f === 0) return "\u2014";
433
+ if (f % 1 === 0) return `${f.toFixed(0)}%`;
434
+ return `${f >= 10 ? f.toFixed(0) : f >= 1 ? f.toFixed(1).replace(/\.0$/, "") : f.toFixed(2).replace(/\.?0+$/, "")}%`;
435
+ }
436
+ const v = parseFloat((value ?? "").trim());
437
+ if (Number.isFinite(v) && v > 0) {
438
+ return `${Math.round(v * 100)}%`;
439
+ }
440
+ return "\u2014";
441
+ }
442
+ function formatAaveV4RiskDisplay(normalized, value) {
443
+ const n = parseFloat((normalized ?? "").trim());
444
+ if (Number.isFinite(n) && n > 0) {
445
+ return n >= 0.01 ? `${n.toFixed(2).replace(/\.0+$|(\.\d*?)0+$/, "$1")}%` : `${n.toFixed(4)}%`;
446
+ }
447
+ const v = parseFloat((value ?? "").trim());
448
+ if (Number.isFinite(v) && v > 0) {
449
+ return `${(v * 100).toFixed(2).replace(/\.0+$|(\.\d*?)0+$/, "$1")}%`;
450
+ }
451
+ return "Low";
452
+ }
453
+ function findHubReserveForUnderlying(list, underlying) {
454
+ const t = viem.getAddress(underlying).toLowerCase();
455
+ return list.find((r) => (r.asset?.underlying?.address ?? "").toLowerCase() === t) ?? null;
456
+ }
457
+ function borrowableAssetsFromHubReserves(rows) {
458
+ const seen = /* @__PURE__ */ new Set();
459
+ const out = [];
460
+ for (const r of rows) {
461
+ if (!r.canBorrow || !r.settings?.borrowable) continue;
462
+ const a = (r.asset?.underlying?.address ?? "").trim();
463
+ if (!a || !viem.isAddress(a)) continue;
464
+ const lo = a.toLowerCase();
465
+ if (seen.has(lo)) continue;
466
+ seen.add(lo);
467
+ const info = r.asset?.underlying?.info;
468
+ out.push({
469
+ address: viem.getAddress(a),
470
+ symbol: (info?.symbol ?? "\u2014").trim() || "\u2014",
471
+ name: (info?.name ?? "").trim() || "\u2014",
472
+ icon: (info?.icon ?? "").trim() || null
473
+ });
474
+ }
475
+ out.sort((p, q) => p.symbol.localeCompare(q.symbol, void 0, { sensitivity: "base" }));
476
+ return out;
477
+ }
478
+ var USER_BORROWS_HUB = `
479
+ query AaveV4UserBorrowsHub($r: UserBorrowsRequest!) {
480
+ userBorrows(request: $r) {
481
+ debt { amount { value } }
482
+ reserve {
483
+ asset { underlying { address } }
484
+ }
485
+ }
486
+ }
487
+ `;
488
+ async function fetchAaveV4UserBorrowsDebtByUnderlyingForHub(args) {
489
+ const u = viem.getAddress(args.user);
490
+ const h = viem.getAddress(args.hubAddress);
491
+ const d = await aaveV4Gql(USER_BORROWS_HUB, {
492
+ r: {
493
+ query: {
494
+ userHub: {
495
+ user: u,
496
+ hub: { input: { address: h, chainId: args.chainId } }
497
+ }
498
+ },
499
+ orderBy: { amount: "DESC" },
500
+ includeZeroBalances: false
501
+ }
502
+ });
503
+ const m = /* @__PURE__ */ new Map();
504
+ for (const row of d.userBorrows ?? []) {
505
+ const addr = (row?.reserve?.asset?.underlying?.address ?? "").trim();
506
+ if (!addr) continue;
507
+ const lo = viem.getAddress(addr).toLowerCase();
508
+ const v = (row?.debt?.amount?.value ?? "").trim();
509
+ if (!v) continue;
510
+ const n = parseFloat(v);
511
+ if (!Number.isFinite(n) || n === 0) continue;
512
+ const prev = m.get(lo);
513
+ if (prev) {
514
+ const p = parseFloat(prev);
515
+ m.set(lo, String((Number.isFinite(p) ? p : 0) + n));
516
+ } else {
517
+ m.set(lo, v);
518
+ }
519
+ }
520
+ return m;
521
+ }
522
+ function buildAaveV4BorrowTableRowsFromHub(borrowable, hubReserves, debtByUnderlying) {
523
+ if (!hubReserves?.length) {
524
+ return borrowable.map((b) => ({
525
+ address: b.address,
526
+ symbol: b.symbol,
527
+ name: b.name,
528
+ icon: b.icon,
529
+ yourBorrowedDisplay: debtByUnderlying === void 0 ? "\u2014" : `0${b.symbol && b.symbol !== "\u2014" ? ` ${b.symbol}` : ""}`,
530
+ baseBorrowApyDisplay: "\u2014",
531
+ totalBorrowsDisplay: "\u2014",
532
+ liquidityDisplay: "\u2014"
533
+ }));
534
+ }
535
+ return borrowable.map((b) => {
536
+ const lo = b.address.toLowerCase();
537
+ const r = hubReserves.find((x) => (x.asset?.underlying?.address ?? "").toLowerCase() === lo) ?? null;
538
+ const s = r?.summary;
539
+ const debtRaw = debtByUnderlying?.get(lo) ?? null;
540
+ const symSuffix = b.symbol && b.symbol !== "\u2014" ? ` ${b.symbol}` : "";
541
+ return {
542
+ address: b.address,
543
+ symbol: b.symbol,
544
+ name: b.name,
545
+ icon: b.icon,
546
+ yourBorrowedDisplay: (() => {
547
+ if (debtByUnderlying === void 0) return "\u2014";
548
+ if (debtRaw == null || !String(debtRaw).trim()) return `0${symSuffix}`;
549
+ const n = parseFloat(String(debtRaw).trim());
550
+ if (!Number.isFinite(n) || n === 0) return `0${symSuffix}`;
551
+ return `${formatAaveV4AmountHumanFromApiString(debtRaw)}${symSuffix}`;
552
+ })(),
553
+ baseBorrowApyDisplay: formatAaveV4PercentDisplay(
554
+ s?.borrowApy?.normalized,
555
+ s?.borrowApy?.value
556
+ ),
557
+ totalBorrowsDisplay: s ? `${formatAaveV4AmountHumanFromApiString(s.borrowed?.amount?.value)}${symSuffix}` : "\u2014",
558
+ liquidityDisplay: s ? `${formatAaveV4ReserveLiquidityFromSummary({
559
+ suppliedValue: s.supplied?.amount?.value,
560
+ borrowedValue: s.borrowed?.amount?.value
561
+ })}${symSuffix}` : "\u2014"
562
+ };
563
+ });
564
+ }
565
+
566
+ // src/chains/evm/rpcUrl.ts
567
+ function isValidRpcUrl(url) {
568
+ const t = url.trim();
569
+ if (!t) return false;
570
+ try {
571
+ const u = new URL(t);
572
+ return u.protocol === "http:" || u.protocol === "https:";
573
+ } catch {
574
+ return false;
575
+ }
576
+ }
577
+
578
+ // src/protocols/evm/aave-v4/spokeOnChain.ts
579
+ var spokeStaticAbi = [
580
+ {
581
+ name: "getReserveCount",
582
+ type: "function",
583
+ stateMutability: "view",
584
+ inputs: [],
585
+ outputs: [{ name: "", type: "uint256" }]
586
+ },
587
+ {
588
+ name: "getReserve",
589
+ type: "function",
590
+ stateMutability: "view",
591
+ inputs: [{ name: "reserveId", type: "uint256" }],
592
+ outputs: [
593
+ {
594
+ name: "",
595
+ type: "tuple",
596
+ components: [
597
+ { name: "underlying", type: "address" },
598
+ { name: "hub", type: "address" },
599
+ { name: "assetId", type: "uint16" },
600
+ { name: "decimals", type: "uint8" },
601
+ { name: "collateralRisk", type: "uint24" },
602
+ { name: "flags", type: "uint8" },
603
+ { name: "dynamicConfigKey", type: "uint32" }
604
+ ]
605
+ }
606
+ ]
607
+ },
608
+ {
609
+ name: "getReserveConfig",
610
+ type: "function",
611
+ stateMutability: "view",
612
+ inputs: [{ name: "reserveId", type: "uint256" }],
613
+ outputs: [
614
+ {
615
+ name: "",
616
+ type: "tuple",
617
+ components: [
618
+ { name: "collateralRisk", type: "uint24" },
619
+ { name: "paused", type: "bool" },
620
+ { name: "frozen", type: "bool" },
621
+ { name: "borrowable", type: "bool" },
622
+ { name: "receiveSharesEnabled", type: "bool" }
623
+ ]
624
+ }
625
+ ]
626
+ }
627
+ ];
628
+ async function fetchAaveV4SpokeReserveIdForUnderlying(args) {
629
+ const url = (args.rpcUrl ?? "").trim();
630
+ if (!isValidRpcUrl(url)) return null;
631
+ if (!Number.isFinite(args.chainId) || args.chainId < 0) return null;
632
+ const spoke = viem.getAddress(args.spoke);
633
+ const want = viem.getAddress(args.underlying).toLowerCase();
634
+ const chain = viem.defineChain({
635
+ id: args.chainId,
636
+ name: "Rpc",
637
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
638
+ rpcUrls: { default: { http: [url] } }
639
+ });
640
+ const client = viem.createPublicClient({ chain, transport: viem.http(url) });
641
+ const count = await client.readContract({
642
+ address: spoke,
643
+ abi: spokeStaticAbi,
644
+ functionName: "getReserveCount"
645
+ });
646
+ const n = Number(count);
647
+ if (!Number.isFinite(n) || n < 0 || n > 256) return null;
648
+ for (let i = 0; i < n; i++) {
649
+ const r = await client.readContract({
650
+ address: spoke,
651
+ abi: spokeStaticAbi,
652
+ functionName: "getReserve",
653
+ args: [BigInt(i)]
654
+ });
655
+ if (r.underlying.toLowerCase() === want) return BigInt(i);
656
+ }
657
+ return null;
658
+ }
659
+ async function fetchAaveV4SpokeReserveStatusForUnderlying(args) {
660
+ const reserveId = await fetchAaveV4SpokeReserveIdForUnderlying(args);
661
+ if (reserveId == null) return null;
662
+ const url = (args.rpcUrl ?? "").trim();
663
+ const spoke = viem.getAddress(args.spoke);
664
+ const chain = viem.defineChain({
665
+ id: args.chainId,
666
+ name: "Rpc",
667
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
668
+ rpcUrls: { default: { http: [url] } }
669
+ });
670
+ const client = viem.createPublicClient({ chain, transport: viem.http(url) });
671
+ const cfg = await client.readContract({
672
+ address: spoke,
673
+ abi: spokeStaticAbi,
674
+ functionName: "getReserveConfig",
675
+ args: [reserveId]
676
+ });
677
+ return {
678
+ reserveId,
679
+ paused: cfg.paused,
680
+ frozen: cfg.frozen,
681
+ borrowable: cfg.borrowable,
682
+ receiveSharesEnabled: cfg.receiveSharesEnabled,
683
+ collateralRisk: Number(cfg.collateralRisk)
684
+ };
685
+ }
686
+
687
+ // src/core/keygen.ts
688
+ function firstClientIdFromKeyGen(data) {
689
+ const map = data?.ClientKeys;
690
+ if (!map || typeof map !== "object") return null;
691
+ for (const v of Object.values(map)) {
692
+ if (typeof v === "string" && v.trim()) return v.trim();
693
+ }
694
+ return null;
695
+ }
696
+
697
+ // src/chains/evm/txParams.ts
698
+ function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
699
+ if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
700
+ return estimatedGas;
701
+ }
702
+ const cfg = BigInt(Math.floor(chainGasLimit));
703
+ return cfg > estimatedGas ? cfg : estimatedGas;
704
+ }
705
+ async function fetchChainFeeParams(rpcUrl, chainId) {
706
+ const url = rpcUrl.trim();
707
+ if (!url) return { isEip1559: false };
708
+ const chainIdNum = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
709
+ if (Number.isNaN(chainIdNum)) return { isEip1559: false };
710
+ const chain = viem.defineChain({
711
+ id: chainIdNum,
712
+ name: "Discovery",
713
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
714
+ rpcUrls: { default: { http: [url] } }
715
+ });
716
+ const publicClient = viem.createPublicClient({
717
+ chain,
718
+ transport: viem.http(url)
719
+ });
720
+ const getGasPriceGwei = async () => {
721
+ const gasPriceWei = await publicClient.getGasPrice();
722
+ return parseFloat(viem.formatUnits(gasPriceWei, 9));
723
+ };
724
+ try {
725
+ const block = await publicClient.getBlock({ blockTag: "latest" });
726
+ const baseFeePerGas = block?.baseFeePerGas;
727
+ if (baseFeePerGas == null || baseFeePerGas === void 0) {
728
+ const gasPriceGwei2 = await getGasPriceGwei();
729
+ return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
730
+ }
731
+ const baseFeeGwei = parseFloat(viem.formatUnits(baseFeePerGas, 9));
732
+ let priorityFeeGwei;
733
+ try {
734
+ const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
735
+ priorityFeeGwei = parseFloat(viem.formatUnits(priorityWei, 9));
736
+ } catch {
737
+ }
738
+ const gasPriceGwei = await getGasPriceGwei();
739
+ return {
740
+ isEip1559: true,
741
+ baseFeeGwei,
742
+ priorityFeeGwei,
743
+ gasPriceGwei
744
+ };
745
+ } catch {
746
+ try {
747
+ const gasPriceWei = await publicClient.getGasPrice();
748
+ const gasPriceGwei = parseFloat(viem.formatUnits(gasPriceWei, 9));
749
+ return { isEip1559: false, gasPriceGwei };
750
+ } catch {
751
+ return { isEip1559: false };
752
+ }
753
+ }
754
+ }
755
+ function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
756
+ let maxP = maxPriorityFeePerGas;
757
+ let maxF = maxFeePerGas;
758
+ if (baseWei > 0n && maxF < baseWei + maxP) {
759
+ maxF = baseWei + maxP + viem.parseGwei("0.001");
760
+ }
761
+ if (maxF < maxP) {
762
+ maxF = baseWei > 0n ? baseWei + maxP + viem.parseGwei("0.001") : maxP * 2n;
763
+ }
764
+ return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
765
+ }
766
+ function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
767
+ return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
768
+ }
769
+
770
+ // src/protocols/evm/aave-v4/multisign.ts
771
+ var AAVE_V4_SPOKE_SUPPLY_DEFAULT_GAS_UNITS = 1200000n;
772
+ var AAVE_SPOKE_SUPPLY_ESTIMATE_FALLBACK = AAVE_V4_SPOKE_SUPPLY_DEFAULT_GAS_UNITS;
773
+ var AAVE_SPOKE_SET_USING_AS_COLLATERAL_FALLBACK = 450000n;
774
+ var AAVE_ERC20_APPROVE_FALLBACK = 100000n;
775
+ var AAVE_WETH_DEPOSIT_FALLBACK = 120000n;
776
+ var AAVE_V4_SPOKE_WITHDRAW_DEFAULT_GAS_UNITS = 800000n;
777
+ var AAVE_SPOKE_WITHDRAW_ESTIMATE_FALLBACK = AAVE_V4_SPOKE_WITHDRAW_DEFAULT_GAS_UNITS;
778
+ var AAVE_V4_SPOKE_BORROW_DEFAULT_GAS_UNITS = 900000n;
779
+ var AAVE_SPOKE_BORROW_ESTIMATE_FALLBACK = AAVE_V4_SPOKE_BORROW_DEFAULT_GAS_UNITS;
780
+ var AAVE_V4_SPOKE_REPAY_DEFAULT_GAS_UNITS = 1000000n;
781
+ var AAVE_SPOKE_REPAY_ESTIMATE_FALLBACK = AAVE_V4_SPOKE_REPAY_DEFAULT_GAS_UNITS;
782
+ var AAVE_MERKL_CLAIM_ESTIMATE_FALLBACK = 500000n;
783
+ var EULER_REUL_UNLOCK_ESTIMATE_FALLBACK = 500000n;
784
+ function gweiToDecimalString(n) {
785
+ if (!Number.isFinite(n)) return "0";
786
+ if (n === 0) return "0";
787
+ const s = String(n);
788
+ if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
789
+ return s;
790
+ }
791
+ function txToViemStep(tx) {
792
+ return { to: viem.getAddress(tx.to), data: tx.data, value: tx.value };
793
+ }
794
+ var wethDepositAbi = viem.parseAbi(["function deposit() payable"]);
795
+ var erc20AllowanceAbi = viem.parseAbi([
796
+ "function allowance(address owner, address spender) view returns (uint256)",
797
+ "function decimals() view returns (uint8)"
798
+ ]);
799
+ var erc20ApproveAbi = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
800
+ var spokeSupplyAbi = viem.parseAbi(["function supply(uint256 reserveId, uint256 amount, address onBehalfOf)"]);
801
+ var spokeWithdrawAbi = viem.parseAbi(["function withdraw(uint256 reserveId, uint256 amount, address onBehalfOf)"]);
802
+ var spokeBorrowAbi = viem.parseAbi(["function borrow(uint256 reserveId, uint256 amount, address onBehalfOf)"]);
803
+ var spokeRepayAbi = viem.parseAbi(["function repay(uint256 reserveId, uint256 amount, address onBehalfOf)"]);
804
+ var spokeSetUsingAsCollateralAbi = viem.parseAbi([
805
+ "function setUsingAsCollateral(uint256 reserveId, bool usingAsCollateral, address onBehalfOf)"
806
+ ]);
807
+ async function buildEvmMultisignBodyAaveV4DepositBatch(args) {
808
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
809
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
810
+ const keyList = args.keyGen.keylist ?? [];
811
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
812
+ const asset = viem.getAddress(args.asset);
813
+ const spoke = viem.getAddress(args.spoke);
814
+ const weth = viem.getAddress(args.nativeWrapped);
815
+ const executor = viem.getAddress(args.executorAddress);
816
+ const onBehalf = viem.getAddress(args.onBehalfOf);
817
+ if (args.isNativeIn && asset.toLowerCase() !== weth.toLowerCase()) {
818
+ throw new Error("Native deposit path: underlying asset must match the chain wrapped native token.");
819
+ }
820
+ const ch = viem.defineChain({
821
+ id: args.chainId,
822
+ name: "Destination",
823
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
824
+ rpcUrls: { default: { http: [args.rpcUrl] } }
825
+ });
826
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
827
+ const dec = await publicClient.readContract({
828
+ address: asset,
829
+ abi: erc20AllowanceAbi,
830
+ functionName: "decimals"
831
+ });
832
+ const amountWei = viem.parseUnits(args.amountHuman, Number(dec));
833
+ if (amountWei === 0n) throw new Error("Amount is zero after converting with token decimals.");
834
+ const steps = [];
835
+ if (args.isNativeIn) {
836
+ const dataDeposit = viem.encodeFunctionData({ abi: wethDepositAbi, functionName: "deposit", args: [] });
837
+ steps.push({ kind: "weth_deposit", to: weth, data: dataDeposit, value: amountWei });
838
+ const wethAllowance = await publicClient.readContract({
839
+ address: weth,
840
+ abi: erc20AllowanceAbi,
841
+ functionName: "allowance",
842
+ args: [executor, spoke]
843
+ });
844
+ if (wethAllowance < amountWei) {
845
+ if (wethAllowance > 0n) {
846
+ const dataReset = viem.encodeFunctionData({
847
+ abi: erc20ApproveAbi,
848
+ functionName: "approve",
849
+ args: [spoke, 0n]
850
+ });
851
+ steps.push({ kind: "approve", to: weth, data: dataReset, value: 0n });
852
+ }
853
+ const dataApprove = viem.encodeFunctionData({
854
+ abi: erc20ApproveAbi,
855
+ functionName: "approve",
856
+ args: [spoke, amountWei]
857
+ });
858
+ steps.push({ kind: "approve", to: weth, data: dataApprove, value: 0n });
859
+ }
860
+ } else {
861
+ const currentAllowance = await publicClient.readContract({
862
+ address: asset,
863
+ abi: erc20AllowanceAbi,
864
+ functionName: "allowance",
865
+ args: [executor, spoke]
866
+ });
867
+ if (currentAllowance < amountWei) {
868
+ if (currentAllowance > 0n) {
869
+ const dataReset = viem.encodeFunctionData({
870
+ abi: erc20ApproveAbi,
871
+ functionName: "approve",
872
+ args: [spoke, 0n]
873
+ });
874
+ steps.push({ kind: "approve", to: asset, data: dataReset, value: 0n });
875
+ }
876
+ const dataApprove = viem.encodeFunctionData({
877
+ abi: erc20ApproveAbi,
878
+ functionName: "approve",
879
+ args: [spoke, amountWei]
880
+ });
881
+ steps.push({ kind: "approve", to: asset, data: dataApprove, value: 0n });
882
+ }
883
+ }
884
+ const reserveId = await fetchAaveV4SpokeReserveIdForUnderlying({
885
+ rpcUrl: args.rpcUrl,
886
+ chainId: args.chainId,
887
+ spoke,
888
+ underlying: asset
889
+ });
890
+ if (reserveId == null) {
891
+ throw new Error(
892
+ "Could not resolve Aave v4 reserve id for this asset on the Spoke (getReserve). Check RPC and the selected market."
893
+ );
894
+ }
895
+ const supplyData = viem.encodeFunctionData({
896
+ abi: spokeSupplyAbi,
897
+ functionName: "supply",
898
+ args: [reserveId, amountWei, onBehalf]
899
+ });
900
+ steps.push({ kind: "supply", to: spoke, data: supplyData, value: 0n });
901
+ if (args.enableAsCollateralAfterSupply === true) {
902
+ const setCollateralData = viem.encodeFunctionData({
903
+ abi: spokeSetUsingAsCollateralAbi,
904
+ functionName: "setUsingAsCollateral",
905
+ args: [reserveId, true, onBehalf]
906
+ });
907
+ steps.push({ kind: "set_collateral", to: spoke, data: setCollateralData, value: 0n });
908
+ }
909
+ const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
910
+ const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
911
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
912
+ const useCustomGas = args.useCustomGas;
913
+ const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
914
+ const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
915
+ const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
916
+ const messageHashes = [];
917
+ const messageRawBatch = [];
918
+ const proposalTxParamsBatch = [];
919
+ const batchMeta = [];
920
+ let firstTxFeePayload = {};
921
+ let firstDataNo0x = "";
922
+ const marketLabel = String(args.market);
923
+ for (let i = 0; i < steps.length; i++) {
924
+ const s = steps[i];
925
+ const v = txToViemStep(s);
926
+ const currentNonce = baseNonce + i;
927
+ let estimatedGas;
928
+ try {
929
+ estimatedGas = await publicClient.estimateGas({
930
+ to: v.to,
931
+ data: v.data,
932
+ value: v.value,
933
+ account: executor
934
+ });
935
+ } catch {
936
+ if (s.kind === "weth_deposit") estimatedGas = AAVE_WETH_DEPOSIT_FALLBACK;
937
+ else if (s.kind === "approve") estimatedGas = AAVE_ERC20_APPROVE_FALLBACK;
938
+ else if (s.kind === "set_collateral") estimatedGas = AAVE_SPOKE_SET_USING_AS_COLLATERAL_FALLBACK;
939
+ else estimatedGas = AAVE_SPOKE_SUPPLY_ESTIMATE_FALLBACK;
940
+ }
941
+ const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
942
+ if (legacy) {
943
+ let gasPriceWei = await publicClient.getGasPrice();
944
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
945
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
946
+ }
947
+ if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
948
+ const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
949
+ if (configured > gasPriceWei) gasPriceWei = configured;
950
+ }
951
+ const ser = viem.serializeTransaction({
952
+ type: "legacy",
953
+ to: v.to,
954
+ data: v.data,
955
+ value: v.value,
956
+ gas: gasLimitI,
957
+ gasPrice: gasPriceWei,
958
+ nonce: currentNonce,
959
+ chainId: args.chainId
960
+ });
961
+ const h = viem.keccak256(ser);
962
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
963
+ messageRawBatch.push(ser);
964
+ proposalTxParamsBatch.push({
965
+ nonce: currentNonce,
966
+ gasLimit: gasLimitI.toString(),
967
+ txType: "legacy",
968
+ gasPrice: gasPriceWei.toString()
969
+ });
970
+ if (i === 0) {
971
+ firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
972
+ firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
973
+ }
974
+ } else {
975
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
976
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
977
+ const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
978
+ const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
979
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
980
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
981
+ const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
982
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
983
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
984
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
985
+ let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
986
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
987
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
988
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
989
+ }
990
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
991
+ maxFeePerGas,
992
+ maxPriorityFeePerGas,
993
+ latestBaseFeeWei
994
+ ));
995
+ const ser = viem.serializeTransaction({
996
+ type: "eip1559",
997
+ to: v.to,
998
+ data: v.data,
999
+ value: v.value,
1000
+ gas: gasLimitI,
1001
+ maxFeePerGas,
1002
+ maxPriorityFeePerGas,
1003
+ nonce: currentNonce,
1004
+ chainId: args.chainId
1005
+ });
1006
+ const h = viem.keccak256(ser);
1007
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1008
+ messageRawBatch.push(ser);
1009
+ proposalTxParamsBatch.push({
1010
+ nonce: currentNonce,
1011
+ gasLimit: gasLimitI.toString(),
1012
+ txType: "eip1559",
1013
+ maxFeePerGas: maxFeePerGas.toString(),
1014
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1015
+ });
1016
+ if (i === 0) {
1017
+ firstTxFeePayload = {
1018
+ txNonce: currentNonce,
1019
+ txGasLimit: gasLimitI.toString(),
1020
+ txMaxFeePerGas: maxFeePerGas.toString(),
1021
+ txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1022
+ };
1023
+ firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1024
+ }
1025
+ }
1026
+ if (s.kind === "weth_deposit") {
1027
+ batchMeta.push({
1028
+ destinationAddress: weth,
1029
+ signatureText: JSON.stringify({
1030
+ kind: "AaveV4",
1031
+ name: "WETH.deposit",
1032
+ function: "deposit()",
1033
+ valueWei: amountWei.toString(),
1034
+ market: marketLabel,
1035
+ note: "Wrap native to WETH, then supply to Aave v4 (same batch)."
1036
+ }),
1037
+ evm: { type: "aave_v4_weth_deposit", version: 1, chainId: String(args.chainId) },
1038
+ aaveV4: { step: "weth_deposit", market: marketLabel, amountHuman: args.amountHuman, hubAsset: asset }
1039
+ });
1040
+ } else if (s.kind === "approve") {
1041
+ const tokenForApprove = s.to;
1042
+ batchMeta.push({
1043
+ destinationAddress: tokenForApprove,
1044
+ signatureText: JSON.stringify({
1045
+ kind: "AaveV4",
1046
+ name: "ERC20.approve",
1047
+ to: "Aave v4 spoke",
1048
+ function: "approve(address spender, uint256 amount)",
1049
+ spoke,
1050
+ amountHuman: args.amountHuman,
1051
+ note: "Allowance for this supply amount only (not unlimited)."
1052
+ }),
1053
+ evm: { type: "aave_v4_erc20_approve", version: 1, chainId: String(args.chainId) },
1054
+ aaveV4: { market: marketLabel, amountHuman: args.amountHuman, spoke }
1055
+ });
1056
+ } else if (s.kind === "set_collateral") {
1057
+ batchMeta.push({
1058
+ destinationAddress: spoke,
1059
+ signatureText: JSON.stringify({
1060
+ kind: "AaveV4",
1061
+ name: "Spoke.setUsingAsCollateral",
1062
+ function: "setUsingAsCollateral(uint256 reserveId, bool usingAsCollateral, address onBehalfOf)",
1063
+ reserveId: reserveId.toString(),
1064
+ usingAsCollateral: true,
1065
+ onBehalfOf: onBehalf,
1066
+ market: marketLabel,
1067
+ note: "Enables this supplied reserve as collateral for borrowing (after supply in the same batch)."
1068
+ }),
1069
+ evm: { type: "aave_v4_spoke_set_using_as_collateral", version: 1, chainId: String(args.chainId) },
1070
+ aaveV4: { market: marketLabel, asset, spoke, reserveId: reserveId.toString(), gasBuildSetCollateral: { baseGasUnits: gasLimitI.toString() } }
1071
+ });
1072
+ } else {
1073
+ batchMeta.push({
1074
+ destinationAddress: spoke,
1075
+ signatureText: JSON.stringify({
1076
+ kind: "AaveV4",
1077
+ name: "Spoke.supply",
1078
+ function: "supply(uint256 reserveId, uint256 amount, address onBehalfOf)",
1079
+ reserveId: reserveId.toString(),
1080
+ asset,
1081
+ onBehalfOf: onBehalf,
1082
+ market: marketLabel,
1083
+ amountHuman: args.amountHuman
1084
+ }),
1085
+ evm: { type: "aave_v4_spoke_supply", version: 1, chainId: String(args.chainId) },
1086
+ aaveV4: {
1087
+ market: marketLabel,
1088
+ amountHuman: args.amountHuman,
1089
+ asset,
1090
+ spoke,
1091
+ reserveId: reserveId.toString(),
1092
+ gasBuildSupply: { baseGasUnits: gasLimitI.toString() }
1093
+ }
1094
+ });
1095
+ }
1096
+ }
1097
+ const extraPayload = { batchMeta };
1098
+ if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
1099
+ extraPayload.customGasChainDetails = args.customGasChainDetails;
1100
+ }
1101
+ const extraJSON = JSON.stringify(extraPayload);
1102
+ const firstSigText = batchMeta[0].signatureText;
1103
+ const n = steps.length;
1104
+ const hasWrap = args.isNativeIn;
1105
+ const withCollateral = args.enableAsCollateralAfterSupply === true;
1106
+ const purposeSuffix = (() => {
1107
+ if (hasWrap) {
1108
+ return withCollateral ? `Aave v4: ${n}-tx batch \u2014 wrap native to WETH (if needed), approve spoke for the exact amount, supply, then setUsingAsCollateral.` : `Aave v4: ${n}-tx batch \u2014 wrap native to WETH (if needed), approve spoke for the exact amount, then supply.`;
1109
+ }
1110
+ if (n === 1) {
1111
+ return "Aave v4: 1-tx \u2014 supply (allowance already sufficient).";
1112
+ }
1113
+ if (withCollateral && n === 2) {
1114
+ return "Aave v4: 2-tx batch \u2014 supply (allowance already sufficient), then setUsingAsCollateral.";
1115
+ }
1116
+ if (withCollateral) {
1117
+ return `Aave v4: ${n}-tx batch \u2014 approve spoke for the exact amount, supply, then setUsingAsCollateral.`;
1118
+ }
1119
+ return `Aave v4: ${n}-tx batch \u2014 approve spoke for the exact amount, then supply.`;
1120
+ })();
1121
+ const firstValue = steps[0].value;
1122
+ const bodyForSign = {
1123
+ keyList,
1124
+ pubKey: ph,
1125
+ msgHash: messageHashes[0],
1126
+ msgRaw: firstDataNo0x,
1127
+ messageHashes,
1128
+ messageRawBatch,
1129
+ destinationChainID: String(args.chainId),
1130
+ destinationAddress: steps[0].to,
1131
+ extraJSON,
1132
+ signatureText: firstSigText,
1133
+ purpose: (() => {
1134
+ const t = args.purposeText.trim();
1135
+ return (t ? `${t}
1136
+
1137
+ ` : "") + purposeSuffix;
1138
+ })(),
1139
+ ...firstTxFeePayload,
1140
+ proposalTxParams: proposalTxParamsBatch
1141
+ };
1142
+ if (firstValue > 0n) {
1143
+ bodyForSign.value = firstValue.toString();
1144
+ }
1145
+ if (clientId) bodyForSign.clientId = clientId;
1146
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
1147
+ }
1148
+ async function buildEvmMultisignBodyAaveV4SpokeWithdraw(args) {
1149
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
1150
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1151
+ const keyList = args.keyGen.keylist ?? [];
1152
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
1153
+ const asset = viem.getAddress(args.underlying);
1154
+ const spoke = viem.getAddress(args.spoke);
1155
+ const onBehalf = viem.getAddress(args.onBehalfOf);
1156
+ const executor = viem.getAddress(args.executorAddress);
1157
+ const ch = viem.defineChain({
1158
+ id: args.chainId,
1159
+ name: "Destination",
1160
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1161
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1162
+ });
1163
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1164
+ const dec = await publicClient.readContract({
1165
+ address: asset,
1166
+ abi: erc20AllowanceAbi,
1167
+ functionName: "decimals"
1168
+ });
1169
+ const amountWei = viem.parseUnits(args.amountHuman, Number(dec));
1170
+ if (amountWei === 0n) throw new Error("Withdraw amount is zero after converting with token decimals.");
1171
+ let reserveId;
1172
+ if (args.reserveIdOnChain != null && args.reserveIdOnChain >= 0n) {
1173
+ reserveId = args.reserveIdOnChain;
1174
+ } else {
1175
+ const r = await fetchAaveV4SpokeReserveIdForUnderlying({
1176
+ rpcUrl: args.rpcUrl,
1177
+ chainId: args.chainId,
1178
+ spoke,
1179
+ underlying: asset
1180
+ });
1181
+ if (r == null) {
1182
+ throw new Error("Could not resolve Aave v4 reserve id for this asset on the Spoke.");
1183
+ }
1184
+ reserveId = r;
1185
+ }
1186
+ const data = viem.encodeFunctionData({
1187
+ abi: spokeWithdrawAbi,
1188
+ functionName: "withdraw",
1189
+ args: [reserveId, amountWei, onBehalf]
1190
+ });
1191
+ const tx = { to: spoke, data, value: 0n };
1192
+ return buildEvmMultisignBodyAaveV4OneStep({
1193
+ ph,
1194
+ keyList,
1195
+ clientId: clientId ?? void 0,
1196
+ chainId: args.chainId,
1197
+ rpcUrl: args.rpcUrl,
1198
+ chainDetail: args.chainDetail,
1199
+ useCustomGas: args.useCustomGas,
1200
+ customGasChainDetails: args.customGasChainDetails ?? void 0,
1201
+ purposeText: args.purposeText,
1202
+ purposeSuffix: `Aave v4: 1-tx \u2014 Spoke.withdraw for ${(args.amountHuman || "").trim() || "\u2026"} of underlying on this market (see signature text).`,
1203
+ executorAddress: executor,
1204
+ v: tx,
1205
+ estimateGasFallback: AAVE_SPOKE_WITHDRAW_ESTIMATE_FALLBACK,
1206
+ buildBatchMeta: (ctx) => ({
1207
+ destinationAddress: spoke,
1208
+ signatureText: JSON.stringify({
1209
+ kind: "AaveV4",
1210
+ name: "Spoke.withdraw",
1211
+ function: "withdraw(uint256 reserveId, uint256 amount, address onBehalfOf)",
1212
+ reserveId: reserveId.toString(),
1213
+ asset,
1214
+ onBehalfOf: onBehalf,
1215
+ market: args.marketLabel,
1216
+ amountHuman: args.amountHuman
1217
+ }),
1218
+ evm: { type: "aave_v4_spoke_withdraw", version: 1, chainId: String(args.chainId) },
1219
+ aaveV4: {
1220
+ market: args.marketLabel,
1221
+ amountHuman: args.amountHuman,
1222
+ asset,
1223
+ spoke,
1224
+ reserveId: reserveId.toString(),
1225
+ gasBuildWithdraw: { baseGasUnits: ctx.gasLimit.toString() }
1226
+ }
1227
+ })
1228
+ });
1229
+ }
1230
+ async function buildEvmMultisignBodyAaveV4SpokeBorrow(args) {
1231
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
1232
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1233
+ const keyList = args.keyGen.keylist ?? [];
1234
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
1235
+ const asset = viem.getAddress(args.underlying);
1236
+ const spoke = viem.getAddress(args.spoke);
1237
+ const onBehalf = viem.getAddress(args.onBehalfOf);
1238
+ const executor = viem.getAddress(args.executorAddress);
1239
+ const ch = viem.defineChain({
1240
+ id: args.chainId,
1241
+ name: "Destination",
1242
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1243
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1244
+ });
1245
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1246
+ const dec = await publicClient.readContract({
1247
+ address: asset,
1248
+ abi: erc20AllowanceAbi,
1249
+ functionName: "decimals"
1250
+ });
1251
+ const amountWei = viem.parseUnits(args.amountHuman, Number(dec));
1252
+ if (amountWei === 0n) throw new Error("Borrow amount is zero after converting with token decimals.");
1253
+ let reserveId;
1254
+ if (args.reserveIdOnChain != null && args.reserveIdOnChain >= 0n) {
1255
+ reserveId = args.reserveIdOnChain;
1256
+ } else {
1257
+ const r = await fetchAaveV4SpokeReserveIdForUnderlying({
1258
+ rpcUrl: args.rpcUrl,
1259
+ chainId: args.chainId,
1260
+ spoke,
1261
+ underlying: asset
1262
+ });
1263
+ if (r == null) {
1264
+ throw new Error("Could not resolve Aave v4 reserve id for this asset on the Spoke.");
1265
+ }
1266
+ reserveId = r;
1267
+ }
1268
+ const data = viem.encodeFunctionData({
1269
+ abi: spokeBorrowAbi,
1270
+ functionName: "borrow",
1271
+ args: [reserveId, amountWei, onBehalf]
1272
+ });
1273
+ const tx = { to: spoke, data, value: 0n };
1274
+ return buildEvmMultisignBodyAaveV4OneStep({
1275
+ ph,
1276
+ keyList,
1277
+ clientId: clientId ?? void 0,
1278
+ chainId: args.chainId,
1279
+ rpcUrl: args.rpcUrl,
1280
+ chainDetail: args.chainDetail,
1281
+ useCustomGas: args.useCustomGas,
1282
+ customGasChainDetails: args.customGasChainDetails ?? void 0,
1283
+ purposeText: args.purposeText,
1284
+ purposeSuffix: `Aave v4: 1-tx \u2014 Spoke.borrow for ${(args.amountHuman || "").trim() || "\u2026"} of underlying on this market (see signature text).`,
1285
+ executorAddress: executor,
1286
+ v: tx,
1287
+ estimateGasFallback: AAVE_SPOKE_BORROW_ESTIMATE_FALLBACK,
1288
+ buildBatchMeta: (ctx) => ({
1289
+ destinationAddress: spoke,
1290
+ signatureText: JSON.stringify({
1291
+ kind: "AaveV4",
1292
+ name: "Spoke.borrow",
1293
+ function: "borrow(uint256 reserveId, uint256 amount, address onBehalfOf)",
1294
+ reserveId: reserveId.toString(),
1295
+ asset,
1296
+ onBehalfOf: onBehalf,
1297
+ market: args.marketLabel,
1298
+ amountHuman: args.amountHuman
1299
+ }),
1300
+ evm: { type: "aave_v4_spoke_borrow", version: 1, chainId: String(args.chainId) },
1301
+ aaveV4: {
1302
+ market: args.marketLabel,
1303
+ amountHuman: args.amountHuman,
1304
+ asset,
1305
+ spoke,
1306
+ reserveId: reserveId.toString(),
1307
+ gasBuildBorrow: { baseGasUnits: ctx.gasLimit.toString() }
1308
+ }
1309
+ })
1310
+ });
1311
+ }
1312
+ async function buildEvmMultisignBodyAaveV4SpokeRepay(args) {
1313
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
1314
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1315
+ const keyList = args.keyGen.keylist ?? [];
1316
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
1317
+ const asset = viem.getAddress(args.underlying);
1318
+ const spoke = viem.getAddress(args.spoke);
1319
+ const onBehalf = viem.getAddress(args.onBehalfOf);
1320
+ const executor = viem.getAddress(args.executorAddress);
1321
+ const ch = viem.defineChain({
1322
+ id: args.chainId,
1323
+ name: "Destination",
1324
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1325
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1326
+ });
1327
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1328
+ const dec = await publicClient.readContract({
1329
+ address: asset,
1330
+ abi: erc20AllowanceAbi,
1331
+ functionName: "decimals"
1332
+ });
1333
+ const amountWei = viem.parseUnits(args.amountHuman, Number(dec));
1334
+ if (amountWei === 0n) throw new Error("Repay amount is zero after converting with token decimals.");
1335
+ let reserveId;
1336
+ if (args.reserveIdOnChain != null && args.reserveIdOnChain >= 0n) {
1337
+ reserveId = args.reserveIdOnChain;
1338
+ } else {
1339
+ const r = await fetchAaveV4SpokeReserveIdForUnderlying({
1340
+ rpcUrl: args.rpcUrl,
1341
+ chainId: args.chainId,
1342
+ spoke,
1343
+ underlying: asset
1344
+ });
1345
+ if (r == null) {
1346
+ throw new Error("Could not resolve Aave v4 reserve id for this asset on the Spoke.");
1347
+ }
1348
+ reserveId = r;
1349
+ }
1350
+ const steps = [];
1351
+ const currentAllowance = await publicClient.readContract({
1352
+ address: asset,
1353
+ abi: erc20AllowanceAbi,
1354
+ functionName: "allowance",
1355
+ args: [executor, spoke]
1356
+ });
1357
+ if (currentAllowance < amountWei) {
1358
+ if (currentAllowance > 0n) {
1359
+ const dataReset = viem.encodeFunctionData({
1360
+ abi: erc20ApproveAbi,
1361
+ functionName: "approve",
1362
+ args: [spoke, 0n]
1363
+ });
1364
+ steps.push({ kind: "approve", to: asset, data: dataReset, value: 0n });
1365
+ }
1366
+ const dataApprove = viem.encodeFunctionData({
1367
+ abi: erc20ApproveAbi,
1368
+ functionName: "approve",
1369
+ args: [spoke, amountWei]
1370
+ });
1371
+ steps.push({ kind: "approve", to: asset, data: dataApprove, value: 0n });
1372
+ }
1373
+ const repayData = viem.encodeFunctionData({
1374
+ abi: spokeRepayAbi,
1375
+ functionName: "repay",
1376
+ args: [reserveId, amountWei, onBehalf]
1377
+ });
1378
+ steps.push({ kind: "repay", to: spoke, data: repayData, value: 0n });
1379
+ const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
1380
+ const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
1381
+ const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
1382
+ const useCustom = args.useCustomGas;
1383
+ const gasLimitConfig = useCustom && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
1384
+ const gasFeeMultiplier = useCustom && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
1385
+ const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
1386
+ const messageHashes = [];
1387
+ const messageRawBatch = [];
1388
+ const proposalTxParamsBatch = [];
1389
+ const batchMeta = [];
1390
+ let firstTxFeePayload = {};
1391
+ let firstDataNo0x = "";
1392
+ const marketLabel = String(args.market);
1393
+ for (let i = 0; i < steps.length; i++) {
1394
+ const s = steps[i];
1395
+ const v = txToViemStep(s);
1396
+ const currentNonce = baseNonce + i;
1397
+ let estimatedGas;
1398
+ try {
1399
+ estimatedGas = await publicClient.estimateGas({
1400
+ to: v.to,
1401
+ data: v.data,
1402
+ value: v.value,
1403
+ account: executor
1404
+ });
1405
+ } catch {
1406
+ if (s.kind === "approve") estimatedGas = AAVE_ERC20_APPROVE_FALLBACK;
1407
+ else estimatedGas = AAVE_SPOKE_REPAY_ESTIMATE_FALLBACK;
1408
+ }
1409
+ const gasLimitI = useCustom ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
1410
+ if (legacy) {
1411
+ let gasPriceWei = await publicClient.getGasPrice();
1412
+ if (useCustom && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1413
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
1414
+ }
1415
+ if (useCustom && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
1416
+ const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
1417
+ if (configured > gasPriceWei) gasPriceWei = configured;
1418
+ }
1419
+ const ser = viem.serializeTransaction({
1420
+ type: "legacy",
1421
+ to: v.to,
1422
+ data: v.data,
1423
+ value: v.value,
1424
+ gas: gasLimitI,
1425
+ gasPrice: gasPriceWei,
1426
+ nonce: currentNonce,
1427
+ chainId: args.chainId
1428
+ });
1429
+ const h = viem.keccak256(ser);
1430
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1431
+ messageRawBatch.push(ser);
1432
+ proposalTxParamsBatch.push({
1433
+ nonce: currentNonce,
1434
+ gasLimit: gasLimitI.toString(),
1435
+ txType: "legacy",
1436
+ gasPrice: gasPriceWei.toString()
1437
+ });
1438
+ if (i === 0) {
1439
+ firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
1440
+ firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1441
+ }
1442
+ } else {
1443
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
1444
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
1445
+ const configuredBase = useCustom && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
1446
+ const configuredPriority = useCustom && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
1447
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
1448
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
1449
+ const baseFeeMultiplierPct = useCustom && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
1450
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
1451
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
1452
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
1453
+ let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
1454
+ if (useCustom && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1455
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1456
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1457
+ }
1458
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
1459
+ maxFeePerGas,
1460
+ maxPriorityFeePerGas,
1461
+ latestBaseFeeWei
1462
+ ));
1463
+ const ser = viem.serializeTransaction({
1464
+ type: "eip1559",
1465
+ to: v.to,
1466
+ data: v.data,
1467
+ value: v.value,
1468
+ gas: gasLimitI,
1469
+ maxFeePerGas,
1470
+ maxPriorityFeePerGas,
1471
+ nonce: currentNonce,
1472
+ chainId: args.chainId
1473
+ });
1474
+ const h = viem.keccak256(ser);
1475
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1476
+ messageRawBatch.push(ser);
1477
+ proposalTxParamsBatch.push({
1478
+ nonce: currentNonce,
1479
+ gasLimit: gasLimitI.toString(),
1480
+ txType: "eip1559",
1481
+ maxFeePerGas: maxFeePerGas.toString(),
1482
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1483
+ });
1484
+ if (i === 0) {
1485
+ firstTxFeePayload = {
1486
+ txNonce: currentNonce,
1487
+ txGasLimit: gasLimitI.toString(),
1488
+ txMaxFeePerGas: maxFeePerGas.toString(),
1489
+ txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1490
+ };
1491
+ firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1492
+ }
1493
+ }
1494
+ if (s.kind === "approve") {
1495
+ const tokenForApprove = s.to;
1496
+ batchMeta.push({
1497
+ destinationAddress: tokenForApprove,
1498
+ signatureText: JSON.stringify({
1499
+ kind: "AaveV4",
1500
+ name: "ERC20.approve (repay)",
1501
+ to: "Aave v4 spoke",
1502
+ function: "approve(address spender, uint256 amount)",
1503
+ spoke,
1504
+ amountHuman: args.amountHuman,
1505
+ note: "Allowance to repay this amount (or reset+approve to exact amount)."
1506
+ }),
1507
+ evm: { type: "aave_v4_erc20_approve", version: 1, chainId: String(args.chainId) },
1508
+ aaveV4: { market: marketLabel, amountHuman: args.amountHuman, spoke, step: "repay_prepare" }
1509
+ });
1510
+ } else {
1511
+ batchMeta.push({
1512
+ destinationAddress: spoke,
1513
+ signatureText: JSON.stringify({
1514
+ kind: "AaveV4",
1515
+ name: "Spoke.repay",
1516
+ function: "repay(uint256 reserveId, uint256 amount, address onBehalfOf)",
1517
+ reserveId: reserveId.toString(),
1518
+ asset,
1519
+ onBehalfOf: onBehalf,
1520
+ market: marketLabel,
1521
+ amountHuman: args.amountHuman
1522
+ }),
1523
+ evm: { type: "aave_v4_spoke_repay", version: 1, chainId: String(args.chainId) },
1524
+ aaveV4: {
1525
+ market: marketLabel,
1526
+ amountHuman: args.amountHuman,
1527
+ asset,
1528
+ spoke,
1529
+ reserveId: reserveId.toString(),
1530
+ gasBuildRepay: { baseGasUnits: gasLimitI.toString() }
1531
+ }
1532
+ });
1533
+ }
1534
+ }
1535
+ const extraPayload = { batchMeta };
1536
+ if (useCustom && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
1537
+ extraPayload.customGasChainDetails = args.customGasChainDetails;
1538
+ }
1539
+ const extraJSON = JSON.stringify(extraPayload);
1540
+ const firstSigText = batchMeta[0].signatureText;
1541
+ const n = steps.length;
1542
+ const purposeSuffix = `Aave v4: ${n}-tx batch \u2014 approve spoke if needed, then repay ${(args.amountHuman || "").trim() || "\u2026"} of debt.`;
1543
+ const firstValue = steps[0].value;
1544
+ const bodyForSign = {
1545
+ keyList,
1546
+ pubKey: ph,
1547
+ msgHash: messageHashes[0],
1548
+ msgRaw: firstDataNo0x,
1549
+ messageHashes,
1550
+ messageRawBatch,
1551
+ destinationChainID: String(args.chainId),
1552
+ destinationAddress: steps[0].to,
1553
+ extraJSON,
1554
+ signatureText: firstSigText,
1555
+ purpose: (() => {
1556
+ const t = args.purposeText.trim();
1557
+ return (t ? `${t}
1558
+
1559
+ ` : "") + purposeSuffix;
1560
+ })(),
1561
+ ...firstTxFeePayload,
1562
+ proposalTxParams: proposalTxParamsBatch
1563
+ };
1564
+ if (firstValue > 0n) {
1565
+ bodyForSign.value = firstValue.toString();
1566
+ }
1567
+ if (clientId) bodyForSign.clientId = clientId;
1568
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
1569
+ }
1570
+ async function buildEvmMultisignBodyAaveV4MerklClaimRewards(args) {
1571
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
1572
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1573
+ const keyList = args.keyGen.keylist ?? [];
1574
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
1575
+ const to = viem.getAddress(args.to);
1576
+ const executor = viem.getAddress(args.executorAddress);
1577
+ return buildEvmMultisignBodyAaveV4OneStep({
1578
+ ph,
1579
+ keyList,
1580
+ clientId: clientId ?? void 0,
1581
+ chainId: args.chainId,
1582
+ rpcUrl: args.rpcUrl,
1583
+ chainDetail: args.chainDetail,
1584
+ useCustomGas: args.useCustomGas,
1585
+ customGasChainDetails: args.customGasChainDetails ?? void 0,
1586
+ purposeText: args.purposeText,
1587
+ purposeSuffix: `Aave v4: 1-tx \u2014 Merkl claim rewards (${args.rewardIdCount} id(s), see calldata in signature text).`,
1588
+ executorAddress: executor,
1589
+ v: { to, data: args.data, value: args.valueWei },
1590
+ estimateGasFallback: AAVE_MERKL_CLAIM_ESTIMATE_FALLBACK,
1591
+ buildBatchMeta: (ctx) => ({
1592
+ destinationAddress: to,
1593
+ signatureText: JSON.stringify({
1594
+ kind: "AaveV4",
1595
+ name: "claimRewards (Merkl)",
1596
+ rewardIdCount: args.rewardIdCount,
1597
+ chainId: args.chainId,
1598
+ valueWei: args.valueWei.toString(),
1599
+ note: "From Aave v4 claimRewards request."
1600
+ }),
1601
+ evm: { type: "aave_v4_merkl_claim_rewards", version: 1, chainId: String(args.chainId) },
1602
+ aaveV4: { step: "merkl_claim", rewardIdCount: args.rewardIdCount, gasBuildClaim: { baseGasUnits: ctx.gasLimit.toString() } }
1603
+ })
1604
+ });
1605
+ }
1606
+ async function buildEvmMultisignBodyEulerV2MerklDistributorClaim(args) {
1607
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
1608
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1609
+ const keyList = args.keyGen.keylist ?? [];
1610
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
1611
+ const to = viem.getAddress(args.to);
1612
+ const executor = viem.getAddress(args.executorAddress);
1613
+ return buildEvmMultisignBodyAaveV4OneStep({
1614
+ ph,
1615
+ keyList,
1616
+ clientId: clientId ?? void 0,
1617
+ chainId: args.chainId,
1618
+ rpcUrl: args.rpcUrl,
1619
+ chainDetail: args.chainDetail,
1620
+ useCustomGas: args.useCustomGas,
1621
+ customGasChainDetails: args.customGasChainDetails ?? void 0,
1622
+ purposeText: args.purposeText,
1623
+ purposeSuffix: `Euler v2: 1-tx \u2014 Merkl distributor claim (${args.claimLeafCount} reward(s), see calldata in signature text).`,
1624
+ executorAddress: executor,
1625
+ v: { to, data: args.data, value: args.valueWei },
1626
+ estimateGasFallback: AAVE_MERKL_CLAIM_ESTIMATE_FALLBACK,
1627
+ buildBatchMeta: (ctx) => ({
1628
+ destinationAddress: to,
1629
+ signatureText: JSON.stringify({
1630
+ kind: "EulerV2",
1631
+ name: "Merkl Distributor claim",
1632
+ claimLeafCount: args.claimLeafCount,
1633
+ chainId: args.chainId,
1634
+ valueWei: args.valueWei.toString(),
1635
+ note: "Merkl API proofs; Distributor.claim(users,tokens,amounts,proofs)."
1636
+ }),
1637
+ evm: { type: "euler_v2_merkl_distributor_claim", version: 1, chainId: String(args.chainId) },
1638
+ eulerV2: { step: "merkl_distributor_claim", claimLeafCount: args.claimLeafCount, gasBuildClaim: { baseGasUnits: ctx.gasLimit.toString() } }
1639
+ })
1640
+ });
1641
+ }
1642
+ async function buildEvmMultisignBodyEulerV2ReulUnlock(args) {
1643
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
1644
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
1645
+ const keyList = args.keyGen.keylist ?? [];
1646
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
1647
+ const to = viem.getAddress(args.to);
1648
+ const executor = viem.getAddress(args.executorAddress);
1649
+ const lossLabel = args.allowRemainderLoss ? "early unlock (remainder to receiver)" : "vested only";
1650
+ return buildEvmMultisignBodyAaveV4OneStep({
1651
+ ph,
1652
+ keyList,
1653
+ clientId: clientId ?? void 0,
1654
+ chainId: args.chainId,
1655
+ rpcUrl: args.rpcUrl,
1656
+ chainDetail: args.chainDetail,
1657
+ useCustomGas: args.useCustomGas,
1658
+ customGasChainDetails: args.customGasChainDetails ?? void 0,
1659
+ purposeText: args.purposeText,
1660
+ purposeSuffix: `Euler v2: 1-tx \u2014 rEUL unlock (${args.lockTimestampCount} lock(s), ${lossLabel}).`,
1661
+ executorAddress: executor,
1662
+ v: { to, data: args.data, value: args.valueWei },
1663
+ estimateGasFallback: EULER_REUL_UNLOCK_ESTIMATE_FALLBACK,
1664
+ buildBatchMeta: (ctx) => ({
1665
+ destinationAddress: to,
1666
+ signatureText: JSON.stringify({
1667
+ kind: "EulerV2",
1668
+ name: "rEUL unlock (RewardToken)",
1669
+ lockTimestampCount: args.lockTimestampCount,
1670
+ allowRemainderLoss: args.allowRemainderLoss,
1671
+ chainId: args.chainId,
1672
+ valueWei: args.valueWei.toString(),
1673
+ note: "RewardToken.withdrawToByLockTimestamps(account, lockTimestamps, allowRemainderLoss)."
1674
+ }),
1675
+ evm: { type: "euler_v2_reul_unlock", version: 1, chainId: String(args.chainId) },
1676
+ eulerV2: {
1677
+ step: "reul_unlock",
1678
+ lockTimestampCount: args.lockTimestampCount,
1679
+ allowRemainderLoss: args.allowRemainderLoss,
1680
+ gasBuildClaim: { baseGasUnits: ctx.gasLimit.toString() }
1681
+ }
1682
+ })
1683
+ });
1684
+ }
1685
+ async function buildEvmMultisignBodyAaveV4OneStep(args) {
1686
+ const ch = viem.defineChain({
1687
+ id: args.chainId,
1688
+ name: "Destination",
1689
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1690
+ rpcUrls: { default: { http: [args.rpcUrl] } }
1691
+ });
1692
+ const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
1693
+ const v = args.v;
1694
+ const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
1695
+ const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
1696
+ const latestBaseFeeWeiOneStep = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
1697
+ const useCustomGas = args.useCustomGas;
1698
+ const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
1699
+ const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
1700
+ const baseNonce = await publicClient.getTransactionCount({ address: args.executorAddress, blockTag: "pending" });
1701
+ const currentNonce = baseNonce;
1702
+ let estimatedGas;
1703
+ try {
1704
+ estimatedGas = await publicClient.estimateGas({
1705
+ to: v.to,
1706
+ data: v.data,
1707
+ value: v.value,
1708
+ account: args.executorAddress
1709
+ });
1710
+ } catch {
1711
+ estimatedGas = args.estimateGasFallback;
1712
+ }
1713
+ const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
1714
+ const batchMeta0 = args.buildBatchMeta({ gasLimit: gasLimitI });
1715
+ let firstTxFeePayload = {};
1716
+ let firstDataNo0x = "";
1717
+ const messageHashes = [];
1718
+ const messageRawBatch = [];
1719
+ const proposalTxParamsBatch = [];
1720
+ if (legacy) {
1721
+ let gasPriceWei = await publicClient.getGasPrice();
1722
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1723
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
1724
+ }
1725
+ if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
1726
+ const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
1727
+ if (configured > gasPriceWei) gasPriceWei = configured;
1728
+ }
1729
+ const ser = viem.serializeTransaction({
1730
+ type: "legacy",
1731
+ to: v.to,
1732
+ data: v.data,
1733
+ value: v.value,
1734
+ gas: gasLimitI,
1735
+ gasPrice: gasPriceWei,
1736
+ nonce: currentNonce,
1737
+ chainId: args.chainId
1738
+ });
1739
+ const h = viem.keccak256(ser);
1740
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1741
+ messageRawBatch.push(ser);
1742
+ proposalTxParamsBatch.push({
1743
+ nonce: currentNonce,
1744
+ gasLimit: gasLimitI.toString(),
1745
+ txType: "legacy",
1746
+ gasPrice: gasPriceWei.toString()
1747
+ });
1748
+ firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
1749
+ firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1750
+ } else {
1751
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
1752
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
1753
+ const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
1754
+ const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
1755
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
1756
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
1757
+ const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
1758
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
1759
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
1760
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
1761
+ let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
1762
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
1763
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1764
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
1765
+ }
1766
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
1767
+ maxFeePerGas,
1768
+ maxPriorityFeePerGas,
1769
+ latestBaseFeeWeiOneStep
1770
+ ));
1771
+ const ser = viem.serializeTransaction({
1772
+ type: "eip1559",
1773
+ to: v.to,
1774
+ data: v.data,
1775
+ value: v.value,
1776
+ gas: gasLimitI,
1777
+ maxFeePerGas,
1778
+ maxPriorityFeePerGas,
1779
+ nonce: currentNonce,
1780
+ chainId: args.chainId
1781
+ });
1782
+ const h = viem.keccak256(ser);
1783
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
1784
+ messageRawBatch.push(ser);
1785
+ proposalTxParamsBatch.push({
1786
+ nonce: currentNonce,
1787
+ gasLimit: gasLimitI.toString(),
1788
+ txType: "eip1559",
1789
+ maxFeePerGas: maxFeePerGas.toString(),
1790
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1791
+ });
1792
+ firstTxFeePayload = {
1793
+ txNonce: currentNonce,
1794
+ txGasLimit: gasLimitI.toString(),
1795
+ txMaxFeePerGas: maxFeePerGas.toString(),
1796
+ txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
1797
+ };
1798
+ firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
1799
+ }
1800
+ const extraPayload = { batchMeta: [batchMeta0] };
1801
+ if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
1802
+ extraPayload.customGasChainDetails = args.customGasChainDetails;
1803
+ }
1804
+ const extraJSON = JSON.stringify(extraPayload);
1805
+ const firstSigText = batchMeta0.signatureText;
1806
+ const bodyForSign = {
1807
+ keyList: args.keyList,
1808
+ pubKey: args.ph,
1809
+ msgHash: messageHashes[0],
1810
+ msgRaw: firstDataNo0x,
1811
+ messageHashes,
1812
+ messageRawBatch,
1813
+ destinationChainID: String(args.chainId),
1814
+ destinationAddress: v.to,
1815
+ extraJSON,
1816
+ signatureText: firstSigText,
1817
+ purpose: (() => {
1818
+ const t = args.purposeText.trim();
1819
+ return (t ? `${t}
1820
+
1821
+ ` : "") + args.purposeSuffix;
1822
+ })(),
1823
+ ...firstTxFeePayload,
1824
+ proposalTxParams: proposalTxParamsBatch
1825
+ };
1826
+ if (v.value > 0n) {
1827
+ bodyForSign.value = v.value.toString();
1828
+ }
1829
+ if (args.clientId) bodyForSign.clientId = args.clientId;
1830
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
1831
+ }
1832
+ var MIN_AAVE_V4_DEPOSIT_GAS_EXEC = 400000n;
1833
+ var AAVE_V4_EVM_TYPES = /* @__PURE__ */ new Set([
1834
+ "aave_v4_weth_deposit",
1835
+ "aave_v4_erc20_approve",
1836
+ "aave_v4_spoke_supply",
1837
+ "aave_v4_spoke_set_using_as_collateral",
1838
+ "aave_v4_spoke_withdraw",
1839
+ "aave_v4_spoke_borrow",
1840
+ "aave_v4_spoke_repay",
1841
+ "aave_v4_merkl_claim_rewards"
1842
+ ]);
1843
+ function parseExtraJsonObject(detail) {
1844
+ if (!detail) return null;
1845
+ const raw = detail.ExtraJSON ?? detail.extraJSON;
1846
+ if (raw == null) return null;
1847
+ if (typeof raw === "object" && !Array.isArray(raw)) return raw;
1848
+ if (typeof raw === "string" && raw.trim()) {
1849
+ try {
1850
+ const p = JSON.parse(raw);
1851
+ if (p && typeof p === "object" && !Array.isArray(p)) return p;
1852
+ } catch {
1853
+ return null;
1854
+ }
1855
+ }
1856
+ return null;
1857
+ }
1858
+ function parseOptionalGasLimitString(raw) {
1859
+ if (raw == null) return null;
1860
+ const s = String(raw).trim();
1861
+ if (!s) return null;
1862
+ try {
1863
+ if (/^0x[0-9a-fA-F]+$/.test(s)) return BigInt(s);
1864
+ return BigInt(s);
1865
+ } catch {
1866
+ return null;
1867
+ }
1868
+ }
1869
+ function isAaveV4DepositEvmSignRequest(detail, batchIndex) {
1870
+ const ex = parseExtraJsonObject(detail);
1871
+ if (!ex) return false;
1872
+ const idx = batchIndex != null && batchIndex >= 0 ? batchIndex : 0;
1873
+ const bm = ex.batchMeta;
1874
+ if (Array.isArray(bm) && bm[idx] && typeof bm[idx] === "object" && !Array.isArray(bm[idx])) {
1875
+ const evm0 = bm[idx].evm;
1876
+ if (evm0 && typeof evm0 === "object" && !Array.isArray(evm0)) {
1877
+ if (AAVE_V4_EVM_TYPES.has(String(evm0.type ?? ""))) return true;
1878
+ }
1879
+ }
1880
+ if (batchIndex != null && batchIndex >= 0) return false;
1881
+ const evm = ex.evm;
1882
+ if (evm && typeof evm === "object" && !Array.isArray(evm)) {
1883
+ if (AAVE_V4_EVM_TYPES.has(String(evm.type ?? ""))) return true;
1884
+ }
1885
+ return false;
1886
+ }
1887
+ function resolveAaveV4DepositGasUnitsFromSignRequest(detail, batchIndex) {
1888
+ if (!detail) return null;
1889
+ const index = batchIndex != null && batchIndex >= 0 ? batchIndex : 0;
1890
+ const propBatch = detail.proposal_tx_params ?? detail.proposalTxParams ?? detail.ProposalTxParams;
1891
+ if (Array.isArray(propBatch) && propBatch.length > index) {
1892
+ const row = propBatch[index];
1893
+ if (row && typeof row === "object" && !Array.isArray(row)) {
1894
+ const r = row;
1895
+ const gl = parseOptionalGasLimitString(
1896
+ r.gas_limit ?? r.gasLimit ?? r.GasLimit
1897
+ );
1898
+ if (gl != null && gl > 0n) return gl;
1899
+ }
1900
+ }
1901
+ if (batchIndex == null || batchIndex === 0) {
1902
+ const tp = detail.txParams ?? detail.TxParams;
1903
+ if (tp && typeof tp === "object") {
1904
+ const gl = parseOptionalGasLimitString(
1905
+ tp.gasLimit ?? tp.GasLimit ?? tp.txGasLimit
1906
+ );
1907
+ if (gl != null && gl > 0n) return gl;
1908
+ }
1909
+ }
1910
+ const ex = parseExtraJsonObject(detail);
1911
+ const bm = ex?.batchMeta;
1912
+ if (Array.isArray(bm) && bm[index] && typeof bm[index] === "object" && !Array.isArray(bm[index])) {
1913
+ const a4 = bm[index].aaveV4;
1914
+ if (a4 && typeof a4 === "object") {
1915
+ const gs = a4.gasBuildSupply ?? a4.gasBuildSetCollateral ?? a4.gasBuildWithdraw ?? a4.gasBuildBorrow ?? a4.gasBuildRepay ?? a4.gasBuildClaim;
1916
+ const base = parseOptionalGasLimitString(gs?.baseGasUnits);
1917
+ if (base != null && base > 0n) return base;
1918
+ }
1919
+ }
1920
+ return null;
1921
+ }
1922
+
1923
+ // src/protocols/evm/aave-v4/index.ts
1924
+ var AAVE_V4_PROTOCOL_ID = "aave-v4";
1925
+ var aaveV4ProtocolModule = {
1926
+ id: AAVE_V4_PROTOCOL_ID,
1927
+ chainCategory: "evm",
1928
+ isChainSupported(ctx) {
1929
+ return ctx.chainCategory === "evm";
1930
+ },
1931
+ isTokenSupported(token) {
1932
+ return token.category === "evm" && (token.kind === "native" || token.kind === "erc20");
1933
+ },
1934
+ actions: [
1935
+ { id: "aave-v4.deposit", protocolId: AAVE_V4_PROTOCOL_ID, chainCategory: "evm", description: "Supply to Aave v4 Spoke", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
1936
+ { id: "aave-v4.withdraw", protocolId: AAVE_V4_PROTOCOL_ID, chainCategory: "evm", description: "Withdraw from Spoke", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
1937
+ { id: "aave-v4.borrow", protocolId: AAVE_V4_PROTOCOL_ID, chainCategory: "evm", description: "Borrow from Spoke", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
1938
+ { id: "aave-v4.repay", protocolId: AAVE_V4_PROTOCOL_ID, chainCategory: "evm", description: "Repay Spoke debt", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} }
1939
+ ]
1940
+ };
1941
+ registerProtocolModule(aaveV4ProtocolModule);
1942
+
1943
+ exports.AAVE_V4_GRAPHQL_URL = AAVE_V4_GRAPHQL_URL;
1944
+ exports.AAVE_V4_PROTOCOL_ID = AAVE_V4_PROTOCOL_ID;
1945
+ exports.AAVE_V4_SPOKE_BORROW_DEFAULT_GAS_UNITS = AAVE_V4_SPOKE_BORROW_DEFAULT_GAS_UNITS;
1946
+ exports.AAVE_V4_SPOKE_REPAY_DEFAULT_GAS_UNITS = AAVE_V4_SPOKE_REPAY_DEFAULT_GAS_UNITS;
1947
+ exports.AAVE_V4_SPOKE_SUPPLY_DEFAULT_GAS_UNITS = AAVE_V4_SPOKE_SUPPLY_DEFAULT_GAS_UNITS;
1948
+ exports.AAVE_V4_SPOKE_WITHDRAW_DEFAULT_GAS_UNITS = AAVE_V4_SPOKE_WITHDRAW_DEFAULT_GAS_UNITS;
1949
+ exports.MIN_AAVE_V4_DEPOSIT_GAS_EXEC = MIN_AAVE_V4_DEPOSIT_GAS_EXEC;
1950
+ exports.aaveV4Gql = aaveV4Gql;
1951
+ exports.aaveV4KeyForNodeAssetRow = aaveV4KeyForNodeAssetRow;
1952
+ exports.aaveV4ProtocolModule = aaveV4ProtocolModule;
1953
+ exports.aaveV4UiMarketIdForHubName = aaveV4UiMarketIdForHubName;
1954
+ exports.aggregateV4SupplyDisplay = aggregateV4SupplyDisplay;
1955
+ exports.borrowableAssetsFromHubReserves = borrowableAssetsFromHubReserves;
1956
+ exports.buildAaveV4BorrowTableRowsFromHub = buildAaveV4BorrowTableRowsFromHub;
1957
+ exports.buildEvmMultisignBodyAaveV4DepositBatch = buildEvmMultisignBodyAaveV4DepositBatch;
1958
+ exports.buildEvmMultisignBodyAaveV4MerklClaimRewards = buildEvmMultisignBodyAaveV4MerklClaimRewards;
1959
+ exports.buildEvmMultisignBodyAaveV4SpokeBorrow = buildEvmMultisignBodyAaveV4SpokeBorrow;
1960
+ exports.buildEvmMultisignBodyAaveV4SpokeRepay = buildEvmMultisignBodyAaveV4SpokeRepay;
1961
+ exports.buildEvmMultisignBodyAaveV4SpokeWithdraw = buildEvmMultisignBodyAaveV4SpokeWithdraw;
1962
+ exports.buildEvmMultisignBodyEulerV2MerklDistributorClaim = buildEvmMultisignBodyEulerV2MerklDistributorClaim;
1963
+ exports.buildEvmMultisignBodyEulerV2ReulUnlock = buildEvmMultisignBodyEulerV2ReulUnlock;
1964
+ exports.ensureAaveV4ChainTokenCache = ensureAaveV4ChainTokenCache;
1965
+ exports.fetchAaveV4Chains = fetchAaveV4Chains;
1966
+ exports.fetchAaveV4HubReserves = fetchAaveV4HubReserves;
1967
+ exports.fetchAaveV4HubsForChain = fetchAaveV4HubsForChain;
1968
+ exports.fetchAaveV4NativeWrappedToken = fetchAaveV4NativeWrappedToken;
1969
+ exports.fetchAaveV4ReservesForUnderlying = fetchAaveV4ReservesForUnderlying;
1970
+ exports.fetchAaveV4SpokeReserveIdForUnderlying = fetchAaveV4SpokeReserveIdForUnderlying;
1971
+ exports.fetchAaveV4SpokeReserveStatusForUnderlying = fetchAaveV4SpokeReserveStatusForUnderlying;
1972
+ exports.fetchAaveV4SupportedUnderlyingAddressSet = fetchAaveV4SupportedUnderlyingAddressSet;
1973
+ exports.fetchAaveV4UserBorrowsDebtByUnderlyingForHub = fetchAaveV4UserBorrowsDebtByUnderlyingForHub;
1974
+ exports.findAaveV4HubReserveForChainUnderlying = findAaveV4HubReserveForChainUnderlying;
1975
+ exports.findHubReserveForUnderlying = findHubReserveForUnderlying;
1976
+ exports.formatAaveV4AmountHumanFromApiString = formatAaveV4AmountHumanFromApiString;
1977
+ exports.formatAaveV4PercentDisplay = formatAaveV4PercentDisplay;
1978
+ exports.formatAaveV4ReserveLiquidityFromSummary = formatAaveV4ReserveLiquidityFromSummary;
1979
+ exports.formatAaveV4RiskDisplay = formatAaveV4RiskDisplay;
1980
+ exports.formatAaveV4TokenAmount = formatAaveV4TokenAmount;
1981
+ exports.isAaveV4DepositEvmSignRequest = isAaveV4DepositEvmSignRequest;
1982
+ exports.loadAaveV4SupportedChainIdsFromV4Api = loadAaveV4SupportedChainIdsFromV4Api;
1983
+ exports.pickAaveV4ReserveRowForSpoke = pickAaveV4ReserveRowForSpoke;
1984
+ exports.resolveAaveV4DepositGasUnitsFromSignRequest = resolveAaveV4DepositGasUnitsFromSignRequest;
1985
+ exports.resolveAaveV4HubForUiMarket = resolveAaveV4HubForUiMarket;
1986
+ //# sourceMappingURL=index.cjs.map
1987
+ //# sourceMappingURL=index.cjs.map