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