@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.
- package/README.md +16 -1
- package/dist/agent/catalog.cjs +913 -142
- package/dist/agent/catalog.cjs.map +1 -1
- package/dist/agent/catalog.d.cts +834 -12
- package/dist/agent/catalog.d.ts +834 -12
- package/dist/agent/catalog.js +863 -143
- package/dist/agent/catalog.js.map +1 -1
- package/dist/chains/evm/index.cjs +13 -0
- package/dist/chains/evm/index.cjs.map +1 -1
- package/dist/chains/evm/index.d.cts +3 -1
- package/dist/chains/evm/index.d.ts +3 -1
- package/dist/chains/evm/index.js +13 -1
- package/dist/chains/evm/index.js.map +1 -1
- package/dist/core/index.cjs +76 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +35 -2
- package/dist/core/index.d.ts +35 -2
- package/dist/core/index.js +70 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +934 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +927 -142
- package/dist/index.js.map +1 -1
- package/dist/protocols/evm/aave-v4/index.cjs +1987 -0
- package/dist/protocols/evm/aave-v4/index.cjs.map +1 -0
- package/dist/protocols/evm/aave-v4/index.d.cts +500 -0
- package/dist/protocols/evm/aave-v4/index.d.ts +500 -0
- package/dist/protocols/evm/aave-v4/index.js +1943 -0
- package/dist/protocols/evm/aave-v4/index.js.map +1 -0
- package/dist/protocols/evm/ethena/index.cjs +965 -0
- package/dist/protocols/evm/ethena/index.cjs.map +1 -0
- package/dist/protocols/evm/ethena/index.d.cts +161 -0
- package/dist/protocols/evm/ethena/index.d.ts +161 -0
- package/dist/protocols/evm/ethena/index.js +943 -0
- package/dist/protocols/evm/ethena/index.js.map +1 -0
- package/dist/protocols/evm/euler-v2/index.cjs +2263 -0
- package/dist/protocols/evm/euler-v2/index.cjs.map +1 -0
- package/dist/protocols/evm/euler-v2/index.d.cts +317 -0
- package/dist/protocols/evm/euler-v2/index.d.ts +317 -0
- package/dist/protocols/evm/euler-v2/index.js +2238 -0
- package/dist/protocols/evm/euler-v2/index.js.map +1 -0
- package/dist/protocols/evm/lido/index.cjs +834 -0
- package/dist/protocols/evm/lido/index.cjs.map +1 -0
- package/dist/protocols/evm/lido/index.d.cts +120 -0
- package/dist/protocols/evm/lido/index.d.ts +120 -0
- package/dist/protocols/evm/lido/index.js +809 -0
- package/dist/protocols/evm/lido/index.js.map +1 -0
- package/dist/protocols/evm/maple/index.cjs +707 -0
- package/dist/protocols/evm/maple/index.cjs.map +1 -0
- package/dist/protocols/evm/maple/index.d.cts +109 -0
- package/dist/protocols/evm/maple/index.d.ts +109 -0
- package/dist/protocols/evm/maple/index.js +693 -0
- package/dist/protocols/evm/maple/index.js.map +1 -0
- package/dist/protocols/evm/sky/index.cjs +1254 -0
- package/dist/protocols/evm/sky/index.cjs.map +1 -0
- package/dist/protocols/evm/sky/index.d.cts +218 -0
- package/dist/protocols/evm/sky/index.d.ts +218 -0
- package/dist/protocols/evm/sky/index.js +1229 -0
- package/dist/protocols/evm/sky/index.js.map +1 -0
- package/dist/protocols/evm/uniswap-v4/index.cjs +31 -11
- package/dist/protocols/evm/uniswap-v4/index.cjs.map +1 -1
- package/dist/protocols/evm/uniswap-v4/index.d.cts +2 -1
- package/dist/protocols/evm/uniswap-v4/index.d.ts +2 -1
- package/dist/protocols/evm/uniswap-v4/index.js +31 -11
- package/dist/protocols/evm/uniswap-v4/index.js.map +1 -1
- package/package.json +37 -3
|
@@ -0,0 +1,2263 @@
|
|
|
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 EULER_SAME_ASSET_BORROW_PROTOCOL_HEADROOM_BPS = 50n;
|
|
16
|
+
var EULER_SAME_ASSET_BORROW_APPROVAL_BUFFER_BPS = 100n;
|
|
17
|
+
var EULER_SAME_ASSET_BORROW_MAX_ROUNDS = 48;
|
|
18
|
+
var EULER_SAME_ASSET_BORROW_RATIO_STOP_EPS_BPS = 15n;
|
|
19
|
+
function eulerBorrowAndCollateralSameAsset(row) {
|
|
20
|
+
const c = (row.collateralAssetAddressLower ?? "").trim().toLowerCase();
|
|
21
|
+
const b = (row.borrowAssetAddressLower ?? "").trim().toLowerCase();
|
|
22
|
+
if (!c.startsWith("0x") || !b.startsWith("0x")) return false;
|
|
23
|
+
try {
|
|
24
|
+
return viem.getAddress(c).toLowerCase() === viem.getAddress(b).toLowerCase();
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function planSameAssetLeveragedBorrows(args) {
|
|
30
|
+
const { initialCollateralWei: C0wei, oneBorrowUnitUoAWei, borrowDecimals, maxNewBorrowWei } = args;
|
|
31
|
+
const targetRaw = Number(args.targetLtvBps);
|
|
32
|
+
const maxLtvBps = Number(args.maxLtvBps);
|
|
33
|
+
if (C0wei <= 0n || oneBorrowUnitUoAWei <= 0n || maxNewBorrowWei <= 0n) return null;
|
|
34
|
+
if (!Number.isFinite(targetRaw) || !(targetRaw > 0) || targetRaw >= 1e4) return null;
|
|
35
|
+
if (!Number.isFinite(maxLtvBps) || !(maxLtvBps > 0) || maxLtvBps > 1e4) return null;
|
|
36
|
+
const unit = 10n ** BigInt(borrowDecimals);
|
|
37
|
+
const effectiveMax = maxLtvBps > Number(EULER_SAME_ASSET_BORROW_PROTOCOL_HEADROOM_BPS) ? maxLtvBps - Number(EULER_SAME_ASSET_BORROW_PROTOCOL_HEADROOM_BPS) : maxLtvBps;
|
|
38
|
+
let targetBps = Math.min(targetRaw, effectiveMax);
|
|
39
|
+
if (targetBps <= 0) return null;
|
|
40
|
+
const workingCeiling = Math.max(1, effectiveMax - Number(EULER_SAME_ASSET_BORROW_RATIO_STOP_EPS_BPS));
|
|
41
|
+
targetBps = Math.min(targetBps, workingCeiling);
|
|
42
|
+
const uoaFromBorrowWei = (w) => {
|
|
43
|
+
if (w <= 0n) return 0n;
|
|
44
|
+
return w * oneBorrowUnitUoAWei / unit;
|
|
45
|
+
};
|
|
46
|
+
let C = uoaFromBorrowWei(C0wei);
|
|
47
|
+
let D = 0n;
|
|
48
|
+
if (C <= 0n) return null;
|
|
49
|
+
const borrowWeis = [];
|
|
50
|
+
let liquidityLeft = maxNewBorrowWei;
|
|
51
|
+
const targetBn = BigInt(targetBps);
|
|
52
|
+
const stopBn = targetBn - BigInt(Number(EULER_SAME_ASSET_BORROW_RATIO_STOP_EPS_BPS));
|
|
53
|
+
const capBn = BigInt(effectiveMax);
|
|
54
|
+
for (let i = 0; i < EULER_SAME_ASSET_BORROW_MAX_ROUNDS; i++) {
|
|
55
|
+
if (C <= 0n) break;
|
|
56
|
+
const ratioBps = D * 10000n / C;
|
|
57
|
+
if (ratioBps >= stopBn) break;
|
|
58
|
+
const headroomUoa = capBn * C / 10000n - D;
|
|
59
|
+
if (headroomUoa <= 0n) break;
|
|
60
|
+
const den = 10000n - targetBn;
|
|
61
|
+
if (den <= 0n) break;
|
|
62
|
+
let bUoa = (targetBn * C - 10000n * D) / den;
|
|
63
|
+
if (bUoa <= 0n) break;
|
|
64
|
+
if (bUoa > headroomUoa) bUoa = headroomUoa;
|
|
65
|
+
let borrowWei = bUoa * unit / oneBorrowUnitUoAWei;
|
|
66
|
+
if (borrowWei <= 0n) break;
|
|
67
|
+
if (borrowWei > liquidityLeft) borrowWei = liquidityLeft;
|
|
68
|
+
if (borrowWei <= 0n) break;
|
|
69
|
+
const bUoaActual = uoaFromBorrowWei(borrowWei);
|
|
70
|
+
borrowWeis.push(borrowWei);
|
|
71
|
+
D += bUoaActual;
|
|
72
|
+
C += bUoaActual;
|
|
73
|
+
liquidityLeft -= borrowWei;
|
|
74
|
+
}
|
|
75
|
+
if (borrowWeis.length === 0) return null;
|
|
76
|
+
const totalBorrowWei = borrowWeis.reduce((a, w) => a + w, 0n);
|
|
77
|
+
const projectedFinalLtvBps = C > 0n ? D * 10000n / C : 0n;
|
|
78
|
+
return { borrowWeis, totalBorrowWei, projectedFinalLtvBps };
|
|
79
|
+
}
|
|
80
|
+
function eulerSameAssetTotalCollateralPullWei(args) {
|
|
81
|
+
let s = args.initialCollateralWei;
|
|
82
|
+
for (const w of args.loopBorrowWeis) s += w;
|
|
83
|
+
return s;
|
|
84
|
+
}
|
|
85
|
+
function eulerSameAssetApproveAmountWithBuffer(args) {
|
|
86
|
+
const extra = args.totalPullWei * EULER_SAME_ASSET_BORROW_APPROVAL_BUFFER_BPS / 10000n;
|
|
87
|
+
return args.totalPullWei + extra + 1n;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/core/keygen.ts
|
|
91
|
+
function firstClientIdFromKeyGen(data) {
|
|
92
|
+
const map = data?.ClientKeys;
|
|
93
|
+
if (!map || typeof map !== "object") return null;
|
|
94
|
+
for (const v of Object.values(map)) {
|
|
95
|
+
if (typeof v === "string" && v.trim()) return v.trim();
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/chains/evm/txParams.ts
|
|
101
|
+
function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
|
|
102
|
+
if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
|
|
103
|
+
return estimatedGas;
|
|
104
|
+
}
|
|
105
|
+
const cfg = BigInt(Math.floor(chainGasLimit));
|
|
106
|
+
return cfg > estimatedGas ? cfg : estimatedGas;
|
|
107
|
+
}
|
|
108
|
+
async function fetchChainFeeParams(rpcUrl, chainId) {
|
|
109
|
+
const url = rpcUrl.trim();
|
|
110
|
+
if (!url) return { isEip1559: false };
|
|
111
|
+
const chainIdNum = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
|
|
112
|
+
if (Number.isNaN(chainIdNum)) return { isEip1559: false };
|
|
113
|
+
const chain = viem.defineChain({
|
|
114
|
+
id: chainIdNum,
|
|
115
|
+
name: "Discovery",
|
|
116
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
117
|
+
rpcUrls: { default: { http: [url] } }
|
|
118
|
+
});
|
|
119
|
+
const publicClient = viem.createPublicClient({
|
|
120
|
+
chain,
|
|
121
|
+
transport: viem.http(url)
|
|
122
|
+
});
|
|
123
|
+
const getGasPriceGwei = async () => {
|
|
124
|
+
const gasPriceWei = await publicClient.getGasPrice();
|
|
125
|
+
return parseFloat(viem.formatUnits(gasPriceWei, 9));
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
const block = await publicClient.getBlock({ blockTag: "latest" });
|
|
129
|
+
const baseFeePerGas = block?.baseFeePerGas;
|
|
130
|
+
if (baseFeePerGas == null || baseFeePerGas === void 0) {
|
|
131
|
+
const gasPriceGwei2 = await getGasPriceGwei();
|
|
132
|
+
return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
|
|
133
|
+
}
|
|
134
|
+
const baseFeeGwei = parseFloat(viem.formatUnits(baseFeePerGas, 9));
|
|
135
|
+
let priorityFeeGwei;
|
|
136
|
+
try {
|
|
137
|
+
const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
|
|
138
|
+
priorityFeeGwei = parseFloat(viem.formatUnits(priorityWei, 9));
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
141
|
+
const gasPriceGwei = await getGasPriceGwei();
|
|
142
|
+
return {
|
|
143
|
+
isEip1559: true,
|
|
144
|
+
baseFeeGwei,
|
|
145
|
+
priorityFeeGwei,
|
|
146
|
+
gasPriceGwei
|
|
147
|
+
};
|
|
148
|
+
} catch {
|
|
149
|
+
try {
|
|
150
|
+
const gasPriceWei = await publicClient.getGasPrice();
|
|
151
|
+
const gasPriceGwei = parseFloat(viem.formatUnits(gasPriceWei, 9));
|
|
152
|
+
return { isEip1559: false, gasPriceGwei };
|
|
153
|
+
} catch {
|
|
154
|
+
return { isEip1559: false };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
|
|
159
|
+
let maxP = maxPriorityFeePerGas;
|
|
160
|
+
let maxF = maxFeePerGas;
|
|
161
|
+
if (baseWei > 0n && maxF < baseWei + maxP) {
|
|
162
|
+
maxF = baseWei + maxP + viem.parseGwei("0.001");
|
|
163
|
+
}
|
|
164
|
+
if (maxF < maxP) {
|
|
165
|
+
maxF = baseWei > 0n ? baseWei + maxP + viem.parseGwei("0.001") : maxP * 2n;
|
|
166
|
+
}
|
|
167
|
+
return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
|
|
168
|
+
}
|
|
169
|
+
function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
|
|
170
|
+
return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/protocols/evm/euler-v2/vaultWithdrawMultisign.ts
|
|
174
|
+
var EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS = 900000n;
|
|
175
|
+
function gweiToDecimalString(n) {
|
|
176
|
+
if (!Number.isFinite(n)) return "0";
|
|
177
|
+
if (n === 0) return "0";
|
|
178
|
+
const s = String(n);
|
|
179
|
+
if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
|
|
180
|
+
return s;
|
|
181
|
+
}
|
|
182
|
+
var erc20DecimalsAbi = viem.parseAbi(["function decimals() view returns (uint8)"]);
|
|
183
|
+
var erc4626AssetAbi = viem.parseAbi(["function asset() view returns (address)"]);
|
|
184
|
+
var erc4626MaxWithdrawAbi = viem.parseAbi(["function maxWithdraw(address owner) view returns (uint256)"]);
|
|
185
|
+
var erc4626WithdrawAbi = viem.parseAbi([
|
|
186
|
+
"function withdraw(uint256 assets, address receiver, address owner) returns (uint256 shares)"
|
|
187
|
+
]);
|
|
188
|
+
async function fetchEulerVaultUnderlyingMeta(args) {
|
|
189
|
+
const ch = viem.defineChain({
|
|
190
|
+
id: args.chainId,
|
|
191
|
+
name: "Destination",
|
|
192
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
193
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
194
|
+
});
|
|
195
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
196
|
+
const assetAddrRaw = await publicClient.readContract({
|
|
197
|
+
address: args.evault,
|
|
198
|
+
abi: erc4626AssetAbi,
|
|
199
|
+
functionName: "asset"
|
|
200
|
+
});
|
|
201
|
+
const assetAddr = viem.getAddress(assetAddrRaw);
|
|
202
|
+
const d = await publicClient.readContract({
|
|
203
|
+
address: assetAddr,
|
|
204
|
+
abi: erc20DecimalsAbi,
|
|
205
|
+
functionName: "decimals"
|
|
206
|
+
});
|
|
207
|
+
const n = typeof d === "bigint" ? Number(d) : Number(d);
|
|
208
|
+
const decimals = !Number.isFinite(n) || n < 0 || n > 36 ? 18 : n;
|
|
209
|
+
return { asset: assetAddr, decimals };
|
|
210
|
+
}
|
|
211
|
+
function clampEulerUnderlyingDecimalsForEulerUi(args) {
|
|
212
|
+
const a = args.underlyingAssetLower.trim().toLowerCase();
|
|
213
|
+
let d = args.fetchedDecimals;
|
|
214
|
+
if (args.wrappedGasAliasesLower.has(a)) {
|
|
215
|
+
return Math.max(d, 18);
|
|
216
|
+
}
|
|
217
|
+
if (d >= 18) return d;
|
|
218
|
+
const label = `${args.underlyingSymbol} ${args.marketName}`;
|
|
219
|
+
const ethish = /\b(WETH|wstETH|stETH|rETH|weETH|eETH)\b/i.test(label) || /(^|[^A-Z0-9])ETH([^A-Z0-9]|$)/i.test(label);
|
|
220
|
+
if (ethish) return 18;
|
|
221
|
+
return d;
|
|
222
|
+
}
|
|
223
|
+
async function fetchEulerVaultAssetDecimals(args) {
|
|
224
|
+
const m = await fetchEulerVaultUnderlyingMeta(args);
|
|
225
|
+
return m.decimals;
|
|
226
|
+
}
|
|
227
|
+
async function fetchEulerVaultMaxWithdrawWei(args) {
|
|
228
|
+
const ch = viem.defineChain({
|
|
229
|
+
id: args.chainId,
|
|
230
|
+
name: "Destination",
|
|
231
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
232
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
233
|
+
});
|
|
234
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
235
|
+
return publicClient.readContract({
|
|
236
|
+
address: args.evault,
|
|
237
|
+
abi: erc4626MaxWithdrawAbi,
|
|
238
|
+
functionName: "maxWithdraw",
|
|
239
|
+
args: [args.owner]
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
var eulerVaultSharesCashAbi = viem.parseAbi([
|
|
243
|
+
"function balanceOf(address account) view returns (uint256)",
|
|
244
|
+
"function convertToAssets(uint256 shares) view returns (uint256)",
|
|
245
|
+
"function cash() view returns (uint256)"
|
|
246
|
+
]);
|
|
247
|
+
async function eulerDirectWithdrawSimulatesOk(args) {
|
|
248
|
+
const withdrawData = viem.encodeFunctionData({
|
|
249
|
+
abi: erc4626WithdrawAbi,
|
|
250
|
+
functionName: "withdraw",
|
|
251
|
+
args: [args.assetsWei, args.receiver, args.vaultShareOwner]
|
|
252
|
+
});
|
|
253
|
+
try {
|
|
254
|
+
await args.publicClient.call({
|
|
255
|
+
account: args.txSender,
|
|
256
|
+
to: args.evault,
|
|
257
|
+
data: withdrawData,
|
|
258
|
+
gas: 8000000n
|
|
259
|
+
});
|
|
260
|
+
return true;
|
|
261
|
+
} catch {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function eulerLendEarnEffectiveMaxWeiBySimulation(args) {
|
|
266
|
+
const ch = viem.defineChain({
|
|
267
|
+
id: args.chainId,
|
|
268
|
+
name: "EulerLendRedeem",
|
|
269
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
270
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
271
|
+
});
|
|
272
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
273
|
+
let shareBal;
|
|
274
|
+
let vaultCash;
|
|
275
|
+
try {
|
|
276
|
+
;
|
|
277
|
+
[shareBal, vaultCash] = await Promise.all([
|
|
278
|
+
publicClient.readContract({
|
|
279
|
+
address: args.evault,
|
|
280
|
+
abi: eulerVaultSharesCashAbi,
|
|
281
|
+
functionName: "balanceOf",
|
|
282
|
+
args: [args.vaultShareOwner]
|
|
283
|
+
}),
|
|
284
|
+
publicClient.readContract({
|
|
285
|
+
address: args.evault,
|
|
286
|
+
abi: eulerVaultSharesCashAbi,
|
|
287
|
+
functionName: "cash"
|
|
288
|
+
})
|
|
289
|
+
]);
|
|
290
|
+
} catch {
|
|
291
|
+
return 0n;
|
|
292
|
+
}
|
|
293
|
+
if (shareBal === 0n) return 0n;
|
|
294
|
+
let assetsOwned;
|
|
295
|
+
try {
|
|
296
|
+
assetsOwned = await publicClient.readContract({
|
|
297
|
+
address: args.evault,
|
|
298
|
+
abi: eulerVaultSharesCashAbi,
|
|
299
|
+
functionName: "convertToAssets",
|
|
300
|
+
args: [shareBal]
|
|
301
|
+
});
|
|
302
|
+
} catch {
|
|
303
|
+
return 0n;
|
|
304
|
+
}
|
|
305
|
+
const hi = vaultCash <= 0n ? 0n : vaultCash <= assetsOwned ? vaultCash : assetsOwned;
|
|
306
|
+
if (hi === 0n) return 0n;
|
|
307
|
+
let lo = 0n;
|
|
308
|
+
let hiProbe = hi;
|
|
309
|
+
let best = 0n;
|
|
310
|
+
while (lo <= hiProbe) {
|
|
311
|
+
const mid = lo + (hiProbe - lo) / 2n;
|
|
312
|
+
const ok = await eulerDirectWithdrawSimulatesOk({
|
|
313
|
+
publicClient,
|
|
314
|
+
evault: args.evault,
|
|
315
|
+
vaultShareOwner: args.vaultShareOwner,
|
|
316
|
+
receiver: args.receiver,
|
|
317
|
+
txSender: args.txSender,
|
|
318
|
+
assetsWei: mid
|
|
319
|
+
});
|
|
320
|
+
if (ok) {
|
|
321
|
+
best = mid;
|
|
322
|
+
lo = mid + 1n;
|
|
323
|
+
} else {
|
|
324
|
+
hiProbe = mid - 1n;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return best;
|
|
328
|
+
}
|
|
329
|
+
async function fetchEulerLendEarnVaultEffectiveMaxWithdrawWei(args) {
|
|
330
|
+
const rpc = args.rpcUrl.trim();
|
|
331
|
+
if (!rpc) return 0n;
|
|
332
|
+
const evault = viem.getAddress(args.evault);
|
|
333
|
+
const vaultShareOwner = viem.getAddress(args.vaultShareOwner);
|
|
334
|
+
const txSender = viem.getAddress(args.txSender);
|
|
335
|
+
const receiver = txSender;
|
|
336
|
+
const std = await fetchEulerVaultMaxWithdrawWei({
|
|
337
|
+
rpcUrl: rpc,
|
|
338
|
+
chainId: args.chainId,
|
|
339
|
+
evault,
|
|
340
|
+
owner: vaultShareOwner
|
|
341
|
+
});
|
|
342
|
+
if (std > 0n) return std;
|
|
343
|
+
return eulerLendEarnEffectiveMaxWeiBySimulation({
|
|
344
|
+
rpcUrl: rpc,
|
|
345
|
+
chainId: args.chainId,
|
|
346
|
+
evault,
|
|
347
|
+
vaultShareOwner,
|
|
348
|
+
receiver,
|
|
349
|
+
txSender
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
async function eulerMultisignBodyOneStep(args) {
|
|
353
|
+
const ch = viem.defineChain({
|
|
354
|
+
id: args.chainId,
|
|
355
|
+
name: "Destination",
|
|
356
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
357
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
358
|
+
});
|
|
359
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
360
|
+
const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
|
|
361
|
+
const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
|
|
362
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
363
|
+
const useCustomGas = args.useCustomGas;
|
|
364
|
+
const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
|
|
365
|
+
const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
|
|
366
|
+
const baseNonce = await publicClient.getTransactionCount({ address: args.executorAddress, blockTag: "pending" });
|
|
367
|
+
let estimatedGas;
|
|
368
|
+
try {
|
|
369
|
+
estimatedGas = await publicClient.estimateGas({
|
|
370
|
+
to: args.to,
|
|
371
|
+
data: args.data,
|
|
372
|
+
value: args.value,
|
|
373
|
+
account: args.executorAddress
|
|
374
|
+
});
|
|
375
|
+
} catch {
|
|
376
|
+
estimatedGas = args.estimateGasFallback;
|
|
377
|
+
}
|
|
378
|
+
const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
379
|
+
const batchMeta0 = args.buildBatchMeta({ gasLimit: gasLimitI });
|
|
380
|
+
let firstTxFeePayload = {};
|
|
381
|
+
let firstDataNo0x = "";
|
|
382
|
+
const messageHashes = [];
|
|
383
|
+
const messageRawBatch = [];
|
|
384
|
+
const proposalTxParamsBatch = [];
|
|
385
|
+
const vTo = args.to;
|
|
386
|
+
const vData = args.data;
|
|
387
|
+
const vValue = args.value;
|
|
388
|
+
if (legacy) {
|
|
389
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
390
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
391
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
392
|
+
}
|
|
393
|
+
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
394
|
+
const configured = viem.parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
|
|
395
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
396
|
+
}
|
|
397
|
+
const ser = viem.serializeTransaction({
|
|
398
|
+
type: "legacy",
|
|
399
|
+
to: vTo,
|
|
400
|
+
data: vData,
|
|
401
|
+
value: vValue,
|
|
402
|
+
gas: gasLimitI,
|
|
403
|
+
gasPrice: gasPriceWei,
|
|
404
|
+
nonce: baseNonce,
|
|
405
|
+
chainId: args.chainId
|
|
406
|
+
});
|
|
407
|
+
const h = viem.keccak256(ser);
|
|
408
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
409
|
+
messageRawBatch.push(ser);
|
|
410
|
+
proposalTxParamsBatch.push({
|
|
411
|
+
nonce: baseNonce,
|
|
412
|
+
gasLimit: gasLimitI.toString(),
|
|
413
|
+
txType: "legacy",
|
|
414
|
+
gasPrice: gasPriceWei.toString()
|
|
415
|
+
});
|
|
416
|
+
firstTxFeePayload = { txNonce: baseNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
|
|
417
|
+
firstDataNo0x = vData.startsWith("0x") ? vData.slice(2) : vData;
|
|
418
|
+
} else {
|
|
419
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
420
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
421
|
+
const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
422
|
+
const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
423
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
424
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
425
|
+
const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
426
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
427
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
428
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
429
|
+
let maxFeePerGas = viem.parseGwei(gweiToDecimalString(maxFeePerGasGwei));
|
|
430
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
431
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
432
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
433
|
+
}
|
|
434
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
|
|
435
|
+
maxFeePerGas,
|
|
436
|
+
maxPriorityFeePerGas,
|
|
437
|
+
latestBaseFeeWei
|
|
438
|
+
));
|
|
439
|
+
const ser = viem.serializeTransaction({
|
|
440
|
+
type: "eip1559",
|
|
441
|
+
to: vTo,
|
|
442
|
+
data: vData,
|
|
443
|
+
value: vValue,
|
|
444
|
+
gas: gasLimitI,
|
|
445
|
+
maxFeePerGas,
|
|
446
|
+
maxPriorityFeePerGas,
|
|
447
|
+
nonce: baseNonce,
|
|
448
|
+
chainId: args.chainId
|
|
449
|
+
});
|
|
450
|
+
const h = viem.keccak256(ser);
|
|
451
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
452
|
+
messageRawBatch.push(ser);
|
|
453
|
+
proposalTxParamsBatch.push({
|
|
454
|
+
nonce: baseNonce,
|
|
455
|
+
gasLimit: gasLimitI.toString(),
|
|
456
|
+
txType: "eip1559",
|
|
457
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
458
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
459
|
+
});
|
|
460
|
+
firstTxFeePayload = {
|
|
461
|
+
txNonce: baseNonce,
|
|
462
|
+
txGasLimit: gasLimitI.toString(),
|
|
463
|
+
txMaxFeePerGas: maxFeePerGas.toString(),
|
|
464
|
+
txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
465
|
+
};
|
|
466
|
+
firstDataNo0x = vData.startsWith("0x") ? vData.slice(2) : vData;
|
|
467
|
+
}
|
|
468
|
+
const extraPayload = { batchMeta: [batchMeta0] };
|
|
469
|
+
if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
|
|
470
|
+
extraPayload.customGasChainDetails = args.customGasChainDetails;
|
|
471
|
+
}
|
|
472
|
+
const extraJSON = JSON.stringify(extraPayload);
|
|
473
|
+
const firstSigText = batchMeta0.signatureText;
|
|
474
|
+
const bodyForSign = {
|
|
475
|
+
keyList: args.keyList,
|
|
476
|
+
pubKey: args.ph,
|
|
477
|
+
msgHash: messageHashes[0],
|
|
478
|
+
msgRaw: firstDataNo0x,
|
|
479
|
+
messageHashes,
|
|
480
|
+
messageRawBatch,
|
|
481
|
+
destinationChainID: String(args.chainId),
|
|
482
|
+
destinationAddress: vTo,
|
|
483
|
+
extraJSON,
|
|
484
|
+
signatureText: firstSigText,
|
|
485
|
+
purpose: (() => {
|
|
486
|
+
const t = args.purposeText.trim();
|
|
487
|
+
return (t ? `${t}
|
|
488
|
+
|
|
489
|
+
` : "") + args.purposeSuffix;
|
|
490
|
+
})(),
|
|
491
|
+
...firstTxFeePayload,
|
|
492
|
+
proposalTxParams: proposalTxParamsBatch
|
|
493
|
+
};
|
|
494
|
+
if (vValue > 0n) bodyForSign.value = vValue.toString();
|
|
495
|
+
if (args.clientId) bodyForSign.clientId = args.clientId;
|
|
496
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
497
|
+
}
|
|
498
|
+
async function buildEvmMultisignBodyEulerV2VaultWithdraw(args) {
|
|
499
|
+
const ph = (args.keyGen.pubkeyhex ?? "").trim();
|
|
500
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
501
|
+
const keyList = args.keyGen.keylist ?? [];
|
|
502
|
+
const clientId = firstClientIdFromKeyGen(args.keyGen);
|
|
503
|
+
const evault = viem.getAddress(args.evault);
|
|
504
|
+
const receiver = viem.getAddress(args.owner);
|
|
505
|
+
const shareOwner = viem.getAddress(args.vaultShareOwner ?? args.owner);
|
|
506
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
507
|
+
const dec = await fetchEulerVaultAssetDecimals({ rpcUrl: args.rpcUrl, chainId: args.chainId, evault });
|
|
508
|
+
const amountWei = viem.parseUnits(args.amountHuman, dec);
|
|
509
|
+
if (amountWei === 0n) throw new Error("Withdraw amount is zero after converting with token decimals.");
|
|
510
|
+
const maxW = await fetchEulerLendEarnVaultEffectiveMaxWithdrawWei({
|
|
511
|
+
rpcUrl: args.rpcUrl,
|
|
512
|
+
chainId: args.chainId,
|
|
513
|
+
evault,
|
|
514
|
+
vaultShareOwner: shareOwner,
|
|
515
|
+
txSender: executor
|
|
516
|
+
});
|
|
517
|
+
if (amountWei > maxW) {
|
|
518
|
+
throw new Error("Withdraw amount exceeds simulated max redeem for this vault (LTV / vault cash). Try a smaller amount.");
|
|
519
|
+
}
|
|
520
|
+
const withdrawData = viem.encodeFunctionData({
|
|
521
|
+
abi: erc4626WithdrawAbi,
|
|
522
|
+
functionName: "withdraw",
|
|
523
|
+
args: [amountWei, receiver, shareOwner]
|
|
524
|
+
});
|
|
525
|
+
const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
|
|
526
|
+
const purposeSuffix = `Euler v2: 1-tx \u2014 eVault.withdraw (${args.amountHuman} underlying) from isolated vault "${vaultLabel}" (ERC-4626).`;
|
|
527
|
+
return eulerMultisignBodyOneStep({
|
|
528
|
+
ph,
|
|
529
|
+
keyList,
|
|
530
|
+
clientId: clientId ?? void 0,
|
|
531
|
+
chainId: args.chainId,
|
|
532
|
+
rpcUrl: args.rpcUrl,
|
|
533
|
+
chainDetail: args.chainDetail,
|
|
534
|
+
useCustomGas: args.useCustomGas,
|
|
535
|
+
customGasChainDetails: args.customGasChainDetails,
|
|
536
|
+
purposeText: args.purposeText,
|
|
537
|
+
purposeSuffix,
|
|
538
|
+
executorAddress: executor,
|
|
539
|
+
to: evault,
|
|
540
|
+
data: withdrawData,
|
|
541
|
+
value: 0n,
|
|
542
|
+
estimateGasFallback: EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS,
|
|
543
|
+
buildBatchMeta: (ctx) => ({
|
|
544
|
+
destinationAddress: evault,
|
|
545
|
+
signatureText: JSON.stringify({
|
|
546
|
+
kind: "EulerV2",
|
|
547
|
+
name: "EVault.withdraw",
|
|
548
|
+
function: "withdraw(uint256 assets, address receiver, address owner)",
|
|
549
|
+
evault,
|
|
550
|
+
receiver,
|
|
551
|
+
owner: shareOwner,
|
|
552
|
+
vaultMarket: vaultLabel,
|
|
553
|
+
amountHuman: args.amountHuman
|
|
554
|
+
}),
|
|
555
|
+
evm: { type: "euler_v2_vault_withdraw", version: 1, chainId: String(args.chainId) },
|
|
556
|
+
eulerV2: {
|
|
557
|
+
vaultMarket: vaultLabel,
|
|
558
|
+
amountHuman: args.amountHuman,
|
|
559
|
+
evault,
|
|
560
|
+
owner: shareOwner,
|
|
561
|
+
gasBuildWithdraw: { baseGasUnits: ctx.gasLimit.toString() }
|
|
562
|
+
}
|
|
563
|
+
})
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/protocols/evm/euler-v2/borrowCollateralMaxWithdrawWei.ts
|
|
568
|
+
var erc4626WithdrawAbi2 = viem.parseAbi([
|
|
569
|
+
"function withdraw(uint256 assets, address receiver, address owner) returns (uint256 shares)"
|
|
570
|
+
]);
|
|
571
|
+
var evcBatchAbi = viem.parseAbi([
|
|
572
|
+
"function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
|
|
573
|
+
]);
|
|
574
|
+
var eulerCollateralVaultReadAbi = viem.parseAbi([
|
|
575
|
+
"function balanceOf(address account) view returns (uint256)",
|
|
576
|
+
"function convertToAssets(uint256 shares) view returns (uint256)",
|
|
577
|
+
"function cash() view returns (uint256)"
|
|
578
|
+
]);
|
|
579
|
+
async function evcWithdrawSimulatesOk(args) {
|
|
580
|
+
const withdrawData = viem.encodeFunctionData({
|
|
581
|
+
abi: erc4626WithdrawAbi2,
|
|
582
|
+
functionName: "withdraw",
|
|
583
|
+
args: [args.assetsWei, args.caller, args.subAccount]
|
|
584
|
+
});
|
|
585
|
+
const batchData = viem.encodeFunctionData({
|
|
586
|
+
abi: evcBatchAbi,
|
|
587
|
+
functionName: "batch",
|
|
588
|
+
args: [
|
|
589
|
+
[
|
|
590
|
+
{
|
|
591
|
+
targetContract: args.collateralVault,
|
|
592
|
+
onBehalfOfAccount: args.subAccount,
|
|
593
|
+
value: 0n,
|
|
594
|
+
data: withdrawData
|
|
595
|
+
}
|
|
596
|
+
]
|
|
597
|
+
]
|
|
598
|
+
});
|
|
599
|
+
try {
|
|
600
|
+
await args.publicClient.call({
|
|
601
|
+
account: args.caller,
|
|
602
|
+
to: args.evc,
|
|
603
|
+
data: batchData,
|
|
604
|
+
gas: 8000000n
|
|
605
|
+
});
|
|
606
|
+
return true;
|
|
607
|
+
} catch {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async function collateralMaxWithdrawBySimulation(args) {
|
|
612
|
+
const ch = viem.defineChain({
|
|
613
|
+
id: args.chainId,
|
|
614
|
+
name: "EulerColMaxW",
|
|
615
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
616
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
617
|
+
});
|
|
618
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
619
|
+
let shareBal;
|
|
620
|
+
let assetsOwned;
|
|
621
|
+
let vaultCash;
|
|
622
|
+
try {
|
|
623
|
+
;
|
|
624
|
+
[shareBal, vaultCash] = await Promise.all([
|
|
625
|
+
publicClient.readContract({
|
|
626
|
+
address: args.collateralVault,
|
|
627
|
+
abi: eulerCollateralVaultReadAbi,
|
|
628
|
+
functionName: "balanceOf",
|
|
629
|
+
args: [args.subAccount]
|
|
630
|
+
}),
|
|
631
|
+
publicClient.readContract({
|
|
632
|
+
address: args.collateralVault,
|
|
633
|
+
abi: eulerCollateralVaultReadAbi,
|
|
634
|
+
functionName: "cash"
|
|
635
|
+
})
|
|
636
|
+
]);
|
|
637
|
+
} catch {
|
|
638
|
+
return 0n;
|
|
639
|
+
}
|
|
640
|
+
if (shareBal === 0n) return 0n;
|
|
641
|
+
try {
|
|
642
|
+
assetsOwned = await publicClient.readContract({
|
|
643
|
+
address: args.collateralVault,
|
|
644
|
+
abi: eulerCollateralVaultReadAbi,
|
|
645
|
+
functionName: "convertToAssets",
|
|
646
|
+
args: [shareBal]
|
|
647
|
+
});
|
|
648
|
+
} catch {
|
|
649
|
+
return 0n;
|
|
650
|
+
}
|
|
651
|
+
const hi = vaultCash <= 0n ? 0n : vaultCash <= assetsOwned ? vaultCash : assetsOwned;
|
|
652
|
+
if (hi === 0n) return 0n;
|
|
653
|
+
let lo = 0n;
|
|
654
|
+
let hiProbe = hi;
|
|
655
|
+
let best = 0n;
|
|
656
|
+
while (lo <= hiProbe) {
|
|
657
|
+
const mid = lo + (hiProbe - lo) / 2n;
|
|
658
|
+
const ok = await evcWithdrawSimulatesOk({
|
|
659
|
+
publicClient,
|
|
660
|
+
evc: args.evc,
|
|
661
|
+
collateralVault: args.collateralVault,
|
|
662
|
+
subAccount: args.subAccount,
|
|
663
|
+
caller: args.caller,
|
|
664
|
+
assetsWei: mid
|
|
665
|
+
});
|
|
666
|
+
if (ok) {
|
|
667
|
+
best = mid;
|
|
668
|
+
lo = mid + 1n;
|
|
669
|
+
} else {
|
|
670
|
+
hiProbe = mid - 1n;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return best;
|
|
674
|
+
}
|
|
675
|
+
async function fetchEulerBorrowCollateralMaxWithdrawAssetsWei(args) {
|
|
676
|
+
const rpcUrl = args.rpcUrl.trim();
|
|
677
|
+
if (!rpcUrl) return 0n;
|
|
678
|
+
const evc = viem.getAddress(args.evc);
|
|
679
|
+
const collateralVault = viem.getAddress(args.collateralVault);
|
|
680
|
+
const subAccount = viem.getAddress(args.subAccount);
|
|
681
|
+
const caller = viem.getAddress(args.caller);
|
|
682
|
+
const stdMax = await fetchEulerVaultMaxWithdrawWei({
|
|
683
|
+
rpcUrl,
|
|
684
|
+
chainId: args.chainId,
|
|
685
|
+
evault: collateralVault,
|
|
686
|
+
owner: subAccount
|
|
687
|
+
});
|
|
688
|
+
if (stdMax > 0n) return stdMax;
|
|
689
|
+
return collateralMaxWithdrawBySimulation({
|
|
690
|
+
rpcUrl,
|
|
691
|
+
chainId: args.chainId,
|
|
692
|
+
evc,
|
|
693
|
+
collateralVault,
|
|
694
|
+
subAccount,
|
|
695
|
+
caller
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
var EULER_V2_ISOLATED_VAULT_DEPOSIT_FALLBACK_GAS = 950000n;
|
|
699
|
+
var EULER_VAULT_DEPOSIT_ESTIMATE_FALLBACK = EULER_V2_ISOLATED_VAULT_DEPOSIT_FALLBACK_GAS;
|
|
700
|
+
var EULER_ERC20_APPROVE_FALLBACK = 100000n;
|
|
701
|
+
var EULER_WETH_DEPOSIT_FALLBACK = 120000n;
|
|
702
|
+
function gweiToDecimalString2(n) {
|
|
703
|
+
if (!Number.isFinite(n)) return "0";
|
|
704
|
+
if (n === 0) return "0";
|
|
705
|
+
const s = String(n);
|
|
706
|
+
if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
|
|
707
|
+
return s;
|
|
708
|
+
}
|
|
709
|
+
function txToViemStep(tx) {
|
|
710
|
+
return { to: viem.getAddress(tx.to), data: tx.data, value: tx.value };
|
|
711
|
+
}
|
|
712
|
+
var wethDepositAbi = viem.parseAbi(["function deposit() payable"]);
|
|
713
|
+
var erc20AllowanceAbi = viem.parseAbi([
|
|
714
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
715
|
+
"function decimals() view returns (uint8)"
|
|
716
|
+
]);
|
|
717
|
+
var erc20ApproveAbi = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
718
|
+
var erc4626DepositAbi = viem.parseAbi([
|
|
719
|
+
"function deposit(uint256 assets, address receiver) returns (uint256 shares)"
|
|
720
|
+
]);
|
|
721
|
+
async function buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch(args) {
|
|
722
|
+
const ph = (args.keyGen.pubkeyhex ?? "").trim();
|
|
723
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
724
|
+
const keyList = args.keyGen.keylist ?? [];
|
|
725
|
+
const clientId = firstClientIdFromKeyGen(args.keyGen);
|
|
726
|
+
const asset = viem.getAddress(args.underlying);
|
|
727
|
+
const evault = viem.getAddress(args.evault);
|
|
728
|
+
const weth = viem.getAddress(args.nativeWrapped);
|
|
729
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
730
|
+
const receiver = viem.getAddress(args.receiver);
|
|
731
|
+
if (args.isNativeIn && asset.toLowerCase() !== weth.toLowerCase()) {
|
|
732
|
+
throw new Error("Native lend path: underlying asset must match the chain wrapped native token.");
|
|
733
|
+
}
|
|
734
|
+
const ch = viem.defineChain({
|
|
735
|
+
id: args.chainId,
|
|
736
|
+
name: "Destination",
|
|
737
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
738
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
739
|
+
});
|
|
740
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
741
|
+
const dec = await publicClient.readContract({
|
|
742
|
+
address: asset,
|
|
743
|
+
abi: erc20AllowanceAbi,
|
|
744
|
+
functionName: "decimals"
|
|
745
|
+
});
|
|
746
|
+
const amountWei = viem.parseUnits(args.amountHuman, Number(dec));
|
|
747
|
+
if (amountWei === 0n) throw new Error("Amount is zero after converting with token decimals.");
|
|
748
|
+
const steps = [];
|
|
749
|
+
if (args.isNativeIn) {
|
|
750
|
+
const dataDeposit = viem.encodeFunctionData({ abi: wethDepositAbi, functionName: "deposit", args: [] });
|
|
751
|
+
steps.push({ kind: "weth_deposit", to: weth, data: dataDeposit, value: amountWei });
|
|
752
|
+
const wethAllowance = await publicClient.readContract({
|
|
753
|
+
address: weth,
|
|
754
|
+
abi: erc20AllowanceAbi,
|
|
755
|
+
functionName: "allowance",
|
|
756
|
+
args: [executor, evault]
|
|
757
|
+
});
|
|
758
|
+
if (wethAllowance < amountWei) {
|
|
759
|
+
if (wethAllowance > 0n) {
|
|
760
|
+
const dataReset = viem.encodeFunctionData({
|
|
761
|
+
abi: erc20ApproveAbi,
|
|
762
|
+
functionName: "approve",
|
|
763
|
+
args: [evault, 0n]
|
|
764
|
+
});
|
|
765
|
+
steps.push({ kind: "approve", to: weth, data: dataReset, value: 0n });
|
|
766
|
+
}
|
|
767
|
+
const dataApprove = viem.encodeFunctionData({
|
|
768
|
+
abi: erc20ApproveAbi,
|
|
769
|
+
functionName: "approve",
|
|
770
|
+
args: [evault, amountWei]
|
|
771
|
+
});
|
|
772
|
+
steps.push({ kind: "approve", to: weth, data: dataApprove, value: 0n });
|
|
773
|
+
}
|
|
774
|
+
} else {
|
|
775
|
+
const currentAllowance = await publicClient.readContract({
|
|
776
|
+
address: asset,
|
|
777
|
+
abi: erc20AllowanceAbi,
|
|
778
|
+
functionName: "allowance",
|
|
779
|
+
args: [executor, evault]
|
|
780
|
+
});
|
|
781
|
+
if (currentAllowance < amountWei) {
|
|
782
|
+
if (currentAllowance > 0n) {
|
|
783
|
+
const dataReset = viem.encodeFunctionData({
|
|
784
|
+
abi: erc20ApproveAbi,
|
|
785
|
+
functionName: "approve",
|
|
786
|
+
args: [evault, 0n]
|
|
787
|
+
});
|
|
788
|
+
steps.push({ kind: "approve", to: asset, data: dataReset, value: 0n });
|
|
789
|
+
}
|
|
790
|
+
const dataApprove = viem.encodeFunctionData({
|
|
791
|
+
abi: erc20ApproveAbi,
|
|
792
|
+
functionName: "approve",
|
|
793
|
+
args: [evault, amountWei]
|
|
794
|
+
});
|
|
795
|
+
steps.push({ kind: "approve", to: asset, data: dataApprove, value: 0n });
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
const depositData = viem.encodeFunctionData({
|
|
799
|
+
abi: erc4626DepositAbi,
|
|
800
|
+
functionName: "deposit",
|
|
801
|
+
args: [amountWei, receiver]
|
|
802
|
+
});
|
|
803
|
+
steps.push({ kind: "vault_deposit", to: evault, data: depositData, value: 0n });
|
|
804
|
+
const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
|
|
805
|
+
const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
|
|
806
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
807
|
+
const useCustomGas = args.useCustomGas;
|
|
808
|
+
const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
|
|
809
|
+
const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
|
|
810
|
+
const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
811
|
+
const messageHashes = [];
|
|
812
|
+
const messageRawBatch = [];
|
|
813
|
+
const proposalTxParamsBatch = [];
|
|
814
|
+
const batchMeta = [];
|
|
815
|
+
let firstTxFeePayload = {};
|
|
816
|
+
let firstDataNo0x = "";
|
|
817
|
+
const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
|
|
818
|
+
for (let i = 0; i < steps.length; i++) {
|
|
819
|
+
const s = steps[i];
|
|
820
|
+
const v = txToViemStep(s);
|
|
821
|
+
const currentNonce = baseNonce + i;
|
|
822
|
+
let estimatedGas;
|
|
823
|
+
try {
|
|
824
|
+
estimatedGas = await publicClient.estimateGas({
|
|
825
|
+
to: v.to,
|
|
826
|
+
data: v.data,
|
|
827
|
+
value: v.value,
|
|
828
|
+
account: executor
|
|
829
|
+
});
|
|
830
|
+
} catch {
|
|
831
|
+
if (s.kind === "weth_deposit") estimatedGas = EULER_WETH_DEPOSIT_FALLBACK;
|
|
832
|
+
else if (s.kind === "approve") estimatedGas = EULER_ERC20_APPROVE_FALLBACK;
|
|
833
|
+
else estimatedGas = EULER_VAULT_DEPOSIT_ESTIMATE_FALLBACK;
|
|
834
|
+
}
|
|
835
|
+
const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
836
|
+
if (legacy) {
|
|
837
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
838
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
839
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
840
|
+
}
|
|
841
|
+
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
842
|
+
const configured = viem.parseGwei(gweiToDecimalString2(Number(args.chainDetail.gasPrice)));
|
|
843
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
844
|
+
}
|
|
845
|
+
const ser = viem.serializeTransaction({
|
|
846
|
+
type: "legacy",
|
|
847
|
+
to: v.to,
|
|
848
|
+
data: v.data,
|
|
849
|
+
value: v.value,
|
|
850
|
+
gas: gasLimitI,
|
|
851
|
+
gasPrice: gasPriceWei,
|
|
852
|
+
nonce: currentNonce,
|
|
853
|
+
chainId: args.chainId
|
|
854
|
+
});
|
|
855
|
+
const h = viem.keccak256(ser);
|
|
856
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
857
|
+
messageRawBatch.push(ser);
|
|
858
|
+
proposalTxParamsBatch.push({
|
|
859
|
+
nonce: currentNonce,
|
|
860
|
+
gasLimit: gasLimitI.toString(),
|
|
861
|
+
txType: "legacy",
|
|
862
|
+
gasPrice: gasPriceWei.toString()
|
|
863
|
+
});
|
|
864
|
+
if (i === 0) {
|
|
865
|
+
firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
|
|
866
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
867
|
+
}
|
|
868
|
+
} else {
|
|
869
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
870
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
871
|
+
const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
872
|
+
const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
873
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
874
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
875
|
+
const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
876
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
877
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
878
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString2(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
879
|
+
let maxFeePerGas = viem.parseGwei(gweiToDecimalString2(maxFeePerGasGwei));
|
|
880
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
881
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
882
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
883
|
+
}
|
|
884
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
|
|
885
|
+
maxFeePerGas,
|
|
886
|
+
maxPriorityFeePerGas,
|
|
887
|
+
latestBaseFeeWei
|
|
888
|
+
));
|
|
889
|
+
const ser = viem.serializeTransaction({
|
|
890
|
+
type: "eip1559",
|
|
891
|
+
to: v.to,
|
|
892
|
+
data: v.data,
|
|
893
|
+
value: v.value,
|
|
894
|
+
gas: gasLimitI,
|
|
895
|
+
maxFeePerGas,
|
|
896
|
+
maxPriorityFeePerGas,
|
|
897
|
+
nonce: currentNonce,
|
|
898
|
+
chainId: args.chainId
|
|
899
|
+
});
|
|
900
|
+
const h = viem.keccak256(ser);
|
|
901
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
902
|
+
messageRawBatch.push(ser);
|
|
903
|
+
proposalTxParamsBatch.push({
|
|
904
|
+
nonce: currentNonce,
|
|
905
|
+
gasLimit: gasLimitI.toString(),
|
|
906
|
+
txType: "eip1559",
|
|
907
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
908
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
909
|
+
});
|
|
910
|
+
if (i === 0) {
|
|
911
|
+
firstTxFeePayload = {
|
|
912
|
+
txNonce: currentNonce,
|
|
913
|
+
txGasLimit: gasLimitI.toString(),
|
|
914
|
+
txMaxFeePerGas: maxFeePerGas.toString(),
|
|
915
|
+
txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
916
|
+
};
|
|
917
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (s.kind === "weth_deposit") {
|
|
921
|
+
batchMeta.push({
|
|
922
|
+
destinationAddress: weth,
|
|
923
|
+
signatureText: JSON.stringify({
|
|
924
|
+
kind: "EulerV2",
|
|
925
|
+
name: "WETH.deposit",
|
|
926
|
+
function: "deposit()",
|
|
927
|
+
valueWei: amountWei.toString(),
|
|
928
|
+
vaultMarket: vaultLabel,
|
|
929
|
+
note: "Wrap native for Euler v2 isolated vault deposit (same batch)."
|
|
930
|
+
}),
|
|
931
|
+
evm: { type: "euler_v2_weth_deposit", version: 1, chainId: String(args.chainId) },
|
|
932
|
+
eulerV2: { step: "weth_deposit", vaultMarket: vaultLabel, amountHuman: args.amountHuman, evault, underlying: asset }
|
|
933
|
+
});
|
|
934
|
+
} else if (s.kind === "approve") {
|
|
935
|
+
batchMeta.push({
|
|
936
|
+
destinationAddress: s.to,
|
|
937
|
+
signatureText: JSON.stringify({
|
|
938
|
+
kind: "EulerV2",
|
|
939
|
+
name: "ERC20.approve",
|
|
940
|
+
to: "Euler eVault",
|
|
941
|
+
function: "approve(address spender, uint256 amount)",
|
|
942
|
+
evault,
|
|
943
|
+
amountHuman: args.amountHuman,
|
|
944
|
+
note: "Allowance for this deposit amount only (not unlimited)."
|
|
945
|
+
}),
|
|
946
|
+
evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
|
|
947
|
+
eulerV2: { vaultMarket: vaultLabel, amountHuman: args.amountHuman, evault, underlying: asset }
|
|
948
|
+
});
|
|
949
|
+
} else {
|
|
950
|
+
batchMeta.push({
|
|
951
|
+
destinationAddress: evault,
|
|
952
|
+
signatureText: JSON.stringify({
|
|
953
|
+
kind: "EulerV2",
|
|
954
|
+
name: "EVault.deposit",
|
|
955
|
+
function: "deposit(uint256 assets, address receiver)",
|
|
956
|
+
evault,
|
|
957
|
+
underlying: asset,
|
|
958
|
+
receiver,
|
|
959
|
+
vaultMarket: vaultLabel,
|
|
960
|
+
amountHuman: args.amountHuman
|
|
961
|
+
}),
|
|
962
|
+
evm: { type: "euler_v2_vault_deposit", version: 1, chainId: String(args.chainId) },
|
|
963
|
+
eulerV2: {
|
|
964
|
+
vaultMarket: vaultLabel,
|
|
965
|
+
amountHuman: args.amountHuman,
|
|
966
|
+
evault,
|
|
967
|
+
underlying: asset,
|
|
968
|
+
receiver,
|
|
969
|
+
gasBuildDeposit: { baseGasUnits: gasLimitI.toString() }
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
const extraPayload = { batchMeta };
|
|
975
|
+
if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
|
|
976
|
+
extraPayload.customGasChainDetails = args.customGasChainDetails;
|
|
977
|
+
}
|
|
978
|
+
const extraJSON = JSON.stringify(extraPayload);
|
|
979
|
+
const firstSigText = batchMeta[0].signatureText;
|
|
980
|
+
const n = steps.length;
|
|
981
|
+
const hasWrap = args.isNativeIn;
|
|
982
|
+
const purposeSuffix = (() => {
|
|
983
|
+
if (hasWrap) {
|
|
984
|
+
return `Euler v2: ${n}-tx batch \u2014 wrap native to WETH (if needed), approve eVault for the exact amount, then ERC-4626 deposit into isolated vault "${vaultLabel}".`;
|
|
985
|
+
}
|
|
986
|
+
if (n === 1) {
|
|
987
|
+
return `Euler v2: 1-tx \u2014 eVault.deposit (allowance already sufficient) into "${vaultLabel}".`;
|
|
988
|
+
}
|
|
989
|
+
return `Euler v2: ${n}-tx batch \u2014 approve eVault for the exact amount, then ERC-4626 deposit into "${vaultLabel}".`;
|
|
990
|
+
})();
|
|
991
|
+
const firstValue = steps[0].value;
|
|
992
|
+
const bodyForSign = {
|
|
993
|
+
keyList,
|
|
994
|
+
pubKey: ph,
|
|
995
|
+
msgHash: messageHashes[0],
|
|
996
|
+
msgRaw: firstDataNo0x,
|
|
997
|
+
messageHashes,
|
|
998
|
+
messageRawBatch,
|
|
999
|
+
destinationChainID: String(args.chainId),
|
|
1000
|
+
destinationAddress: steps[0].to,
|
|
1001
|
+
extraJSON,
|
|
1002
|
+
signatureText: firstSigText,
|
|
1003
|
+
purpose: (() => {
|
|
1004
|
+
const t = args.purposeText.trim();
|
|
1005
|
+
return (t ? `${t}
|
|
1006
|
+
|
|
1007
|
+
` : "") + purposeSuffix;
|
|
1008
|
+
})(),
|
|
1009
|
+
...firstTxFeePayload,
|
|
1010
|
+
proposalTxParams: proposalTxParamsBatch
|
|
1011
|
+
};
|
|
1012
|
+
if (firstValue > 0n) {
|
|
1013
|
+
bodyForSign.value = firstValue.toString();
|
|
1014
|
+
}
|
|
1015
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
1016
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
1017
|
+
}
|
|
1018
|
+
var EULER_BORROW_BATCH_FALLBACK_GAS = 2500000n;
|
|
1019
|
+
var EULER_BORROW_BATCH_FALLBACK_GAS_PER_ROUND = 350000n;
|
|
1020
|
+
var EULER_ERC20_APPROVE_FALLBACK2 = 100000n;
|
|
1021
|
+
var EULER_WETH_DEPOSIT_FALLBACK2 = 120000n;
|
|
1022
|
+
var wethDepositAbi2 = viem.parseAbi(["function deposit() payable"]);
|
|
1023
|
+
var erc20AllowanceAbi2 = viem.parseAbi([
|
|
1024
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
1025
|
+
"function decimals() view returns (uint8)"
|
|
1026
|
+
]);
|
|
1027
|
+
var erc20ApproveAbi2 = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
1028
|
+
var erc4626DepositAbi2 = viem.parseAbi([
|
|
1029
|
+
"function deposit(uint256 assets, address receiver) returns (uint256 shares)"
|
|
1030
|
+
]);
|
|
1031
|
+
var evaultBorrowAbi = viem.parseAbi(["function borrow(uint256 amount, address receiver) returns (uint256)"]);
|
|
1032
|
+
var evcAbi = viem.parseAbi([
|
|
1033
|
+
"function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
|
|
1034
|
+
]);
|
|
1035
|
+
function gweiToDecimalString3(n) {
|
|
1036
|
+
if (!Number.isFinite(n)) return "0";
|
|
1037
|
+
if (n === 0) return "0";
|
|
1038
|
+
const s = String(n);
|
|
1039
|
+
if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
|
|
1040
|
+
return s;
|
|
1041
|
+
}
|
|
1042
|
+
function txToViemStep2(tx) {
|
|
1043
|
+
return { to: viem.getAddress(tx.to), data: tx.data, value: tx.value };
|
|
1044
|
+
}
|
|
1045
|
+
async function buildEvmMultisignBodyEulerV2IsolatedBorrowBatch(args) {
|
|
1046
|
+
const ph = (args.keyGen.pubkeyhex ?? "").trim();
|
|
1047
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
1048
|
+
const keyList = args.keyGen.keylist ?? [];
|
|
1049
|
+
const clientId = firstClientIdFromKeyGen(args.keyGen);
|
|
1050
|
+
const evc = viem.getAddress(args.evc);
|
|
1051
|
+
const borrowVault = viem.getAddress(args.borrowVault);
|
|
1052
|
+
const collateralVault = viem.getAddress(args.collateralVault);
|
|
1053
|
+
const collateralAsset = viem.getAddress(args.collateralUnderlying);
|
|
1054
|
+
const borrowAsset = viem.getAddress(args.borrowUnderlying);
|
|
1055
|
+
const weth = viem.getAddress(args.nativeWrapped);
|
|
1056
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
1057
|
+
const receiver = viem.getAddress(args.receiver);
|
|
1058
|
+
if (args.isNativeCollateralIn && collateralAsset.toLowerCase() !== weth.toLowerCase()) {
|
|
1059
|
+
throw new Error("Native collateral path: collateral underlying must be the chain wrapped native token.");
|
|
1060
|
+
}
|
|
1061
|
+
const ch = viem.defineChain({
|
|
1062
|
+
id: args.chainId,
|
|
1063
|
+
name: "Destination",
|
|
1064
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
1065
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
1066
|
+
});
|
|
1067
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
1068
|
+
const cDecRaw = await publicClient.readContract({
|
|
1069
|
+
address: collateralAsset,
|
|
1070
|
+
abi: erc20AllowanceAbi2,
|
|
1071
|
+
functionName: "decimals"
|
|
1072
|
+
});
|
|
1073
|
+
const collateralDecimals = Number(cDecRaw);
|
|
1074
|
+
const collateralWei = viem.parseUnits(args.collateralAmountHuman, collateralDecimals);
|
|
1075
|
+
if (collateralWei === 0n) throw new Error("Collateral amount is zero after converting with token decimals.");
|
|
1076
|
+
await publicClient.readContract({
|
|
1077
|
+
address: borrowAsset,
|
|
1078
|
+
abi: erc20AllowanceAbi2,
|
|
1079
|
+
functionName: "decimals"
|
|
1080
|
+
});
|
|
1081
|
+
const loops = [...args.loopBorrowWeis];
|
|
1082
|
+
if (loops.length === 0) throw new Error("At least one borrow round is required.");
|
|
1083
|
+
for (const w of loops) {
|
|
1084
|
+
if (w <= 0n) throw new Error("Each borrow round must be positive.");
|
|
1085
|
+
}
|
|
1086
|
+
if (!args.redepositBorrowedToCollateral && loops.length !== 1) {
|
|
1087
|
+
throw new Error("Multiple borrow rounds require redepositBorrowedToCollateral (same borrow and collateral asset).");
|
|
1088
|
+
}
|
|
1089
|
+
const borrowWeiTotal = loops.reduce((a, w) => a + w, 0n);
|
|
1090
|
+
const totalPullWei = eulerSameAssetTotalCollateralPullWei({
|
|
1091
|
+
initialCollateralWei: collateralWei,
|
|
1092
|
+
loopBorrowWeis: args.redepositBorrowedToCollateral ? loops : []
|
|
1093
|
+
});
|
|
1094
|
+
const approveTargetWei = eulerSameAssetApproveAmountWithBuffer({ totalPullWei });
|
|
1095
|
+
const steps = [];
|
|
1096
|
+
if (args.isNativeCollateralIn) {
|
|
1097
|
+
const dataDeposit = viem.encodeFunctionData({ abi: wethDepositAbi2, functionName: "deposit", args: [] });
|
|
1098
|
+
steps.push({ kind: "weth_deposit", to: weth, data: dataDeposit, value: collateralWei });
|
|
1099
|
+
const wethAllowance = await publicClient.readContract({
|
|
1100
|
+
address: weth,
|
|
1101
|
+
abi: erc20AllowanceAbi2,
|
|
1102
|
+
functionName: "allowance",
|
|
1103
|
+
args: [executor, collateralVault]
|
|
1104
|
+
});
|
|
1105
|
+
if (wethAllowance < approveTargetWei) {
|
|
1106
|
+
if (wethAllowance > 0n) {
|
|
1107
|
+
const dataReset = viem.encodeFunctionData({
|
|
1108
|
+
abi: erc20ApproveAbi2,
|
|
1109
|
+
functionName: "approve",
|
|
1110
|
+
args: [collateralVault, 0n]
|
|
1111
|
+
});
|
|
1112
|
+
steps.push({ kind: "approve", to: weth, data: dataReset, value: 0n });
|
|
1113
|
+
}
|
|
1114
|
+
const dataApprove = viem.encodeFunctionData({
|
|
1115
|
+
abi: erc20ApproveAbi2,
|
|
1116
|
+
functionName: "approve",
|
|
1117
|
+
args: [collateralVault, approveTargetWei]
|
|
1118
|
+
});
|
|
1119
|
+
steps.push({ kind: "approve", to: weth, data: dataApprove, value: 0n });
|
|
1120
|
+
}
|
|
1121
|
+
} else {
|
|
1122
|
+
const currentAllowance = await publicClient.readContract({
|
|
1123
|
+
address: collateralAsset,
|
|
1124
|
+
abi: erc20AllowanceAbi2,
|
|
1125
|
+
functionName: "allowance",
|
|
1126
|
+
args: [executor, collateralVault]
|
|
1127
|
+
});
|
|
1128
|
+
if (currentAllowance < approveTargetWei) {
|
|
1129
|
+
if (currentAllowance > 0n) {
|
|
1130
|
+
const dataReset = viem.encodeFunctionData({
|
|
1131
|
+
abi: erc20ApproveAbi2,
|
|
1132
|
+
functionName: "approve",
|
|
1133
|
+
args: [collateralVault, 0n]
|
|
1134
|
+
});
|
|
1135
|
+
steps.push({ kind: "approve", to: collateralAsset, data: dataReset, value: 0n });
|
|
1136
|
+
}
|
|
1137
|
+
const dataApprove = viem.encodeFunctionData({
|
|
1138
|
+
abi: erc20ApproveAbi2,
|
|
1139
|
+
functionName: "approve",
|
|
1140
|
+
args: [collateralVault, approveTargetWei]
|
|
1141
|
+
});
|
|
1142
|
+
steps.push({ kind: "approve", to: collateralAsset, data: dataApprove, value: 0n });
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
const depositData = viem.encodeFunctionData({
|
|
1146
|
+
abi: erc4626DepositAbi2,
|
|
1147
|
+
functionName: "deposit",
|
|
1148
|
+
args: [collateralWei, receiver]
|
|
1149
|
+
});
|
|
1150
|
+
const enableCollData = viem.encodeFunctionData({
|
|
1151
|
+
abi: viem.parseAbi(["function enableCollateral(address account, address vault)"]),
|
|
1152
|
+
functionName: "enableCollateral",
|
|
1153
|
+
args: [receiver, collateralVault]
|
|
1154
|
+
});
|
|
1155
|
+
const enableCtrlData = viem.encodeFunctionData({
|
|
1156
|
+
abi: viem.parseAbi(["function enableController(address account, address vault)"]),
|
|
1157
|
+
functionName: "enableController",
|
|
1158
|
+
args: [receiver, borrowVault]
|
|
1159
|
+
});
|
|
1160
|
+
const batchItems = [
|
|
1161
|
+
{ targetContract: collateralVault, onBehalfOfAccount: receiver, value: 0n, data: depositData },
|
|
1162
|
+
{ targetContract: evc, onBehalfOfAccount: viem.zeroAddress, value: 0n, data: enableCollData },
|
|
1163
|
+
{ targetContract: evc, onBehalfOfAccount: viem.zeroAddress, value: 0n, data: enableCtrlData }
|
|
1164
|
+
];
|
|
1165
|
+
for (const bw of loops) {
|
|
1166
|
+
const borrowData = viem.encodeFunctionData({
|
|
1167
|
+
abi: evaultBorrowAbi,
|
|
1168
|
+
functionName: "borrow",
|
|
1169
|
+
args: [bw, receiver]
|
|
1170
|
+
});
|
|
1171
|
+
batchItems.push({
|
|
1172
|
+
targetContract: borrowVault,
|
|
1173
|
+
onBehalfOfAccount: receiver,
|
|
1174
|
+
value: 0n,
|
|
1175
|
+
data: borrowData
|
|
1176
|
+
});
|
|
1177
|
+
if (args.redepositBorrowedToCollateral) {
|
|
1178
|
+
const redepositData = viem.encodeFunctionData({
|
|
1179
|
+
abi: erc4626DepositAbi2,
|
|
1180
|
+
functionName: "deposit",
|
|
1181
|
+
args: [bw, receiver]
|
|
1182
|
+
});
|
|
1183
|
+
batchItems.push({
|
|
1184
|
+
targetContract: collateralVault,
|
|
1185
|
+
onBehalfOfAccount: receiver,
|
|
1186
|
+
value: 0n,
|
|
1187
|
+
data: redepositData
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
const batchData = viem.encodeFunctionData({
|
|
1192
|
+
abi: evcAbi,
|
|
1193
|
+
functionName: "batch",
|
|
1194
|
+
args: [batchItems]
|
|
1195
|
+
});
|
|
1196
|
+
steps.push({ kind: "evc_batch", to: evc, data: batchData, value: 0n });
|
|
1197
|
+
const borrowRoundsExtraGas = EULER_BORROW_BATCH_FALLBACK_GAS_PER_ROUND * BigInt(Math.max(0, loops.length - 1 + (args.redepositBorrowedToCollateral ? loops.length : 0)));
|
|
1198
|
+
const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
|
|
1199
|
+
const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
|
|
1200
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
1201
|
+
const useCustomGas = args.useCustomGas;
|
|
1202
|
+
const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
|
|
1203
|
+
const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
|
|
1204
|
+
const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
1205
|
+
const messageHashes = [];
|
|
1206
|
+
const messageRawBatch = [];
|
|
1207
|
+
const proposalTxParamsBatch = [];
|
|
1208
|
+
const batchMeta = [];
|
|
1209
|
+
let firstTxFeePayload = {};
|
|
1210
|
+
let firstDataNo0x = "";
|
|
1211
|
+
const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
|
|
1212
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1213
|
+
const s = steps[i];
|
|
1214
|
+
const v = txToViemStep2(s);
|
|
1215
|
+
const currentNonce = baseNonce + i;
|
|
1216
|
+
let estimatedGas;
|
|
1217
|
+
try {
|
|
1218
|
+
estimatedGas = await publicClient.estimateGas({
|
|
1219
|
+
to: v.to,
|
|
1220
|
+
data: v.data,
|
|
1221
|
+
value: v.value,
|
|
1222
|
+
account: executor
|
|
1223
|
+
});
|
|
1224
|
+
} catch {
|
|
1225
|
+
if (s.kind === "weth_deposit") estimatedGas = EULER_WETH_DEPOSIT_FALLBACK2;
|
|
1226
|
+
else if (s.kind === "approve") estimatedGas = EULER_ERC20_APPROVE_FALLBACK2;
|
|
1227
|
+
else estimatedGas = EULER_BORROW_BATCH_FALLBACK_GAS + borrowRoundsExtraGas;
|
|
1228
|
+
}
|
|
1229
|
+
const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
1230
|
+
if (legacy) {
|
|
1231
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
1232
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1233
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1234
|
+
}
|
|
1235
|
+
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
1236
|
+
const configured = viem.parseGwei(gweiToDecimalString3(Number(args.chainDetail.gasPrice)));
|
|
1237
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
1238
|
+
}
|
|
1239
|
+
const ser = viem.serializeTransaction({
|
|
1240
|
+
type: "legacy",
|
|
1241
|
+
to: v.to,
|
|
1242
|
+
data: v.data,
|
|
1243
|
+
value: v.value,
|
|
1244
|
+
gas: gasLimitI,
|
|
1245
|
+
gasPrice: gasPriceWei,
|
|
1246
|
+
nonce: currentNonce,
|
|
1247
|
+
chainId: args.chainId
|
|
1248
|
+
});
|
|
1249
|
+
const h = viem.keccak256(ser);
|
|
1250
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
1251
|
+
messageRawBatch.push(ser);
|
|
1252
|
+
proposalTxParamsBatch.push({
|
|
1253
|
+
nonce: currentNonce,
|
|
1254
|
+
gasLimit: gasLimitI.toString(),
|
|
1255
|
+
txType: "legacy",
|
|
1256
|
+
gasPrice: gasPriceWei.toString()
|
|
1257
|
+
});
|
|
1258
|
+
if (i === 0) {
|
|
1259
|
+
firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
|
|
1260
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
1261
|
+
}
|
|
1262
|
+
} else {
|
|
1263
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
1264
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
1265
|
+
const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
1266
|
+
const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
1267
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
1268
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
1269
|
+
const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
1270
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
1271
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
1272
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString3(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
1273
|
+
let maxFeePerGas = viem.parseGwei(gweiToDecimalString3(maxFeePerGasGwei));
|
|
1274
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1275
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1276
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1277
|
+
}
|
|
1278
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
|
|
1279
|
+
maxFeePerGas,
|
|
1280
|
+
maxPriorityFeePerGas,
|
|
1281
|
+
latestBaseFeeWei
|
|
1282
|
+
));
|
|
1283
|
+
const ser = viem.serializeTransaction({
|
|
1284
|
+
type: "eip1559",
|
|
1285
|
+
to: v.to,
|
|
1286
|
+
data: v.data,
|
|
1287
|
+
value: v.value,
|
|
1288
|
+
gas: gasLimitI,
|
|
1289
|
+
maxFeePerGas,
|
|
1290
|
+
maxPriorityFeePerGas,
|
|
1291
|
+
nonce: currentNonce,
|
|
1292
|
+
chainId: args.chainId
|
|
1293
|
+
});
|
|
1294
|
+
const h = viem.keccak256(ser);
|
|
1295
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
1296
|
+
messageRawBatch.push(ser);
|
|
1297
|
+
proposalTxParamsBatch.push({
|
|
1298
|
+
nonce: currentNonce,
|
|
1299
|
+
gasLimit: gasLimitI.toString(),
|
|
1300
|
+
txType: "eip1559",
|
|
1301
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
1302
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1303
|
+
});
|
|
1304
|
+
if (i === 0) {
|
|
1305
|
+
firstTxFeePayload = {
|
|
1306
|
+
txNonce: currentNonce,
|
|
1307
|
+
txGasLimit: gasLimitI.toString(),
|
|
1308
|
+
txMaxFeePerGas: maxFeePerGas.toString(),
|
|
1309
|
+
txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1310
|
+
};
|
|
1311
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
if (s.kind === "weth_deposit") {
|
|
1315
|
+
batchMeta.push({
|
|
1316
|
+
destinationAddress: weth,
|
|
1317
|
+
signatureText: JSON.stringify({
|
|
1318
|
+
kind: "EulerV2",
|
|
1319
|
+
name: "WETH.deposit",
|
|
1320
|
+
function: "deposit()",
|
|
1321
|
+
valueWei: collateralWei.toString(),
|
|
1322
|
+
vaultMarket: vaultLabel,
|
|
1323
|
+
note: "Wrap native for Euler v2 isolated borrow collateral (same batch as borrow flow)."
|
|
1324
|
+
}),
|
|
1325
|
+
evm: { type: "euler_v2_weth_deposit", version: 1, chainId: String(args.chainId) },
|
|
1326
|
+
eulerV2: { step: "weth_deposit", vaultMarket: vaultLabel, flow: "borrow" }
|
|
1327
|
+
});
|
|
1328
|
+
} else if (s.kind === "approve") {
|
|
1329
|
+
batchMeta.push({
|
|
1330
|
+
destinationAddress: s.to,
|
|
1331
|
+
signatureText: JSON.stringify({
|
|
1332
|
+
kind: "EulerV2",
|
|
1333
|
+
name: "ERC20.approve",
|
|
1334
|
+
to: "Euler collateral eVault",
|
|
1335
|
+
function: "approve(address spender, uint256 amount)",
|
|
1336
|
+
collateralVault,
|
|
1337
|
+
amountHuman: args.collateralAmountHuman,
|
|
1338
|
+
note: "Allowance for initial and follow-on collateral deposits (buffered).",
|
|
1339
|
+
approveTotalWei: approveTargetWei.toString()
|
|
1340
|
+
}),
|
|
1341
|
+
evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
|
|
1342
|
+
eulerV2: { vaultMarket: vaultLabel, flow: "borrow_collateral_approve" }
|
|
1343
|
+
});
|
|
1344
|
+
} else {
|
|
1345
|
+
const borrowNote = args.redepositBorrowedToCollateral ? `Same-asset leverage: ${loops.length} borrow\u2192deposit round(s); total borrow wei ${borrowWeiTotal.toString()}.` : "Deposit collateral, enableCollateral, enableController, borrow in one EVC batch.";
|
|
1346
|
+
batchMeta.push({
|
|
1347
|
+
destinationAddress: evc,
|
|
1348
|
+
signatureText: JSON.stringify({
|
|
1349
|
+
kind: "EulerV2",
|
|
1350
|
+
name: "EVC.batch",
|
|
1351
|
+
function: "batch(BatchItem[])",
|
|
1352
|
+
evc,
|
|
1353
|
+
borrowVault,
|
|
1354
|
+
collateralVault,
|
|
1355
|
+
collateralAmountHuman: args.collateralAmountHuman,
|
|
1356
|
+
borrowAmountHuman: args.borrowAmountHuman,
|
|
1357
|
+
vaultMarket: vaultLabel,
|
|
1358
|
+
loopBorrowRounds: loops.length,
|
|
1359
|
+
redepositBorrowedToCollateral: args.redepositBorrowedToCollateral,
|
|
1360
|
+
borrowWeiTotal: borrowWeiTotal.toString(),
|
|
1361
|
+
note: borrowNote
|
|
1362
|
+
}),
|
|
1363
|
+
evm: { type: "euler_v2_evc_borrow_batch", version: 1, chainId: String(args.chainId) },
|
|
1364
|
+
eulerV2: {
|
|
1365
|
+
flow: "borrow_batch",
|
|
1366
|
+
vaultMarket: vaultLabel,
|
|
1367
|
+
borrowVault,
|
|
1368
|
+
collateralVault,
|
|
1369
|
+
collateralAmountHuman: args.collateralAmountHuman,
|
|
1370
|
+
borrowAmountHuman: args.borrowAmountHuman,
|
|
1371
|
+
loopBorrowRounds: loops.length,
|
|
1372
|
+
redepositBorrowedToCollateral: args.redepositBorrowedToCollateral,
|
|
1373
|
+
borrowWeiTotal: borrowWeiTotal.toString()
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
const extraPayload = { batchMeta };
|
|
1379
|
+
if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
|
|
1380
|
+
extraPayload.customGasChainDetails = args.customGasChainDetails;
|
|
1381
|
+
}
|
|
1382
|
+
const extraJSON = JSON.stringify(extraPayload);
|
|
1383
|
+
const firstSigText = batchMeta[0].signatureText;
|
|
1384
|
+
const n = steps.length;
|
|
1385
|
+
const hasWrap = args.isNativeCollateralIn;
|
|
1386
|
+
const purposeSuffix = (() => {
|
|
1387
|
+
const tail = args.redepositBorrowedToCollateral ? `deposit collateral, enableCollateral, enableController, then ${loops.length}\xD7 borrow+redeposit on "${vaultLabel}" (same-asset target LTV loop).` : `deposit collateral, enableCollateral, enableController, borrow from "${vaultLabel}".`;
|
|
1388
|
+
if (hasWrap) {
|
|
1389
|
+
return `Euler v2: ${n}-tx batch \u2014 wrap native collateral (if needed), approve collateral eVault (buffered for all deposits), then EVC batch: ${tail}`;
|
|
1390
|
+
}
|
|
1391
|
+
return `Euler v2: ${n}-tx batch \u2014 approve collateral (if needed) with buffer for all deposits, then EVC batch: ${tail}`;
|
|
1392
|
+
})();
|
|
1393
|
+
const firstValue = steps[0].value;
|
|
1394
|
+
const bodyForSign = {
|
|
1395
|
+
keyList,
|
|
1396
|
+
pubKey: ph,
|
|
1397
|
+
msgHash: messageHashes[0],
|
|
1398
|
+
msgRaw: firstDataNo0x,
|
|
1399
|
+
messageHashes,
|
|
1400
|
+
messageRawBatch,
|
|
1401
|
+
destinationChainID: String(args.chainId),
|
|
1402
|
+
destinationAddress: steps[0].to,
|
|
1403
|
+
extraJSON,
|
|
1404
|
+
signatureText: firstSigText,
|
|
1405
|
+
purpose: (() => {
|
|
1406
|
+
const t = args.purposeText.trim();
|
|
1407
|
+
return (t ? `${t}
|
|
1408
|
+
|
|
1409
|
+
` : "") + purposeSuffix;
|
|
1410
|
+
})(),
|
|
1411
|
+
...firstTxFeePayload,
|
|
1412
|
+
proposalTxParams: proposalTxParamsBatch
|
|
1413
|
+
};
|
|
1414
|
+
if (firstValue > 0n) {
|
|
1415
|
+
bodyForSign.value = firstValue.toString();
|
|
1416
|
+
}
|
|
1417
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
1418
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
1419
|
+
}
|
|
1420
|
+
var EULER_REPAY_BATCH_FALLBACK = 1200000n;
|
|
1421
|
+
var EULER_ERC20_APPROVE_FALLBACK3 = 100000n;
|
|
1422
|
+
var erc20AllowanceAbi3 = viem.parseAbi([
|
|
1423
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
1424
|
+
"function decimals() view returns (uint8)"
|
|
1425
|
+
]);
|
|
1426
|
+
var erc20ApproveAbi3 = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
1427
|
+
var evaultRepayAbi = viem.parseAbi(["function repay(uint256 amount, address receiver) returns (uint256)"]);
|
|
1428
|
+
var evcAbi2 = viem.parseAbi([
|
|
1429
|
+
"function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
|
|
1430
|
+
]);
|
|
1431
|
+
function gweiToDecimalString4(n) {
|
|
1432
|
+
if (!Number.isFinite(n)) return "0";
|
|
1433
|
+
if (n === 0) return "0";
|
|
1434
|
+
const s = String(n);
|
|
1435
|
+
if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
|
|
1436
|
+
return s;
|
|
1437
|
+
}
|
|
1438
|
+
function txToViemStep3(tx) {
|
|
1439
|
+
return { to: viem.getAddress(tx.to), data: tx.data, value: tx.value };
|
|
1440
|
+
}
|
|
1441
|
+
async function buildEvmMultisignBodyEulerV2BorrowRepayBatch(args) {
|
|
1442
|
+
const ph = (args.keyGen.pubkeyhex ?? "").trim();
|
|
1443
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
1444
|
+
const keyList = args.keyGen.keylist ?? [];
|
|
1445
|
+
const clientId = firstClientIdFromKeyGen(args.keyGen);
|
|
1446
|
+
const evc = viem.getAddress(args.evc);
|
|
1447
|
+
const borrowVault = viem.getAddress(args.borrowVault);
|
|
1448
|
+
const borrowAsset = viem.getAddress(args.borrowUnderlying);
|
|
1449
|
+
const subAccount = viem.getAddress(args.subAccount);
|
|
1450
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
1451
|
+
const ch = viem.defineChain({
|
|
1452
|
+
id: args.chainId,
|
|
1453
|
+
name: "EulerRepay",
|
|
1454
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
1455
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
1456
|
+
});
|
|
1457
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
1458
|
+
const bDecRaw = await publicClient.readContract({
|
|
1459
|
+
address: borrowAsset,
|
|
1460
|
+
abi: erc20AllowanceAbi3,
|
|
1461
|
+
functionName: "decimals"
|
|
1462
|
+
});
|
|
1463
|
+
const borrowDecimals = Number(bDecRaw);
|
|
1464
|
+
const debtAbi = viem.parseAbi(["function debtOf(address account) view returns (uint256)"]);
|
|
1465
|
+
const owed = await publicClient.readContract({
|
|
1466
|
+
address: borrowVault,
|
|
1467
|
+
abi: debtAbi,
|
|
1468
|
+
functionName: "debtOf",
|
|
1469
|
+
args: [subAccount]
|
|
1470
|
+
});
|
|
1471
|
+
let repayWei;
|
|
1472
|
+
if (args.repayAll) {
|
|
1473
|
+
repayWei = viem.maxUint256;
|
|
1474
|
+
} else {
|
|
1475
|
+
repayWei = viem.parseUnits(args.amountHuman, borrowDecimals);
|
|
1476
|
+
if (repayWei === 0n) throw new Error("Repay amount is zero after converting with token decimals.");
|
|
1477
|
+
if (repayWei > owed) {
|
|
1478
|
+
repayWei = owed;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
const steps = [];
|
|
1482
|
+
const allowanceTarget = repayWei === viem.maxUint256 ? owed : repayWei;
|
|
1483
|
+
const currentAllowance = await publicClient.readContract({
|
|
1484
|
+
address: borrowAsset,
|
|
1485
|
+
abi: erc20AllowanceAbi3,
|
|
1486
|
+
functionName: "allowance",
|
|
1487
|
+
args: [executor, borrowVault]
|
|
1488
|
+
});
|
|
1489
|
+
if (currentAllowance < allowanceTarget) {
|
|
1490
|
+
if (currentAllowance > 0n) {
|
|
1491
|
+
const dataReset = viem.encodeFunctionData({
|
|
1492
|
+
abi: erc20ApproveAbi3,
|
|
1493
|
+
functionName: "approve",
|
|
1494
|
+
args: [borrowVault, 0n]
|
|
1495
|
+
});
|
|
1496
|
+
steps.push({ kind: "approve", to: borrowAsset, data: dataReset, value: 0n });
|
|
1497
|
+
}
|
|
1498
|
+
const approveAmt = repayWei === viem.maxUint256 ? viem.maxUint256 : repayWei;
|
|
1499
|
+
const dataApprove = viem.encodeFunctionData({
|
|
1500
|
+
abi: erc20ApproveAbi3,
|
|
1501
|
+
functionName: "approve",
|
|
1502
|
+
args: [borrowVault, approveAmt]
|
|
1503
|
+
});
|
|
1504
|
+
steps.push({ kind: "approve", to: borrowAsset, data: dataApprove, value: 0n });
|
|
1505
|
+
}
|
|
1506
|
+
const repayData = viem.encodeFunctionData({
|
|
1507
|
+
abi: evaultRepayAbi,
|
|
1508
|
+
functionName: "repay",
|
|
1509
|
+
args: [repayWei, subAccount]
|
|
1510
|
+
});
|
|
1511
|
+
const batchItems = [
|
|
1512
|
+
{ targetContract: borrowVault, onBehalfOfAccount: subAccount, value: 0n, data: repayData }
|
|
1513
|
+
];
|
|
1514
|
+
const batchData = viem.encodeFunctionData({
|
|
1515
|
+
abi: evcAbi2,
|
|
1516
|
+
functionName: "batch",
|
|
1517
|
+
args: [batchItems]
|
|
1518
|
+
});
|
|
1519
|
+
steps.push({ kind: "evc_batch", to: evc, data: batchData, value: 0n });
|
|
1520
|
+
const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
|
|
1521
|
+
const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
|
|
1522
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
1523
|
+
const useCustomGas = args.useCustomGas;
|
|
1524
|
+
const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
|
|
1525
|
+
const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
|
|
1526
|
+
const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
1527
|
+
const messageHashes = [];
|
|
1528
|
+
const messageRawBatch = [];
|
|
1529
|
+
const proposalTxParamsBatch = [];
|
|
1530
|
+
const batchMeta = [];
|
|
1531
|
+
let firstTxFeePayload = {};
|
|
1532
|
+
let firstDataNo0x = "";
|
|
1533
|
+
const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
|
|
1534
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1535
|
+
const s = steps[i];
|
|
1536
|
+
const v = txToViemStep3(s);
|
|
1537
|
+
const currentNonce = baseNonce + i;
|
|
1538
|
+
let estimatedGas;
|
|
1539
|
+
try {
|
|
1540
|
+
estimatedGas = await publicClient.estimateGas({
|
|
1541
|
+
to: v.to,
|
|
1542
|
+
data: v.data,
|
|
1543
|
+
value: v.value,
|
|
1544
|
+
account: executor
|
|
1545
|
+
});
|
|
1546
|
+
} catch {
|
|
1547
|
+
estimatedGas = s.kind === "approve" ? EULER_ERC20_APPROVE_FALLBACK3 : EULER_REPAY_BATCH_FALLBACK;
|
|
1548
|
+
}
|
|
1549
|
+
const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
1550
|
+
if (legacy) {
|
|
1551
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
1552
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1553
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1554
|
+
}
|
|
1555
|
+
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
1556
|
+
const configured = viem.parseGwei(gweiToDecimalString4(Number(args.chainDetail.gasPrice)));
|
|
1557
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
1558
|
+
}
|
|
1559
|
+
const ser = viem.serializeTransaction({
|
|
1560
|
+
type: "legacy",
|
|
1561
|
+
to: v.to,
|
|
1562
|
+
data: v.data,
|
|
1563
|
+
value: v.value,
|
|
1564
|
+
gas: gasLimitI,
|
|
1565
|
+
gasPrice: gasPriceWei,
|
|
1566
|
+
nonce: currentNonce,
|
|
1567
|
+
chainId: args.chainId
|
|
1568
|
+
});
|
|
1569
|
+
const h = viem.keccak256(ser);
|
|
1570
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
1571
|
+
messageRawBatch.push(ser);
|
|
1572
|
+
proposalTxParamsBatch.push({
|
|
1573
|
+
nonce: currentNonce,
|
|
1574
|
+
gasLimit: gasLimitI.toString(),
|
|
1575
|
+
txType: "legacy",
|
|
1576
|
+
gasPrice: gasPriceWei.toString()
|
|
1577
|
+
});
|
|
1578
|
+
if (i === 0) {
|
|
1579
|
+
firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
|
|
1580
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
1581
|
+
}
|
|
1582
|
+
} else {
|
|
1583
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
1584
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
1585
|
+
const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
1586
|
+
const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
1587
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
1588
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
1589
|
+
const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
1590
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
1591
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
1592
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString4(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
1593
|
+
let maxFeePerGas = viem.parseGwei(gweiToDecimalString4(maxFeePerGasGwei));
|
|
1594
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1595
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1596
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1597
|
+
}
|
|
1598
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
|
|
1599
|
+
maxFeePerGas,
|
|
1600
|
+
maxPriorityFeePerGas,
|
|
1601
|
+
latestBaseFeeWei
|
|
1602
|
+
));
|
|
1603
|
+
const ser = viem.serializeTransaction({
|
|
1604
|
+
type: "eip1559",
|
|
1605
|
+
to: v.to,
|
|
1606
|
+
data: v.data,
|
|
1607
|
+
value: v.value,
|
|
1608
|
+
gas: gasLimitI,
|
|
1609
|
+
maxFeePerGas,
|
|
1610
|
+
maxPriorityFeePerGas,
|
|
1611
|
+
nonce: currentNonce,
|
|
1612
|
+
chainId: args.chainId
|
|
1613
|
+
});
|
|
1614
|
+
const h = viem.keccak256(ser);
|
|
1615
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
1616
|
+
messageRawBatch.push(ser);
|
|
1617
|
+
proposalTxParamsBatch.push({
|
|
1618
|
+
nonce: currentNonce,
|
|
1619
|
+
gasLimit: gasLimitI.toString(),
|
|
1620
|
+
txType: "eip1559",
|
|
1621
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
1622
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1623
|
+
});
|
|
1624
|
+
if (i === 0) {
|
|
1625
|
+
firstTxFeePayload = {
|
|
1626
|
+
txNonce: currentNonce,
|
|
1627
|
+
txGasLimit: gasLimitI.toString(),
|
|
1628
|
+
txMaxFeePerGas: maxFeePerGas.toString(),
|
|
1629
|
+
txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1630
|
+
};
|
|
1631
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
if (s.kind === "approve") {
|
|
1635
|
+
batchMeta.push({
|
|
1636
|
+
destinationAddress: s.to,
|
|
1637
|
+
signatureText: JSON.stringify({
|
|
1638
|
+
kind: "EulerV2",
|
|
1639
|
+
name: "ERC20.approve",
|
|
1640
|
+
function: "approve(address spender, uint256 amount)",
|
|
1641
|
+
spender: borrowVault,
|
|
1642
|
+
borrowUnderlying: borrowAsset,
|
|
1643
|
+
note: "Allow Euler liability vault to pull assets for repay."
|
|
1644
|
+
}),
|
|
1645
|
+
evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
|
|
1646
|
+
eulerV2: { vaultMarket: vaultLabel, flow: "borrow_repay_approve" }
|
|
1647
|
+
});
|
|
1648
|
+
} else {
|
|
1649
|
+
batchMeta.push({
|
|
1650
|
+
destinationAddress: evc,
|
|
1651
|
+
signatureText: JSON.stringify({
|
|
1652
|
+
kind: "EulerV2",
|
|
1653
|
+
name: "EVC.batch",
|
|
1654
|
+
function: "batch(BatchItem[]) \u2014 repay",
|
|
1655
|
+
evc,
|
|
1656
|
+
borrowVault,
|
|
1657
|
+
subAccount,
|
|
1658
|
+
amountHuman: args.repayAll ? "max" : args.amountHuman,
|
|
1659
|
+
vaultMarket: vaultLabel,
|
|
1660
|
+
note: "Repay borrow on Euler v2 liability vault via EVC."
|
|
1661
|
+
}),
|
|
1662
|
+
evm: { type: "euler_v2_borrow_repay_batch", version: 1, chainId: String(args.chainId) },
|
|
1663
|
+
eulerV2: {
|
|
1664
|
+
flow: "borrow_repay",
|
|
1665
|
+
vaultMarket: vaultLabel,
|
|
1666
|
+
borrowVault,
|
|
1667
|
+
subAccount,
|
|
1668
|
+
repayAll: args.repayAll,
|
|
1669
|
+
amountHuman: args.amountHuman
|
|
1670
|
+
}
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
const extraPayload = { batchMeta };
|
|
1675
|
+
if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
|
|
1676
|
+
extraPayload.customGasChainDetails = args.customGasChainDetails;
|
|
1677
|
+
}
|
|
1678
|
+
const extraJSON = JSON.stringify(extraPayload);
|
|
1679
|
+
const firstSigText = batchMeta[0].signatureText;
|
|
1680
|
+
const n = steps.length;
|
|
1681
|
+
const purposeSuffix = `Euler v2: ${n}-tx batch \u2014 repay borrow on "${vaultLabel}" (approve if needed, then EVC repay).`;
|
|
1682
|
+
const bodyForSign = {
|
|
1683
|
+
keyList,
|
|
1684
|
+
pubKey: ph,
|
|
1685
|
+
msgHash: messageHashes[0],
|
|
1686
|
+
msgRaw: firstDataNo0x,
|
|
1687
|
+
messageHashes,
|
|
1688
|
+
messageRawBatch,
|
|
1689
|
+
destinationChainID: String(args.chainId),
|
|
1690
|
+
destinationAddress: steps[0].to,
|
|
1691
|
+
extraJSON,
|
|
1692
|
+
signatureText: firstSigText,
|
|
1693
|
+
purpose: (() => {
|
|
1694
|
+
const t = args.purposeText.trim();
|
|
1695
|
+
return (t ? `${t}
|
|
1696
|
+
|
|
1697
|
+
` : "") + purposeSuffix;
|
|
1698
|
+
})(),
|
|
1699
|
+
...firstTxFeePayload,
|
|
1700
|
+
proposalTxParams: proposalTxParamsBatch
|
|
1701
|
+
};
|
|
1702
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
1703
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
1704
|
+
}
|
|
1705
|
+
var EULER_COLLATERAL_DEPOSIT_BATCH_FALLBACK = 1600000n;
|
|
1706
|
+
var EULER_ERC20_APPROVE_FALLBACK4 = 100000n;
|
|
1707
|
+
var erc20AllowanceAbi4 = viem.parseAbi([
|
|
1708
|
+
"function allowance(address owner, address spender) view returns (uint256)"
|
|
1709
|
+
]);
|
|
1710
|
+
var erc20ApproveAbi4 = viem.parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
|
|
1711
|
+
var erc4626DepositAbi3 = viem.parseAbi([
|
|
1712
|
+
"function deposit(uint256 assets, address receiver) returns (uint256 shares)"
|
|
1713
|
+
]);
|
|
1714
|
+
var evcAbi3 = viem.parseAbi([
|
|
1715
|
+
"function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
|
|
1716
|
+
]);
|
|
1717
|
+
function gweiToDecimalString5(n) {
|
|
1718
|
+
if (!Number.isFinite(n)) return "0";
|
|
1719
|
+
if (n === 0) return "0";
|
|
1720
|
+
const s = String(n);
|
|
1721
|
+
if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
|
|
1722
|
+
return s;
|
|
1723
|
+
}
|
|
1724
|
+
function txToViemStep4(tx) {
|
|
1725
|
+
return { to: viem.getAddress(tx.to), data: tx.data, value: tx.value };
|
|
1726
|
+
}
|
|
1727
|
+
async function buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch(args) {
|
|
1728
|
+
const ph = (args.keyGen.pubkeyhex ?? "").trim();
|
|
1729
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
1730
|
+
const keyList = args.keyGen.keylist ?? [];
|
|
1731
|
+
const clientId = firstClientIdFromKeyGen(args.keyGen);
|
|
1732
|
+
const evc = viem.getAddress(args.evc);
|
|
1733
|
+
const collateralVault = viem.getAddress(args.collateralVault);
|
|
1734
|
+
const collateralAsset = viem.getAddress(args.collateralUnderlying);
|
|
1735
|
+
const subAccount = viem.getAddress(args.subAccount);
|
|
1736
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
1737
|
+
const ch = viem.defineChain({
|
|
1738
|
+
id: args.chainId,
|
|
1739
|
+
name: "EulerColDeposit",
|
|
1740
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
1741
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
1742
|
+
});
|
|
1743
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
1744
|
+
const dec = await fetchEulerVaultAssetDecimals({
|
|
1745
|
+
rpcUrl: args.rpcUrl,
|
|
1746
|
+
chainId: args.chainId,
|
|
1747
|
+
evault: collateralVault
|
|
1748
|
+
});
|
|
1749
|
+
const amountWei = viem.parseUnits(args.amountHuman, dec);
|
|
1750
|
+
if (amountWei === 0n) throw new Error("Deposit amount is zero after converting with token decimals.");
|
|
1751
|
+
const steps = [];
|
|
1752
|
+
const currentAllowance = await publicClient.readContract({
|
|
1753
|
+
address: collateralAsset,
|
|
1754
|
+
abi: erc20AllowanceAbi4,
|
|
1755
|
+
functionName: "allowance",
|
|
1756
|
+
args: [executor, collateralVault]
|
|
1757
|
+
});
|
|
1758
|
+
if (currentAllowance < amountWei) {
|
|
1759
|
+
if (currentAllowance > 0n) {
|
|
1760
|
+
const dataReset = viem.encodeFunctionData({
|
|
1761
|
+
abi: erc20ApproveAbi4,
|
|
1762
|
+
functionName: "approve",
|
|
1763
|
+
args: [collateralVault, 0n]
|
|
1764
|
+
});
|
|
1765
|
+
steps.push({ kind: "approve", to: collateralAsset, data: dataReset, value: 0n });
|
|
1766
|
+
}
|
|
1767
|
+
const dataApprove = viem.encodeFunctionData({
|
|
1768
|
+
abi: erc20ApproveAbi4,
|
|
1769
|
+
functionName: "approve",
|
|
1770
|
+
args: [collateralVault, amountWei]
|
|
1771
|
+
});
|
|
1772
|
+
steps.push({ kind: "approve", to: collateralAsset, data: dataApprove, value: 0n });
|
|
1773
|
+
}
|
|
1774
|
+
const depositData = viem.encodeFunctionData({
|
|
1775
|
+
abi: erc4626DepositAbi3,
|
|
1776
|
+
functionName: "deposit",
|
|
1777
|
+
args: [amountWei, subAccount]
|
|
1778
|
+
});
|
|
1779
|
+
const batchItems = [
|
|
1780
|
+
{
|
|
1781
|
+
targetContract: collateralVault,
|
|
1782
|
+
onBehalfOfAccount: subAccount,
|
|
1783
|
+
value: 0n,
|
|
1784
|
+
data: depositData
|
|
1785
|
+
}
|
|
1786
|
+
];
|
|
1787
|
+
const batchData = viem.encodeFunctionData({
|
|
1788
|
+
abi: evcAbi3,
|
|
1789
|
+
functionName: "batch",
|
|
1790
|
+
args: [batchItems]
|
|
1791
|
+
});
|
|
1792
|
+
steps.push({ kind: "evc_batch", to: evc, data: batchData, value: 0n });
|
|
1793
|
+
const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
|
|
1794
|
+
const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
|
|
1795
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
1796
|
+
const useCustomGas = args.useCustomGas;
|
|
1797
|
+
const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
|
|
1798
|
+
const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
|
|
1799
|
+
const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
1800
|
+
const messageHashes = [];
|
|
1801
|
+
const messageRawBatch = [];
|
|
1802
|
+
const proposalTxParamsBatch = [];
|
|
1803
|
+
const batchMeta = [];
|
|
1804
|
+
let firstTxFeePayload = {};
|
|
1805
|
+
let firstDataNo0x = "";
|
|
1806
|
+
const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
|
|
1807
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1808
|
+
const s = steps[i];
|
|
1809
|
+
const v = txToViemStep4(s);
|
|
1810
|
+
const currentNonce = baseNonce + i;
|
|
1811
|
+
let estimatedGas;
|
|
1812
|
+
try {
|
|
1813
|
+
estimatedGas = await publicClient.estimateGas({
|
|
1814
|
+
to: v.to,
|
|
1815
|
+
data: v.data,
|
|
1816
|
+
value: v.value,
|
|
1817
|
+
account: executor
|
|
1818
|
+
});
|
|
1819
|
+
} catch {
|
|
1820
|
+
estimatedGas = s.kind === "approve" ? EULER_ERC20_APPROVE_FALLBACK4 : EULER_COLLATERAL_DEPOSIT_BATCH_FALLBACK;
|
|
1821
|
+
}
|
|
1822
|
+
const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
1823
|
+
if (legacy) {
|
|
1824
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
1825
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1826
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1827
|
+
}
|
|
1828
|
+
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
1829
|
+
const configured = viem.parseGwei(gweiToDecimalString5(Number(args.chainDetail.gasPrice)));
|
|
1830
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
1831
|
+
}
|
|
1832
|
+
const ser = viem.serializeTransaction({
|
|
1833
|
+
type: "legacy",
|
|
1834
|
+
to: v.to,
|
|
1835
|
+
data: v.data,
|
|
1836
|
+
value: v.value,
|
|
1837
|
+
gas: gasLimitI,
|
|
1838
|
+
gasPrice: gasPriceWei,
|
|
1839
|
+
nonce: currentNonce,
|
|
1840
|
+
chainId: args.chainId
|
|
1841
|
+
});
|
|
1842
|
+
const h = viem.keccak256(ser);
|
|
1843
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
1844
|
+
messageRawBatch.push(ser);
|
|
1845
|
+
proposalTxParamsBatch.push({
|
|
1846
|
+
nonce: currentNonce,
|
|
1847
|
+
gasLimit: gasLimitI.toString(),
|
|
1848
|
+
txType: "legacy",
|
|
1849
|
+
gasPrice: gasPriceWei.toString()
|
|
1850
|
+
});
|
|
1851
|
+
if (i === 0) {
|
|
1852
|
+
firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
|
|
1853
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
1854
|
+
}
|
|
1855
|
+
} else {
|
|
1856
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
1857
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
1858
|
+
const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
1859
|
+
const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
1860
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
1861
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
1862
|
+
const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
1863
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
1864
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
1865
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString5(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
1866
|
+
let maxFeePerGas = viem.parseGwei(gweiToDecimalString5(maxFeePerGasGwei));
|
|
1867
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
1868
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1869
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
1870
|
+
}
|
|
1871
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
|
|
1872
|
+
maxFeePerGas,
|
|
1873
|
+
maxPriorityFeePerGas,
|
|
1874
|
+
latestBaseFeeWei
|
|
1875
|
+
));
|
|
1876
|
+
const ser = viem.serializeTransaction({
|
|
1877
|
+
type: "eip1559",
|
|
1878
|
+
to: v.to,
|
|
1879
|
+
data: v.data,
|
|
1880
|
+
value: v.value,
|
|
1881
|
+
gas: gasLimitI,
|
|
1882
|
+
maxFeePerGas,
|
|
1883
|
+
maxPriorityFeePerGas,
|
|
1884
|
+
nonce: currentNonce,
|
|
1885
|
+
chainId: args.chainId
|
|
1886
|
+
});
|
|
1887
|
+
const h = viem.keccak256(ser);
|
|
1888
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
1889
|
+
messageRawBatch.push(ser);
|
|
1890
|
+
proposalTxParamsBatch.push({
|
|
1891
|
+
nonce: currentNonce,
|
|
1892
|
+
gasLimit: gasLimitI.toString(),
|
|
1893
|
+
txType: "eip1559",
|
|
1894
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
1895
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1896
|
+
});
|
|
1897
|
+
if (i === 0) {
|
|
1898
|
+
firstTxFeePayload = {
|
|
1899
|
+
txNonce: currentNonce,
|
|
1900
|
+
txGasLimit: gasLimitI.toString(),
|
|
1901
|
+
txMaxFeePerGas: maxFeePerGas.toString(),
|
|
1902
|
+
txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
1903
|
+
};
|
|
1904
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
if (s.kind === "approve") {
|
|
1908
|
+
batchMeta.push({
|
|
1909
|
+
destinationAddress: s.to,
|
|
1910
|
+
signatureText: JSON.stringify({
|
|
1911
|
+
kind: "EulerV2",
|
|
1912
|
+
name: "ERC20.approve",
|
|
1913
|
+
function: "approve(address spender, uint256 amount)",
|
|
1914
|
+
spender: collateralVault,
|
|
1915
|
+
collateralUnderlying: collateralAsset,
|
|
1916
|
+
note: "Allow Euler collateral eVault to pull assets for collateral deposit."
|
|
1917
|
+
}),
|
|
1918
|
+
evm: { type: "euler_v2_erc20_approve", version: 1, chainId: String(args.chainId) },
|
|
1919
|
+
eulerV2: { vaultMarket: vaultLabel, flow: "borrow_collateral_deposit_approve" }
|
|
1920
|
+
});
|
|
1921
|
+
} else {
|
|
1922
|
+
batchMeta.push({
|
|
1923
|
+
destinationAddress: evc,
|
|
1924
|
+
signatureText: JSON.stringify({
|
|
1925
|
+
kind: "EulerV2",
|
|
1926
|
+
name: "EVC.batch",
|
|
1927
|
+
function: "batch(BatchItem[]) \u2014 collateral deposit",
|
|
1928
|
+
evc,
|
|
1929
|
+
collateralVault,
|
|
1930
|
+
subAccount,
|
|
1931
|
+
amountHuman: args.amountHuman,
|
|
1932
|
+
vaultMarket: vaultLabel,
|
|
1933
|
+
note: "Deposit collateral into Euler v2 collateral eVault via EVC (reduces LTV)."
|
|
1934
|
+
}),
|
|
1935
|
+
evm: { type: "euler_v2_borrow_collateral_deposit_batch", version: 1, chainId: String(args.chainId) },
|
|
1936
|
+
eulerV2: {
|
|
1937
|
+
flow: "borrow_collateral_deposit",
|
|
1938
|
+
vaultMarket: vaultLabel,
|
|
1939
|
+
collateralVault,
|
|
1940
|
+
subAccount,
|
|
1941
|
+
amountHuman: args.amountHuman
|
|
1942
|
+
}
|
|
1943
|
+
});
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
const extraPayload = { batchMeta };
|
|
1947
|
+
if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
|
|
1948
|
+
extraPayload.customGasChainDetails = args.customGasChainDetails;
|
|
1949
|
+
}
|
|
1950
|
+
const extraJSON = JSON.stringify(extraPayload);
|
|
1951
|
+
const firstSigText = batchMeta[0].signatureText;
|
|
1952
|
+
const n = steps.length;
|
|
1953
|
+
const purposeSuffix = `Euler v2: ${n}-tx batch \u2014 deposit ${args.amountHuman} collateral into "${vaultLabel}" (approve if needed, then EVC deposit).`;
|
|
1954
|
+
const bodyForSign = {
|
|
1955
|
+
keyList,
|
|
1956
|
+
pubKey: ph,
|
|
1957
|
+
msgHash: messageHashes[0],
|
|
1958
|
+
msgRaw: firstDataNo0x,
|
|
1959
|
+
messageHashes,
|
|
1960
|
+
messageRawBatch,
|
|
1961
|
+
destinationChainID: String(args.chainId),
|
|
1962
|
+
destinationAddress: steps[0].to,
|
|
1963
|
+
extraJSON,
|
|
1964
|
+
signatureText: firstSigText,
|
|
1965
|
+
purpose: (() => {
|
|
1966
|
+
const t = args.purposeText.trim();
|
|
1967
|
+
return (t ? `${t}
|
|
1968
|
+
|
|
1969
|
+
` : "") + purposeSuffix;
|
|
1970
|
+
})(),
|
|
1971
|
+
...firstTxFeePayload,
|
|
1972
|
+
proposalTxParams: proposalTxParamsBatch
|
|
1973
|
+
};
|
|
1974
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
1975
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
1976
|
+
}
|
|
1977
|
+
var EULER_COLLATERAL_WITHDRAW_BATCH_FALLBACK = 1400000n;
|
|
1978
|
+
var erc4626WithdrawAbi3 = viem.parseAbi([
|
|
1979
|
+
"function withdraw(uint256 assets, address receiver, address owner) returns (uint256 shares)"
|
|
1980
|
+
]);
|
|
1981
|
+
var evcAbi4 = viem.parseAbi([
|
|
1982
|
+
"function batch((address targetContract, address onBehalfOfAccount, uint256 value, bytes data)[])"
|
|
1983
|
+
]);
|
|
1984
|
+
function gweiToDecimalString6(n) {
|
|
1985
|
+
if (!Number.isFinite(n)) return "0";
|
|
1986
|
+
if (n === 0) return "0";
|
|
1987
|
+
const s = String(n);
|
|
1988
|
+
if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
|
|
1989
|
+
return s;
|
|
1990
|
+
}
|
|
1991
|
+
function txToViemStep5(tx) {
|
|
1992
|
+
return { to: viem.getAddress(tx.to), data: tx.data, value: tx.value };
|
|
1993
|
+
}
|
|
1994
|
+
async function buildEvmMultisignBodyEulerV2BorrowCollateralWithdrawBatch(args) {
|
|
1995
|
+
const ph = (args.keyGen.pubkeyhex ?? "").trim();
|
|
1996
|
+
if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
|
|
1997
|
+
const keyList = args.keyGen.keylist ?? [];
|
|
1998
|
+
const clientId = firstClientIdFromKeyGen(args.keyGen);
|
|
1999
|
+
const evc = viem.getAddress(args.evc);
|
|
2000
|
+
const collateralVault = viem.getAddress(args.collateralVault);
|
|
2001
|
+
const subAccount = viem.getAddress(args.subAccount);
|
|
2002
|
+
const receiver = viem.getAddress(args.receiver);
|
|
2003
|
+
const executor = viem.getAddress(args.executorAddress);
|
|
2004
|
+
const ch = viem.defineChain({
|
|
2005
|
+
id: args.chainId,
|
|
2006
|
+
name: "EulerColWithdraw",
|
|
2007
|
+
nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
|
|
2008
|
+
rpcUrls: { default: { http: [args.rpcUrl] } }
|
|
2009
|
+
});
|
|
2010
|
+
const publicClient = viem.createPublicClient({ chain: ch, transport: viem.http(args.rpcUrl) });
|
|
2011
|
+
const dec = await fetchEulerVaultAssetDecimals({ rpcUrl: args.rpcUrl, chainId: args.chainId, evault: collateralVault });
|
|
2012
|
+
const amountWei = viem.parseUnits(args.amountHuman, dec);
|
|
2013
|
+
if (amountWei === 0n) throw new Error("Withdraw amount is zero after converting with token decimals.");
|
|
2014
|
+
const maxW = await fetchEulerBorrowCollateralMaxWithdrawAssetsWei({
|
|
2015
|
+
rpcUrl: args.rpcUrl,
|
|
2016
|
+
chainId: args.chainId,
|
|
2017
|
+
evc,
|
|
2018
|
+
collateralVault,
|
|
2019
|
+
subAccount,
|
|
2020
|
+
caller: executor
|
|
2021
|
+
});
|
|
2022
|
+
if (amountWei > maxW) {
|
|
2023
|
+
throw new Error(
|
|
2024
|
+
"Withdraw amount exceeds simulated max for this collateral vault (LTV cap, vault cash, or health check). Try a smaller amount."
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
const withdrawData = viem.encodeFunctionData({
|
|
2028
|
+
abi: erc4626WithdrawAbi3,
|
|
2029
|
+
functionName: "withdraw",
|
|
2030
|
+
args: [amountWei, receiver, subAccount]
|
|
2031
|
+
});
|
|
2032
|
+
const batchItems = [
|
|
2033
|
+
{
|
|
2034
|
+
targetContract: collateralVault,
|
|
2035
|
+
onBehalfOfAccount: subAccount,
|
|
2036
|
+
value: 0n,
|
|
2037
|
+
data: withdrawData
|
|
2038
|
+
}
|
|
2039
|
+
];
|
|
2040
|
+
const batchData = viem.encodeFunctionData({
|
|
2041
|
+
abi: evcAbi4,
|
|
2042
|
+
functionName: "batch",
|
|
2043
|
+
args: [batchItems]
|
|
2044
|
+
});
|
|
2045
|
+
const steps = [{ kind: "evc_batch", to: evc, data: batchData, value: 0n }];
|
|
2046
|
+
const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
|
|
2047
|
+
const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
|
|
2048
|
+
const latestBaseFeeWei = !legacy ? (await publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
|
|
2049
|
+
const useCustomGas = args.useCustomGas;
|
|
2050
|
+
const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
|
|
2051
|
+
const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
|
|
2052
|
+
const baseNonce = await publicClient.getTransactionCount({ address: executor, blockTag: "pending" });
|
|
2053
|
+
const messageHashes = [];
|
|
2054
|
+
const messageRawBatch = [];
|
|
2055
|
+
const proposalTxParamsBatch = [];
|
|
2056
|
+
const batchMeta = [];
|
|
2057
|
+
let firstTxFeePayload = {};
|
|
2058
|
+
let firstDataNo0x = "";
|
|
2059
|
+
const vaultLabel = (args.vaultMarketLabel ?? "").trim() || "Euler vault";
|
|
2060
|
+
for (let i = 0; i < steps.length; i++) {
|
|
2061
|
+
const s = steps[i];
|
|
2062
|
+
const v = txToViemStep5(s);
|
|
2063
|
+
const currentNonce = baseNonce + i;
|
|
2064
|
+
let estimatedGas;
|
|
2065
|
+
try {
|
|
2066
|
+
estimatedGas = await publicClient.estimateGas({
|
|
2067
|
+
to: v.to,
|
|
2068
|
+
data: v.data,
|
|
2069
|
+
value: v.value,
|
|
2070
|
+
account: executor
|
|
2071
|
+
});
|
|
2072
|
+
} catch {
|
|
2073
|
+
estimatedGas = EULER_COLLATERAL_WITHDRAW_BATCH_FALLBACK;
|
|
2074
|
+
}
|
|
2075
|
+
const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
|
|
2076
|
+
if (legacy) {
|
|
2077
|
+
let gasPriceWei = await publicClient.getGasPrice();
|
|
2078
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
2079
|
+
gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
2080
|
+
}
|
|
2081
|
+
if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
|
|
2082
|
+
const configured = viem.parseGwei(gweiToDecimalString6(Number(args.chainDetail.gasPrice)));
|
|
2083
|
+
if (configured > gasPriceWei) gasPriceWei = configured;
|
|
2084
|
+
}
|
|
2085
|
+
const ser = viem.serializeTransaction({
|
|
2086
|
+
type: "legacy",
|
|
2087
|
+
to: v.to,
|
|
2088
|
+
data: v.data,
|
|
2089
|
+
value: v.value,
|
|
2090
|
+
gas: gasLimitI,
|
|
2091
|
+
gasPrice: gasPriceWei,
|
|
2092
|
+
nonce: currentNonce,
|
|
2093
|
+
chainId: args.chainId
|
|
2094
|
+
});
|
|
2095
|
+
const h = viem.keccak256(ser);
|
|
2096
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
2097
|
+
messageRawBatch.push(ser);
|
|
2098
|
+
proposalTxParamsBatch.push({
|
|
2099
|
+
nonce: currentNonce,
|
|
2100
|
+
gasLimit: gasLimitI.toString(),
|
|
2101
|
+
txType: "legacy",
|
|
2102
|
+
gasPrice: gasPriceWei.toString()
|
|
2103
|
+
});
|
|
2104
|
+
if (i === 0) {
|
|
2105
|
+
firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
|
|
2106
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
2107
|
+
}
|
|
2108
|
+
} else {
|
|
2109
|
+
const fetchedBase = feeParams.baseFeeGwei ?? 0;
|
|
2110
|
+
const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
|
|
2111
|
+
const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
|
|
2112
|
+
const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
|
|
2113
|
+
const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
|
|
2114
|
+
const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
|
|
2115
|
+
const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
|
|
2116
|
+
const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
|
|
2117
|
+
const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
|
|
2118
|
+
let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? viem.parseGwei(gweiToDecimalString6(effectivePriorityFeeGwei)) : viem.parseGwei("1");
|
|
2119
|
+
let maxFeePerGas = viem.parseGwei(gweiToDecimalString6(maxFeePerGasGwei));
|
|
2120
|
+
if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
|
|
2121
|
+
maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
2122
|
+
maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
|
|
2123
|
+
}
|
|
2124
|
+
({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
|
|
2125
|
+
maxFeePerGas,
|
|
2126
|
+
maxPriorityFeePerGas,
|
|
2127
|
+
latestBaseFeeWei
|
|
2128
|
+
));
|
|
2129
|
+
const ser = viem.serializeTransaction({
|
|
2130
|
+
type: "eip1559",
|
|
2131
|
+
to: v.to,
|
|
2132
|
+
data: v.data,
|
|
2133
|
+
value: v.value,
|
|
2134
|
+
gas: gasLimitI,
|
|
2135
|
+
maxFeePerGas,
|
|
2136
|
+
maxPriorityFeePerGas,
|
|
2137
|
+
nonce: currentNonce,
|
|
2138
|
+
chainId: args.chainId
|
|
2139
|
+
});
|
|
2140
|
+
const h = viem.keccak256(ser);
|
|
2141
|
+
messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
|
|
2142
|
+
messageRawBatch.push(ser);
|
|
2143
|
+
proposalTxParamsBatch.push({
|
|
2144
|
+
nonce: currentNonce,
|
|
2145
|
+
gasLimit: gasLimitI.toString(),
|
|
2146
|
+
txType: "eip1559",
|
|
2147
|
+
maxFeePerGas: maxFeePerGas.toString(),
|
|
2148
|
+
maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
2149
|
+
});
|
|
2150
|
+
if (i === 0) {
|
|
2151
|
+
firstTxFeePayload = {
|
|
2152
|
+
txNonce: currentNonce,
|
|
2153
|
+
txGasLimit: gasLimitI.toString(),
|
|
2154
|
+
txMaxFeePerGas: maxFeePerGas.toString(),
|
|
2155
|
+
txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
|
|
2156
|
+
};
|
|
2157
|
+
firstDataNo0x = v.data.startsWith("0x") ? v.data.slice(2) : v.data;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
batchMeta.push({
|
|
2161
|
+
destinationAddress: evc,
|
|
2162
|
+
signatureText: JSON.stringify({
|
|
2163
|
+
kind: "EulerV2",
|
|
2164
|
+
name: "EVC.batch",
|
|
2165
|
+
function: "batch \u2014 collateral withdraw",
|
|
2166
|
+
evc,
|
|
2167
|
+
collateralVault,
|
|
2168
|
+
subAccount,
|
|
2169
|
+
receiver,
|
|
2170
|
+
amountHuman: args.amountHuman,
|
|
2171
|
+
vaultMarket: vaultLabel,
|
|
2172
|
+
note: "Withdraw collateral from Euler v2 eVault via EVC (max from RPC simulate + LTV / vault cash)."
|
|
2173
|
+
}),
|
|
2174
|
+
evm: { type: "euler_v2_borrow_collateral_withdraw_batch", version: 1, chainId: String(args.chainId) },
|
|
2175
|
+
eulerV2: {
|
|
2176
|
+
flow: "borrow_collateral_withdraw",
|
|
2177
|
+
vaultMarket: vaultLabel,
|
|
2178
|
+
collateralVault,
|
|
2179
|
+
subAccount,
|
|
2180
|
+
receiver,
|
|
2181
|
+
amountHuman: args.amountHuman
|
|
2182
|
+
}
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
const extraPayload = { batchMeta };
|
|
2186
|
+
if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
|
|
2187
|
+
extraPayload.customGasChainDetails = args.customGasChainDetails;
|
|
2188
|
+
}
|
|
2189
|
+
const extraJSON = JSON.stringify(extraPayload);
|
|
2190
|
+
const firstSigText = batchMeta[0].signatureText;
|
|
2191
|
+
const purposeSuffix = `Euler v2: 1-tx \u2014 withdraw ${args.amountHuman} collateral from "${vaultLabel}" via EVC (sub-account).`;
|
|
2192
|
+
const bodyForSign = {
|
|
2193
|
+
keyList,
|
|
2194
|
+
pubKey: ph,
|
|
2195
|
+
msgHash: messageHashes[0],
|
|
2196
|
+
msgRaw: firstDataNo0x,
|
|
2197
|
+
messageHashes,
|
|
2198
|
+
messageRawBatch,
|
|
2199
|
+
destinationChainID: String(args.chainId),
|
|
2200
|
+
destinationAddress: steps[0].to,
|
|
2201
|
+
extraJSON,
|
|
2202
|
+
signatureText: firstSigText,
|
|
2203
|
+
purpose: (() => {
|
|
2204
|
+
const t = args.purposeText.trim();
|
|
2205
|
+
return (t ? `${t}
|
|
2206
|
+
|
|
2207
|
+
` : "") + purposeSuffix;
|
|
2208
|
+
})(),
|
|
2209
|
+
...firstTxFeePayload,
|
|
2210
|
+
proposalTxParams: proposalTxParamsBatch
|
|
2211
|
+
};
|
|
2212
|
+
if (clientId) bodyForSign.clientId = clientId;
|
|
2213
|
+
return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
// src/protocols/evm/euler-v2/index.ts
|
|
2217
|
+
var EULER_V2_PROTOCOL_ID = "euler-v2";
|
|
2218
|
+
var eulerV2ProtocolModule = {
|
|
2219
|
+
id: EULER_V2_PROTOCOL_ID,
|
|
2220
|
+
chainCategory: "evm",
|
|
2221
|
+
isChainSupported(ctx) {
|
|
2222
|
+
return ctx.chainCategory === "evm";
|
|
2223
|
+
},
|
|
2224
|
+
isTokenSupported(token) {
|
|
2225
|
+
return token.category === "evm" && (token.kind === "native" || token.kind === "erc20");
|
|
2226
|
+
},
|
|
2227
|
+
actions: [
|
|
2228
|
+
{ id: "euler-v2.isolated-lend", protocolId: EULER_V2_PROTOCOL_ID, chainCategory: "evm", description: "Deposit into Euler vault", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
|
|
2229
|
+
{ id: "euler-v2.isolated-borrow", protocolId: EULER_V2_PROTOCOL_ID, chainCategory: "evm", description: "Borrow from Euler vault", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
|
|
2230
|
+
{ id: "euler-v2.vault-withdraw", protocolId: EULER_V2_PROTOCOL_ID, chainCategory: "evm", description: "Withdraw from Euler vault", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
|
|
2231
|
+
{ id: "euler-v2.borrow-repay", protocolId: EULER_V2_PROTOCOL_ID, chainCategory: "evm", description: "Repay Euler borrow", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
|
|
2232
|
+
{ id: "euler-v2.collateral-deposit", protocolId: EULER_V2_PROTOCOL_ID, chainCategory: "evm", description: "Deposit borrow collateral", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
|
|
2233
|
+
{ id: "euler-v2.collateral-withdraw", protocolId: EULER_V2_PROTOCOL_ID, chainCategory: "evm", description: "Withdraw borrow collateral", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} }
|
|
2234
|
+
]
|
|
2235
|
+
};
|
|
2236
|
+
registerProtocolModule(eulerV2ProtocolModule);
|
|
2237
|
+
|
|
2238
|
+
exports.EULER_SAME_ASSET_BORROW_APPROVAL_BUFFER_BPS = EULER_SAME_ASSET_BORROW_APPROVAL_BUFFER_BPS;
|
|
2239
|
+
exports.EULER_SAME_ASSET_BORROW_MAX_ROUNDS = EULER_SAME_ASSET_BORROW_MAX_ROUNDS;
|
|
2240
|
+
exports.EULER_SAME_ASSET_BORROW_PROTOCOL_HEADROOM_BPS = EULER_SAME_ASSET_BORROW_PROTOCOL_HEADROOM_BPS;
|
|
2241
|
+
exports.EULER_SAME_ASSET_BORROW_RATIO_STOP_EPS_BPS = EULER_SAME_ASSET_BORROW_RATIO_STOP_EPS_BPS;
|
|
2242
|
+
exports.EULER_V2_ISOLATED_VAULT_DEPOSIT_FALLBACK_GAS = EULER_V2_ISOLATED_VAULT_DEPOSIT_FALLBACK_GAS;
|
|
2243
|
+
exports.EULER_V2_PROTOCOL_ID = EULER_V2_PROTOCOL_ID;
|
|
2244
|
+
exports.EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS = EULER_V2_VAULT_WITHDRAW_FALLBACK_GAS;
|
|
2245
|
+
exports.buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch = buildEvmMultisignBodyEulerV2BorrowCollateralDepositBatch;
|
|
2246
|
+
exports.buildEvmMultisignBodyEulerV2BorrowCollateralWithdrawBatch = buildEvmMultisignBodyEulerV2BorrowCollateralWithdrawBatch;
|
|
2247
|
+
exports.buildEvmMultisignBodyEulerV2BorrowRepayBatch = buildEvmMultisignBodyEulerV2BorrowRepayBatch;
|
|
2248
|
+
exports.buildEvmMultisignBodyEulerV2IsolatedBorrowBatch = buildEvmMultisignBodyEulerV2IsolatedBorrowBatch;
|
|
2249
|
+
exports.buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch = buildEvmMultisignBodyEulerV2IsolatedLendDepositBatch;
|
|
2250
|
+
exports.buildEvmMultisignBodyEulerV2VaultWithdraw = buildEvmMultisignBodyEulerV2VaultWithdraw;
|
|
2251
|
+
exports.clampEulerUnderlyingDecimalsForEulerUi = clampEulerUnderlyingDecimalsForEulerUi;
|
|
2252
|
+
exports.eulerBorrowAndCollateralSameAsset = eulerBorrowAndCollateralSameAsset;
|
|
2253
|
+
exports.eulerSameAssetApproveAmountWithBuffer = eulerSameAssetApproveAmountWithBuffer;
|
|
2254
|
+
exports.eulerSameAssetTotalCollateralPullWei = eulerSameAssetTotalCollateralPullWei;
|
|
2255
|
+
exports.eulerV2ProtocolModule = eulerV2ProtocolModule;
|
|
2256
|
+
exports.fetchEulerBorrowCollateralMaxWithdrawAssetsWei = fetchEulerBorrowCollateralMaxWithdrawAssetsWei;
|
|
2257
|
+
exports.fetchEulerLendEarnVaultEffectiveMaxWithdrawWei = fetchEulerLendEarnVaultEffectiveMaxWithdrawWei;
|
|
2258
|
+
exports.fetchEulerVaultAssetDecimals = fetchEulerVaultAssetDecimals;
|
|
2259
|
+
exports.fetchEulerVaultMaxWithdrawWei = fetchEulerVaultMaxWithdrawWei;
|
|
2260
|
+
exports.fetchEulerVaultUnderlyingMeta = fetchEulerVaultUnderlyingMeta;
|
|
2261
|
+
exports.planSameAssetLeveragedBorrows = planSameAssetLeveragedBorrows;
|
|
2262
|
+
//# sourceMappingURL=index.cjs.map
|
|
2263
|
+
//# sourceMappingURL=index.cjs.map
|