@continuumdao/ctm-mpc-defi 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/agent/catalog.cjs +878 -141
  2. package/dist/agent/catalog.cjs.map +1 -1
  3. package/dist/agent/catalog.d.cts +756 -12
  4. package/dist/agent/catalog.d.ts +756 -12
  5. package/dist/agent/catalog.js +829 -142
  6. package/dist/agent/catalog.js.map +1 -1
  7. package/dist/chains/evm/index.cjs +13 -0
  8. package/dist/chains/evm/index.cjs.map +1 -1
  9. package/dist/chains/evm/index.d.cts +3 -1
  10. package/dist/chains/evm/index.d.ts +3 -1
  11. package/dist/chains/evm/index.js +13 -1
  12. package/dist/chains/evm/index.js.map +1 -1
  13. package/dist/index.cjs +825 -141
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +2 -1
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.js +825 -142
  18. package/dist/index.js.map +1 -1
  19. package/dist/protocols/evm/aave-v4/index.cjs +1987 -0
  20. package/dist/protocols/evm/aave-v4/index.cjs.map +1 -0
  21. package/dist/protocols/evm/aave-v4/index.d.cts +500 -0
  22. package/dist/protocols/evm/aave-v4/index.d.ts +500 -0
  23. package/dist/protocols/evm/aave-v4/index.js +1943 -0
  24. package/dist/protocols/evm/aave-v4/index.js.map +1 -0
  25. package/dist/protocols/evm/ethena/index.cjs +965 -0
  26. package/dist/protocols/evm/ethena/index.cjs.map +1 -0
  27. package/dist/protocols/evm/ethena/index.d.cts +161 -0
  28. package/dist/protocols/evm/ethena/index.d.ts +161 -0
  29. package/dist/protocols/evm/ethena/index.js +943 -0
  30. package/dist/protocols/evm/ethena/index.js.map +1 -0
  31. package/dist/protocols/evm/euler-v2/index.cjs +2263 -0
  32. package/dist/protocols/evm/euler-v2/index.cjs.map +1 -0
  33. package/dist/protocols/evm/euler-v2/index.d.cts +317 -0
  34. package/dist/protocols/evm/euler-v2/index.d.ts +317 -0
  35. package/dist/protocols/evm/euler-v2/index.js +2238 -0
  36. package/dist/protocols/evm/euler-v2/index.js.map +1 -0
  37. package/dist/protocols/evm/lido/index.cjs +834 -0
  38. package/dist/protocols/evm/lido/index.cjs.map +1 -0
  39. package/dist/protocols/evm/lido/index.d.cts +120 -0
  40. package/dist/protocols/evm/lido/index.d.ts +120 -0
  41. package/dist/protocols/evm/lido/index.js +809 -0
  42. package/dist/protocols/evm/lido/index.js.map +1 -0
  43. package/dist/protocols/evm/maple/index.cjs +707 -0
  44. package/dist/protocols/evm/maple/index.cjs.map +1 -0
  45. package/dist/protocols/evm/maple/index.d.cts +109 -0
  46. package/dist/protocols/evm/maple/index.d.ts +109 -0
  47. package/dist/protocols/evm/maple/index.js +693 -0
  48. package/dist/protocols/evm/maple/index.js.map +1 -0
  49. package/dist/protocols/evm/sky/index.cjs +1254 -0
  50. package/dist/protocols/evm/sky/index.cjs.map +1 -0
  51. package/dist/protocols/evm/sky/index.d.cts +218 -0
  52. package/dist/protocols/evm/sky/index.d.ts +218 -0
  53. package/dist/protocols/evm/sky/index.js +1229 -0
  54. package/dist/protocols/evm/sky/index.js.map +1 -0
  55. package/package.json +37 -3
@@ -0,0 +1,1229 @@
1
+ import { parseAbi, parseGwei, serializeTransaction, keccak256, defineChain, createPublicClient, http, getAddress, parseUnits, encodeFunctionData, decodeFunctionData, formatUnits } from 'viem';
2
+
3
+ // src/core/registry.ts
4
+ var modules = [];
5
+ function registerProtocolModule(mod) {
6
+ const existing = modules.findIndex((m) => m.id === mod.id);
7
+ if (existing >= 0) {
8
+ modules[existing] = mod;
9
+ } else {
10
+ modules.push(mod);
11
+ }
12
+ }
13
+
14
+ // src/protocols/evm/sky/mainnet.ts
15
+ var SKY_ETHEREUM_MAINNET = "0x56072C95FAA701256059aa122697B133aDEd9279";
16
+ var SKY_ETHEREUM_MAINNET_CHAIN_ID = 1;
17
+ var USDS_ETHEREUM_MAINNET = "0xdC035D45d973E3EC169d2276DDab16f1e407384F";
18
+ var SUSDS_VAULT_ERC4626_MAINNET = "0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD";
19
+ var SKY_LOCKSTAKE_ENGINE_MAINNET = "0xCe01C90dE7FD1bcFa39e237FE6D8D9F569e8A6a3";
20
+ var SKY_LS_SKY_ERC20_MAINNET = "0xf9A9cfD3229E985B91F99Bc866d42938044FFa1C";
21
+ var SKY_LOCKSTAKE_DEFAULT_FARM_MAINNET = "0xB44C2Fb4181D7Cb06bdFf34A46FdFe4a259B40Fc";
22
+ var SKY_LOCKSTAKE_DEFAULT_FARM_REF = 1;
23
+ var SKY_LOCKSTAKE_UI_COLLATERAL_FACTOR = 0.65;
24
+ var SKY_TOKEN_DECIMALS = 18;
25
+ var USDS_DECIMALS = 18;
26
+ async function fetchChainFeeParams(rpcUrl, chainId) {
27
+ const url = rpcUrl.trim();
28
+ if (!url) return { isEip1559: false };
29
+ const chainIdNum = typeof chainId === "string" ? parseInt(chainId, 10) : chainId;
30
+ if (Number.isNaN(chainIdNum)) return { isEip1559: false };
31
+ const chain = defineChain({
32
+ id: chainIdNum,
33
+ name: "Discovery",
34
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
35
+ rpcUrls: { default: { http: [url] } }
36
+ });
37
+ const publicClient = createPublicClient({
38
+ chain,
39
+ transport: http(url)
40
+ });
41
+ const getGasPriceGwei = async () => {
42
+ const gasPriceWei = await publicClient.getGasPrice();
43
+ return parseFloat(formatUnits(gasPriceWei, 9));
44
+ };
45
+ try {
46
+ const block = await publicClient.getBlock({ blockTag: "latest" });
47
+ const baseFeePerGas = block?.baseFeePerGas;
48
+ if (baseFeePerGas == null || baseFeePerGas === void 0) {
49
+ const gasPriceGwei2 = await getGasPriceGwei();
50
+ return { isEip1559: false, gasPriceGwei: gasPriceGwei2 };
51
+ }
52
+ const baseFeeGwei = parseFloat(formatUnits(baseFeePerGas, 9));
53
+ let priorityFeeGwei;
54
+ try {
55
+ const priorityWei = await publicClient.estimateMaxPriorityFeePerGas();
56
+ priorityFeeGwei = parseFloat(formatUnits(priorityWei, 9));
57
+ } catch {
58
+ }
59
+ const gasPriceGwei = await getGasPriceGwei();
60
+ return {
61
+ isEip1559: true,
62
+ baseFeeGwei,
63
+ priorityFeeGwei,
64
+ gasPriceGwei
65
+ };
66
+ } catch {
67
+ try {
68
+ const gasPriceWei = await publicClient.getGasPrice();
69
+ const gasPriceGwei = parseFloat(formatUnits(gasPriceWei, 9));
70
+ return { isEip1559: false, gasPriceGwei };
71
+ } catch {
72
+ return { isEip1559: false };
73
+ }
74
+ }
75
+ }
76
+ function finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, floor, baseWei) {
77
+ let maxP = maxPriorityFeePerGas;
78
+ let maxF = maxFeePerGas;
79
+ if (baseWei > 0n && maxF < baseWei + maxP) {
80
+ maxF = baseWei + maxP + parseGwei("0.001");
81
+ }
82
+ if (maxF < maxP) {
83
+ maxF = baseWei > 0n ? baseWei + maxP + parseGwei("0.001") : maxP * 2n;
84
+ }
85
+ return { maxFeePerGas: maxF, maxPriorityFeePerGas: maxP };
86
+ }
87
+ function alignEip1559FeesWithLatestBase(maxFeePerGas, maxPriorityFeePerGas, latestBlockBaseFeeWei) {
88
+ return finalizeEip1559Fees(maxFeePerGas, maxPriorityFeePerGas, null, latestBlockBaseFeeWei);
89
+ }
90
+
91
+ // src/chains/evm/txParams.ts
92
+ function gasLimitFromEstimateAndChainConfig(estimatedGas, chainGasLimit) {
93
+ if (chainGasLimit == null || !Number.isFinite(chainGasLimit) || chainGasLimit <= 0) {
94
+ return estimatedGas;
95
+ }
96
+ const cfg = BigInt(Math.floor(chainGasLimit));
97
+ return cfg > estimatedGas ? cfg : estimatedGas;
98
+ }
99
+
100
+ // src/core/keygen.ts
101
+ function firstClientIdFromKeyGen(data) {
102
+ const map = data?.ClientKeys;
103
+ if (!map || typeof map !== "object") return null;
104
+ for (const v of Object.values(map)) {
105
+ if (typeof v === "string" && v.trim()) return v.trim();
106
+ }
107
+ return null;
108
+ }
109
+
110
+ // src/protocols/evm/sky/lockstakeMultisign.ts
111
+ var SKY_LOCKSTAKE_MULTICALL_FALLBACK_GAS = 5000000n;
112
+ var erc20AllowanceAbi = parseAbi([
113
+ "function allowance(address owner, address spender) view returns (uint256)",
114
+ "function decimals() view returns (uint8)"
115
+ ]);
116
+ var erc20ApproveAbi = parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
117
+ var engineAbi = parseAbi([
118
+ "function multicall(bytes[] data) returns (bytes[])",
119
+ "function open(uint256 index) returns (address)",
120
+ "function selectFarm(address owner, uint256 index, address farm, uint16 ref)",
121
+ /** Canonical SKY collateral (pulls ERC-20 `sky()`, then `mkrSky.skyToMkr`). Legacy `lock` pulls MKR only. */
122
+ "function lockSky(address owner, uint256 index, uint256 skyWad, uint16 ref)",
123
+ "function draw(address owner, uint256 index, address to, uint256 wad)",
124
+ "function wipe(address owner, uint256 index, uint256 wad)",
125
+ "function wipeAll(address owner, uint256 index) returns (uint256 wad)",
126
+ /** Returns withdrawn SKY; legacy `free` returns MKR after fee-burn. */
127
+ "function freeSky(address owner, uint256 index, address to, uint256 skyWad) returns (uint256 skyFreed)",
128
+ "function getReward(address owner, uint256 index, address farm, address to) returns (uint256)",
129
+ "function ownerUrnsCount(address usr) view returns (uint256)",
130
+ "function ownerUrns(address owner, uint256 index) view returns (address urn)",
131
+ "function urnFarms(address urn) view returns (address farm)",
132
+ "function farms(address farm) view returns (uint8)"
133
+ ]);
134
+ var stakingRewardsFarmReadAbi = parseAbi([
135
+ "function stakingToken() view returns (address)",
136
+ "function rewardsToken() view returns (address)"
137
+ ]);
138
+ function gweiToDecimalString(n) {
139
+ if (!Number.isFinite(n)) return "0";
140
+ if (n === 0) return "0";
141
+ const s = String(n);
142
+ if (s.indexOf("e") !== -1 || s.indexOf("E") !== -1) return n.toFixed(9).replace(/\.?0+$/, "") || "0";
143
+ return s;
144
+ }
145
+ async function appendSerializedSkyBatch(args) {
146
+ const ph = (args.keyGen.pubkeyhex ?? "").trim();
147
+ if (!ph) throw new Error("keyGen pubKey (pubkeyhex) is required");
148
+ const keyList = args.keyGen.keylist ?? [];
149
+ const clientId = firstClientIdFromKeyGen(args.keyGen);
150
+ const feeParams = await fetchChainFeeParams(args.rpcUrl, args.chainId);
151
+ const legacy = Boolean(args.chainDetail?.legacy) || !feeParams.isEip1559;
152
+ const latestBaseFeeWei = !legacy ? (await args.publicClient.getBlock({ blockTag: "latest" })).baseFeePerGas ?? 0n : 0n;
153
+ const useCustomGas = args.useCustomGas;
154
+ const gasLimitConfig = useCustomGas && args.chainDetail?.gasLimit != null ? Number(args.chainDetail.gasLimit) : void 0;
155
+ const gasFeeMultiplier = useCustomGas && args.chainDetail?.gasMultiplier != null ? Number(args.chainDetail.gasMultiplier) : void 0;
156
+ const baseNonce = await args.publicClient.getTransactionCount({ address: args.executor, blockTag: "pending" });
157
+ const messageHashes = [];
158
+ const messageRawBatch = [];
159
+ const proposalTxParamsBatch = [];
160
+ let firstTxFeePayload = {};
161
+ let firstDataNo0x = "";
162
+ for (let i = 0; i < args.steps.length; i++) {
163
+ const s = args.steps[i];
164
+ const currentNonce = baseNonce + i;
165
+ let estimatedGas;
166
+ try {
167
+ estimatedGas = await args.publicClient.estimateGas({
168
+ to: s.to,
169
+ data: s.data,
170
+ value: s.value,
171
+ account: args.executor
172
+ });
173
+ } catch {
174
+ estimatedGas = SKY_LOCKSTAKE_MULTICALL_FALLBACK_GAS;
175
+ }
176
+ const gasLimitI = useCustomGas ? gasLimitFromEstimateAndChainConfig(estimatedGas, gasLimitConfig) : estimatedGas;
177
+ if (legacy) {
178
+ let gasPriceWei = await args.publicClient.getGasPrice();
179
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
180
+ gasPriceWei = gasPriceWei * BigInt(100 + gasFeeMultiplier) / 100n;
181
+ }
182
+ if (useCustomGas && args.chainDetail?.gasPrice != null && args.chainDetail.gasPrice > 0) {
183
+ const configured = parseGwei(gweiToDecimalString(Number(args.chainDetail.gasPrice)));
184
+ if (configured > gasPriceWei) gasPriceWei = configured;
185
+ }
186
+ const ser = serializeTransaction({
187
+ type: "legacy",
188
+ to: s.to,
189
+ data: s.data,
190
+ value: s.value,
191
+ gas: gasLimitI,
192
+ gasPrice: gasPriceWei,
193
+ nonce: currentNonce,
194
+ chainId: args.chainId
195
+ });
196
+ const h = keccak256(ser);
197
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
198
+ messageRawBatch.push(ser);
199
+ proposalTxParamsBatch.push({
200
+ nonce: currentNonce,
201
+ gasLimit: gasLimitI.toString(),
202
+ txType: "legacy",
203
+ gasPrice: gasPriceWei.toString()
204
+ });
205
+ if (i === 0) {
206
+ firstTxFeePayload = { txNonce: currentNonce, txGasLimit: gasLimitI.toString(), txGasPrice: gasPriceWei.toString() };
207
+ firstDataNo0x = s.data.startsWith("0x") ? s.data.slice(2) : s.data;
208
+ }
209
+ } else {
210
+ const fetchedBase = feeParams.baseFeeGwei ?? 0;
211
+ const fetchedPriority = feeParams.priorityFeeGwei ?? 0;
212
+ const configuredBase = useCustomGas && args.chainDetail?.baseFee != null ? Number(args.chainDetail.baseFee) : 0;
213
+ const configuredPriority = useCustomGas && args.chainDetail?.priorityFee != null ? Number(args.chainDetail.priorityFee) : 0;
214
+ const effectiveBaseFeeGwei = Math.max(fetchedBase, configuredBase);
215
+ const effectivePriorityFeeGwei = Math.max(fetchedPriority, configuredPriority);
216
+ const baseFeeMultiplierPct = useCustomGas && args.chainDetail?.baseFeeMultiplier != null ? Math.max(100, Number(args.chainDetail.baseFeeMultiplier)) : 100;
217
+ const baseComponentGwei = effectiveBaseFeeGwei * baseFeeMultiplierPct / 100;
218
+ const maxFeePerGasGwei = baseComponentGwei + effectivePriorityFeeGwei;
219
+ let maxPriorityFeePerGas = effectivePriorityFeeGwei > 0 ? parseGwei(gweiToDecimalString(effectivePriorityFeeGwei)) : parseGwei("1");
220
+ let maxFeePerGas = parseGwei(gweiToDecimalString(maxFeePerGasGwei));
221
+ if (useCustomGas && gasFeeMultiplier != null && gasFeeMultiplier > 0) {
222
+ maxPriorityFeePerGas = maxPriorityFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
223
+ maxFeePerGas = maxFeePerGas * BigInt(100 + gasFeeMultiplier) / 100n;
224
+ }
225
+ ({ maxFeePerGas, maxPriorityFeePerGas } = alignEip1559FeesWithLatestBase(
226
+ maxFeePerGas,
227
+ maxPriorityFeePerGas,
228
+ latestBaseFeeWei
229
+ ));
230
+ const ser = serializeTransaction({
231
+ type: "eip1559",
232
+ to: s.to,
233
+ data: s.data,
234
+ value: s.value,
235
+ gas: gasLimitI,
236
+ maxFeePerGas,
237
+ maxPriorityFeePerGas,
238
+ nonce: currentNonce,
239
+ chainId: args.chainId
240
+ });
241
+ const h = keccak256(ser);
242
+ messageHashes.push(h.startsWith("0x") ? h.slice(2) : h);
243
+ messageRawBatch.push(ser);
244
+ proposalTxParamsBatch.push({
245
+ nonce: currentNonce,
246
+ gasLimit: gasLimitI.toString(),
247
+ txType: "eip1559",
248
+ maxFeePerGas: maxFeePerGas.toString(),
249
+ maxPriorityFeePerGas: maxPriorityFeePerGas.toString()
250
+ });
251
+ if (i === 0) {
252
+ firstTxFeePayload = {
253
+ txNonce: currentNonce,
254
+ txGasLimit: gasLimitI.toString(),
255
+ txMaxFeePerGas: maxFeePerGas.toString(),
256
+ txMaxPriorityFeePerGas: maxPriorityFeePerGas.toString()
257
+ };
258
+ firstDataNo0x = s.data.startsWith("0x") ? s.data.slice(2) : s.data;
259
+ }
260
+ }
261
+ }
262
+ const batchMeta = args.steps.map((s) => s.meta);
263
+ const extraPayload = { batchMeta };
264
+ if (useCustomGas && args.customGasChainDetails && Object.keys(args.customGasChainDetails).length > 0) {
265
+ extraPayload.customGasChainDetails = args.customGasChainDetails;
266
+ }
267
+ const extraJSON = JSON.stringify(extraPayload);
268
+ const firstSigText = batchMeta[0].signatureText;
269
+ const bodyForSign = {
270
+ keyList,
271
+ pubKey: ph,
272
+ msgHash: messageHashes[0],
273
+ msgRaw: firstDataNo0x,
274
+ messageHashes,
275
+ messageRawBatch,
276
+ destinationChainID: args.destinationChainID,
277
+ destinationAddress: args.steps[0].to,
278
+ extraJSON,
279
+ signatureText: firstSigText,
280
+ purpose: args.purposePrefix,
281
+ ...firstTxFeePayload,
282
+ proposalTxParams: proposalTxParamsBatch
283
+ };
284
+ if (clientId) bodyForSign.clientId = clientId;
285
+ return { bodyForSign, messageToSign: JSON.stringify(bodyForSign) };
286
+ }
287
+ async function buildSkyLockstakeStakePositionBatch(args) {
288
+ if (args.chainId !== 1) throw new Error("Sky LockStake is only supported on Ethereum mainnet (chain id 1).");
289
+ const rpc = args.rpcUrl.trim();
290
+ const ch = defineChain({
291
+ id: 1,
292
+ name: "Ethereum",
293
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
294
+ rpcUrls: { default: { http: [rpc] } }
295
+ });
296
+ const publicClient = createPublicClient({ chain: ch, transport: http(rpc) });
297
+ const executor = getAddress(args.executorAddress);
298
+ const sky = getAddress(SKY_ETHEREUM_MAINNET);
299
+ const engine = getAddress(SKY_LOCKSTAKE_ENGINE_MAINNET);
300
+ const farm = getAddress(args.farm ?? SKY_LOCKSTAKE_DEFAULT_FARM_MAINNET);
301
+ const ref = Number(args.farmRef ?? SKY_LOCKSTAKE_DEFAULT_FARM_REF);
302
+ if (!Number.isFinite(ref) || ref < 0 || ref > 65535) throw new Error("Invalid farm ref.");
303
+ const dec = await publicClient.readContract({
304
+ address: sky,
305
+ abi: erc20AllowanceAbi,
306
+ functionName: "decimals"
307
+ });
308
+ const amountWei = parseUnits(args.amountHuman, Number(dec));
309
+ if (amountWei === 0n) throw new Error("Amount is zero.");
310
+ const urnCount = await publicClient.readContract({
311
+ address: engine,
312
+ abi: engineAbi,
313
+ functionName: "ownerUrnsCount",
314
+ args: [executor]
315
+ });
316
+ let effectiveExistingUrnIndex = args.existingUrnIndex;
317
+ if (effectiveExistingUrnIndex === void 0 && urnCount > 0n) {
318
+ effectiveExistingUrnIndex = 0n;
319
+ }
320
+ const isAddCollateral = effectiveExistingUrnIndex !== void 0;
321
+ const index = isAddCollateral ? effectiveExistingUrnIndex : urnCount;
322
+ let addCollateralUrn = null;
323
+ if (isAddCollateral) {
324
+ if (index < 0n || index >= urnCount) {
325
+ throw new Error("Sky LockStake: urn index out of range for this key (use Staking tab if you have no position yet).");
326
+ }
327
+ const urnAddr = await publicClient.readContract({
328
+ address: engine,
329
+ abi: engineAbi,
330
+ functionName: "ownerUrns",
331
+ args: [executor, index]
332
+ });
333
+ if (!urnAddr || getAddress(urnAddr) === "0x0000000000000000000000000000000000000000") {
334
+ throw new Error("Sky LockStake: that urn does not exist on-chain.");
335
+ }
336
+ addCollateralUrn = getAddress(urnAddr);
337
+ }
338
+ const lsSky = getAddress(SKY_LS_SKY_ERC20_MAINNET);
339
+ const farmStatus = await publicClient.readContract({
340
+ address: engine,
341
+ abi: engineAbi,
342
+ functionName: "farms",
343
+ args: [farm]
344
+ });
345
+ if (Number(farmStatus) !== 1) {
346
+ throw new Error(
347
+ `Sky LockStake: farm ${farm} is not ACTIVE in the engine (on-chain status ${farmStatus}). Choose another farm or wait for governance.`
348
+ );
349
+ }
350
+ let farmStakeToken;
351
+ try {
352
+ farmStakeToken = getAddress(
353
+ await publicClient.readContract({
354
+ address: farm,
355
+ abi: stakingRewardsFarmReadAbi,
356
+ functionName: "stakingToken"
357
+ })
358
+ );
359
+ } catch {
360
+ throw new Error(`Sky LockStake: could not read stakingToken() from farm ${farm}.`);
361
+ }
362
+ const farmAcceptsLsSky = farmStakeToken.toLowerCase() === lsSky.toLowerCase();
363
+ const skipSelectFarm = !farmAcceptsLsSky;
364
+ const openCall = encodeFunctionData({
365
+ abi: engineAbi,
366
+ functionName: "open",
367
+ args: [index]
368
+ });
369
+ const selectFarmCall = encodeFunctionData({
370
+ abi: engineAbi,
371
+ functionName: "selectFarm",
372
+ args: [executor, index, farm, ref]
373
+ });
374
+ const lockSkyCall = encodeFunctionData({
375
+ abi: engineAbi,
376
+ functionName: "lockSky",
377
+ args: [executor, index, amountWei, ref]
378
+ });
379
+ let innerCalls;
380
+ let innerLabels;
381
+ if (!isAddCollateral) {
382
+ innerCalls = skipSelectFarm ? [openCall, lockSkyCall] : [openCall, selectFarmCall, lockSkyCall];
383
+ innerLabels = skipSelectFarm ? ["open(index)", "lockSky"] : ["open(index)", "selectFarm", "lockSky"];
384
+ } else {
385
+ const currentFarm = await publicClient.readContract({
386
+ address: engine,
387
+ abi: engineAbi,
388
+ functionName: "urnFarms",
389
+ args: [addCollateralUrn]
390
+ });
391
+ const targetFarm = getAddress(farm);
392
+ const currentNorm = getAddress(currentFarm);
393
+ const sameFarmAsTarget = currentNorm.toLowerCase() === targetFarm.toLowerCase();
394
+ if (skipSelectFarm || sameFarmAsTarget) {
395
+ innerCalls = [lockSkyCall];
396
+ innerLabels = ["lockSky"];
397
+ } else {
398
+ innerCalls = [selectFarmCall, lockSkyCall];
399
+ innerLabels = ["selectFarm", "lockSky"];
400
+ }
401
+ }
402
+ const last = innerCalls[innerCalls.length - 1];
403
+ const lockDecoded = decodeFunctionData({ abi: engineAbi, data: last });
404
+ if (lockDecoded.functionName !== "lockSky" || lockDecoded.args[2] !== amountWei || lockDecoded.args[3] !== ref) {
405
+ throw new Error("Sky LockStake: internal multicall encoding mismatch (lockSky tail); consult support.");
406
+ }
407
+ for (let i = 0; i < innerCalls.length - 1; i++) {
408
+ const d = decodeFunctionData({ abi: engineAbi, data: innerCalls[i] });
409
+ if (d.functionName === "open" && d.args[0] !== index) {
410
+ throw new Error("Sky LockStake: internal multicall encoding mismatch (open index); consult support.");
411
+ }
412
+ if (d.functionName === "selectFarm") {
413
+ if (d.args[3] !== ref || d.args[2] !== getAddress(farm) || d.args[1] !== index || d.args[0] !== executor) {
414
+ throw new Error("Sky LockStake: internal multicall encoding mismatch (selectFarm); consult support.");
415
+ }
416
+ }
417
+ }
418
+ const multicallData = encodeFunctionData({
419
+ abi: engineAbi,
420
+ functionName: "multicall",
421
+ args: [innerCalls]
422
+ });
423
+ const steps = [];
424
+ const currentAllowance = await publicClient.readContract({
425
+ address: sky,
426
+ abi: erc20AllowanceAbi,
427
+ functionName: "allowance",
428
+ args: [executor, engine]
429
+ });
430
+ if (currentAllowance < amountWei) {
431
+ if (currentAllowance > 0n) {
432
+ steps.push({
433
+ to: sky,
434
+ data: encodeFunctionData({
435
+ abi: erc20ApproveAbi,
436
+ functionName: "approve",
437
+ args: [engine, 0n]
438
+ }),
439
+ value: 0n,
440
+ meta: {
441
+ destinationAddress: sky,
442
+ signatureText: JSON.stringify({
443
+ kind: "Sky",
444
+ name: "ERC20.approve",
445
+ function: "approve(address,uint256)",
446
+ spender: engine,
447
+ note: "Reset SKY allowance before new approval."
448
+ }),
449
+ evm: { type: "sky_lockstake_erc20_approve", version: 1, chainId: "1" },
450
+ sky: { step: "approve_sky_reset", engine }
451
+ }
452
+ });
453
+ }
454
+ steps.push({
455
+ to: sky,
456
+ data: encodeFunctionData({
457
+ abi: erc20ApproveAbi,
458
+ functionName: "approve",
459
+ args: [engine, amountWei]
460
+ }),
461
+ value: 0n,
462
+ meta: {
463
+ destinationAddress: sky,
464
+ signatureText: JSON.stringify({
465
+ kind: "Sky",
466
+ name: "ERC20.approve",
467
+ function: "approve(address,uint256)",
468
+ spender: engine,
469
+ amountWei: amountWei.toString(),
470
+ amountHuman: args.amountHuman,
471
+ decimals: Number(dec)
472
+ }),
473
+ evm: { type: "sky_lockstake_erc20_approve", version: 1, chainId: "1" },
474
+ sky: { step: "approve_sky", engine }
475
+ }
476
+ });
477
+ }
478
+ steps.push({
479
+ to: engine,
480
+ data: multicallData,
481
+ value: 0n,
482
+ meta: {
483
+ destinationAddress: engine,
484
+ signatureText: JSON.stringify({
485
+ kind: "Sky",
486
+ name: "LockstakeEngine.multicall",
487
+ inner: innerLabels,
488
+ index: index.toString(),
489
+ farm,
490
+ ref,
491
+ amountWei: amountWei.toString(),
492
+ amountHuman: args.amountHuman,
493
+ borrowOnlySkippedRewardsFarm: skipSelectFarm,
494
+ farmStakingToken: farmStakeToken,
495
+ expectedEngineFarmStakingToken: lsSky,
496
+ addCollateralToExistingUrn: isAddCollateral
497
+ }),
498
+ evm: { type: "sky_lockstake_engine_multicall_stake", version: 1, chainId: "1" },
499
+ sky: {
500
+ step: isAddCollateral ? innerLabels.join("_") : skipSelectFarm ? "open_lockSky_borrow_only" : "open_selectFarm_lockSky",
501
+ engine,
502
+ farm,
503
+ skipSelectFarm,
504
+ addCollateral: isAddCollateral
505
+ }
506
+ }
507
+ });
508
+ const purposePrefix = (() => {
509
+ const t = args.purposeText.trim();
510
+ const borrowNote = skipSelectFarm ? "No rewards farm in this batch: default farm stakingToken does not match lsSKY, so we only open + lockSky (SKY collateral) for borrow (you can select a compatible farm later from the protocol UI when one is ACTIVE)." : "";
511
+ if (isAddCollateral) {
512
+ const core = steps.length > 1 ? `Sky LockStake: ${steps.length}-tx batch \u2014 approve SKY, then multicall ${innerLabels.join(" + ")} on urn index ${index.toString()}.` : `Sky LockStake: multicall ${innerLabels.join(" + ")} on urn index ${index.toString()} (allowance already set).`;
513
+ return (t ? `${t}
514
+
515
+ ` : "") + core;
516
+ }
517
+ const suffixCore = skipSelectFarm ? steps.length > 1 ? `Sky LockStake: ${steps.length}-tx batch \u2014 approve SKY, then multicall open + lockSky (${borrowNote})` : `Sky LockStake: multicall open + lockSky (${borrowNote})` : steps.length > 1 ? `Sky LockStake: ${steps.length}-tx batch \u2014 approve SKY, then open + farm + lockSky on Ethereum.` : `Sky LockStake: multicall open + farm + lockSky on Ethereum (allowance already set).`;
518
+ return (t ? `${t}
519
+
520
+ ` : "") + suffixCore;
521
+ })();
522
+ return appendSerializedSkyBatch({
523
+ steps,
524
+ publicClient,
525
+ executor,
526
+ chainId: 1,
527
+ rpcUrl: rpc,
528
+ chainDetail: args.chainDetail,
529
+ useCustomGas: args.useCustomGas,
530
+ customGasChainDetails: args.customGasChainDetails,
531
+ purposePrefix,
532
+ keyGen: args.keyGen,
533
+ destinationChainID: "1"
534
+ });
535
+ }
536
+ async function buildSkyLockstakeDrawBatch(args) {
537
+ if (args.chainId !== 1) throw new Error("Sky LockStake is only supported on Ethereum mainnet (chain id 1).");
538
+ const rpc = args.rpcUrl.trim();
539
+ const ch = defineChain({
540
+ id: 1,
541
+ name: "Ethereum",
542
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
543
+ rpcUrls: { default: { http: [rpc] } }
544
+ });
545
+ const publicClient = createPublicClient({ chain: ch, transport: http(rpc) });
546
+ const executor = getAddress(args.executorAddress);
547
+ const engine = getAddress(SKY_LOCKSTAKE_ENGINE_MAINNET);
548
+ const usds = getAddress(USDS_ETHEREUM_MAINNET);
549
+ const dec = await publicClient.readContract({
550
+ address: usds,
551
+ abi: erc20AllowanceAbi,
552
+ functionName: "decimals"
553
+ });
554
+ const wad = parseUnits(args.amountHuman, Number(dec));
555
+ if (wad === 0n) throw new Error("Borrow amount is zero.");
556
+ const to = getAddress(args.to ?? executor);
557
+ const data = encodeFunctionData({
558
+ abi: engineAbi,
559
+ functionName: "draw",
560
+ args: [executor, args.urnIndex, to, wad]
561
+ });
562
+ const steps = [
563
+ {
564
+ to: engine,
565
+ data,
566
+ value: 0n,
567
+ meta: {
568
+ destinationAddress: engine,
569
+ signatureText: JSON.stringify({
570
+ kind: "Sky",
571
+ name: "LockstakeEngine.draw",
572
+ function: "draw(address owner,uint256 index,address to,uint256 wad)",
573
+ urnIndex: args.urnIndex.toString(),
574
+ to,
575
+ amountWei: wad.toString(),
576
+ amountHuman: args.amountHuman
577
+ }),
578
+ evm: { type: "sky_lockstake_draw", version: 1, chainId: "1" },
579
+ sky: { step: "draw", engine }
580
+ }
581
+ }
582
+ ];
583
+ const purposePrefix = (() => {
584
+ const t = args.purposeText.trim();
585
+ const suffix = "Sky LockStake: borrow USDS against staked SKY (draw).";
586
+ return (t ? `${t}
587
+
588
+ ` : "") + suffix;
589
+ })();
590
+ return appendSerializedSkyBatch({
591
+ steps,
592
+ publicClient,
593
+ executor,
594
+ chainId: 1,
595
+ rpcUrl: rpc,
596
+ chainDetail: args.chainDetail,
597
+ useCustomGas: args.useCustomGas,
598
+ customGasChainDetails: args.customGasChainDetails,
599
+ purposePrefix,
600
+ keyGen: args.keyGen,
601
+ destinationChainID: "1"
602
+ });
603
+ }
604
+ async function buildSkyLockstakeGetRewardBatch(args) {
605
+ if (args.chainId !== 1) throw new Error("Sky LockStake is only supported on Ethereum mainnet (chain id 1).");
606
+ const rpc = args.rpcUrl.trim();
607
+ const ch = defineChain({
608
+ id: 1,
609
+ name: "Ethereum",
610
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
611
+ rpcUrls: { default: { http: [rpc] } }
612
+ });
613
+ const publicClient = createPublicClient({ chain: ch, transport: http(rpc) });
614
+ const executor = getAddress(args.executorAddress);
615
+ const engine = getAddress(SKY_LOCKSTAKE_ENGINE_MAINNET);
616
+ const farm = getAddress(args.farmAddress);
617
+ const zero = "0x0000000000000000000000000000000000000000";
618
+ if (farm.toLowerCase() === zero.toLowerCase()) {
619
+ throw new Error(
620
+ "Sky LockStake: this urn has no rewards farm selected \u2014 stake SKY with a farm first, then rewards accrue there."
621
+ );
622
+ }
623
+ let rewardToken;
624
+ try {
625
+ rewardToken = getAddress(
626
+ await publicClient.readContract({
627
+ address: farm,
628
+ abi: stakingRewardsFarmReadAbi,
629
+ functionName: "rewardsToken"
630
+ })
631
+ );
632
+ } catch {
633
+ throw new Error(`Sky LockStake: could not read rewardsToken() from farm ${farm}.`);
634
+ }
635
+ const to = getAddress(args.rewardRecipient ?? executor);
636
+ const data = encodeFunctionData({
637
+ abi: engineAbi,
638
+ functionName: "getReward",
639
+ args: [executor, args.urnIndex, farm, to]
640
+ });
641
+ const steps = [
642
+ {
643
+ to: engine,
644
+ data,
645
+ value: 0n,
646
+ meta: {
647
+ destinationAddress: engine,
648
+ signatureText: JSON.stringify({
649
+ kind: "Sky",
650
+ name: "LockstakeEngine.getReward",
651
+ function: "getReward(address owner,uint256 index,address farm,address to)",
652
+ urnIndex: args.urnIndex.toString(),
653
+ farm,
654
+ rewardRecipient: to,
655
+ farmRewardsToken: rewardToken
656
+ }),
657
+ evm: { type: "sky_lockstake_get_reward", version: 1, chainId: "1" },
658
+ sky: { step: "get_reward", engine, farm, rewardToken }
659
+ }
660
+ }
661
+ ];
662
+ const purposePrefix = (() => {
663
+ const t = args.purposeText.trim();
664
+ const suffix = "Sky LockStake: claim staking rewards from the selected farm (getReward \u2192 ERC-20 sent to rewardRecipient).";
665
+ return (t ? `${t}
666
+
667
+ ` : "") + suffix;
668
+ })();
669
+ const built = await appendSerializedSkyBatch({
670
+ steps,
671
+ publicClient,
672
+ executor,
673
+ chainId: 1,
674
+ rpcUrl: rpc,
675
+ chainDetail: args.chainDetail,
676
+ useCustomGas: args.useCustomGas,
677
+ customGasChainDetails: args.customGasChainDetails,
678
+ purposePrefix,
679
+ keyGen: args.keyGen,
680
+ destinationChainID: "1"
681
+ });
682
+ return { ...built, rewardToken };
683
+ }
684
+ async function buildSkyLockstakeWipeBatch(args) {
685
+ if (args.chainId !== 1) throw new Error("Sky LockStake is only supported on Ethereum mainnet (chain id 1).");
686
+ const rpc = args.rpcUrl.trim();
687
+ const ch = defineChain({
688
+ id: 1,
689
+ name: "Ethereum",
690
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
691
+ rpcUrls: { default: { http: [rpc] } }
692
+ });
693
+ const publicClient = createPublicClient({ chain: ch, transport: http(rpc) });
694
+ const executor = getAddress(args.executorAddress);
695
+ const engine = getAddress(SKY_LOCKSTAKE_ENGINE_MAINNET);
696
+ const usds = getAddress(USDS_ETHEREUM_MAINNET);
697
+ const steps = [];
698
+ if (!args.wipeAll) {
699
+ const ah = (args.amountHuman ?? "").trim();
700
+ if (!ah) throw new Error("Repay amount required.");
701
+ const dec = await publicClient.readContract({
702
+ address: usds,
703
+ abi: erc20AllowanceAbi,
704
+ functionName: "decimals"
705
+ });
706
+ const repayWei = parseUnits(ah, Number(dec));
707
+ if (repayWei === 0n) throw new Error("Repay amount is zero.");
708
+ const currentAllowance = await publicClient.readContract({
709
+ address: usds,
710
+ abi: erc20AllowanceAbi,
711
+ functionName: "allowance",
712
+ args: [executor, engine]
713
+ });
714
+ if (currentAllowance < repayWei) {
715
+ if (currentAllowance > 0n) {
716
+ steps.push({
717
+ to: usds,
718
+ data: encodeFunctionData({
719
+ abi: erc20ApproveAbi,
720
+ functionName: "approve",
721
+ args: [engine, 0n]
722
+ }),
723
+ value: 0n,
724
+ meta: {
725
+ destinationAddress: usds,
726
+ signatureText: JSON.stringify({
727
+ kind: "Sky",
728
+ name: "ERC20.approve USDS reset",
729
+ spender: engine
730
+ }),
731
+ evm: { type: "sky_lockstake_usds_approve", version: 1, chainId: "1" },
732
+ sky: { step: "approve_usds_reset", engine }
733
+ }
734
+ });
735
+ }
736
+ steps.push({
737
+ to: usds,
738
+ data: encodeFunctionData({
739
+ abi: erc20ApproveAbi,
740
+ functionName: "approve",
741
+ args: [engine, repayWei]
742
+ }),
743
+ value: 0n,
744
+ meta: {
745
+ destinationAddress: usds,
746
+ signatureText: JSON.stringify({
747
+ kind: "Sky",
748
+ name: "ERC20.approve USDS",
749
+ spender: engine,
750
+ amountWei: repayWei.toString()
751
+ }),
752
+ evm: { type: "sky_lockstake_usds_approve", version: 1, chainId: "1" },
753
+ sky: { step: "approve_usds", engine }
754
+ }
755
+ });
756
+ }
757
+ steps.push({
758
+ to: engine,
759
+ data: encodeFunctionData({
760
+ abi: engineAbi,
761
+ functionName: "wipe",
762
+ args: [executor, args.urnIndex, repayWei]
763
+ }),
764
+ value: 0n,
765
+ meta: {
766
+ destinationAddress: engine,
767
+ signatureText: JSON.stringify({
768
+ kind: "Sky",
769
+ name: "LockstakeEngine.wipe",
770
+ urnIndex: args.urnIndex.toString(),
771
+ amountWei: repayWei.toString()
772
+ }),
773
+ evm: { type: "sky_lockstake_wipe", version: 1, chainId: "1" },
774
+ sky: { step: "wipe", engine }
775
+ }
776
+ });
777
+ } else {
778
+ const maxApproval = 2n ** 256n - 1n;
779
+ steps.push({
780
+ to: usds,
781
+ data: encodeFunctionData({
782
+ abi: erc20ApproveAbi,
783
+ functionName: "approve",
784
+ args: [engine, maxApproval]
785
+ }),
786
+ value: 0n,
787
+ meta: {
788
+ destinationAddress: usds,
789
+ signatureText: JSON.stringify({
790
+ kind: "Sky",
791
+ name: "ERC20.approve USDS (max)",
792
+ spender: engine,
793
+ note: "Allows wipeAll to pull the exact burn amount computed on-chain."
794
+ }),
795
+ evm: { type: "sky_lockstake_usds_approve", version: 1, chainId: "1" },
796
+ sky: { step: "approve_usds_max", engine }
797
+ }
798
+ });
799
+ steps.push({
800
+ to: engine,
801
+ data: encodeFunctionData({
802
+ abi: engineAbi,
803
+ functionName: "wipeAll",
804
+ args: [executor, args.urnIndex]
805
+ }),
806
+ value: 0n,
807
+ meta: {
808
+ destinationAddress: engine,
809
+ signatureText: JSON.stringify({
810
+ kind: "Sky",
811
+ name: "LockstakeEngine.wipeAll",
812
+ urnIndex: args.urnIndex.toString()
813
+ }),
814
+ evm: { type: "sky_lockstake_wipe_all", version: 1, chainId: "1" },
815
+ sky: { step: "wipeAll", engine }
816
+ }
817
+ });
818
+ }
819
+ const purposePrefix = (() => {
820
+ const t = args.purposeText.trim();
821
+ const suffix = args.wipeAll ? `Sky LockStake: repay all USDS debt (wipeAll).` : `Sky LockStake: repay USDS (wipe).`;
822
+ return (t ? `${t}
823
+
824
+ ` : "") + suffix;
825
+ })();
826
+ return appendSerializedSkyBatch({
827
+ steps,
828
+ publicClient,
829
+ executor,
830
+ chainId: 1,
831
+ rpcUrl: rpc,
832
+ chainDetail: args.chainDetail,
833
+ useCustomGas: args.useCustomGas,
834
+ customGasChainDetails: args.customGasChainDetails,
835
+ purposePrefix,
836
+ keyGen: args.keyGen,
837
+ destinationChainID: "1"
838
+ });
839
+ }
840
+ async function buildSkyLockstakeCloseBatch(args) {
841
+ if (args.chainId !== 1) throw new Error("Sky LockStake is only supported on Ethereum mainnet (chain id 1).");
842
+ if (args.inkWei === 0n) throw new Error("Nothing to free.");
843
+ const rpc = args.rpcUrl.trim();
844
+ const ch = defineChain({
845
+ id: 1,
846
+ name: "Ethereum",
847
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
848
+ rpcUrls: { default: { http: [rpc] } }
849
+ });
850
+ const publicClient = createPublicClient({ chain: ch, transport: http(rpc) });
851
+ const executor = getAddress(args.executorAddress);
852
+ const engine = getAddress(SKY_LOCKSTAKE_ENGINE_MAINNET);
853
+ const usds = getAddress(USDS_ETHEREUM_MAINNET);
854
+ const toSky = getAddress(args.skyRecipient ?? executor);
855
+ const maxApproval = 2n ** 256n - 1n;
856
+ const steps = [
857
+ {
858
+ to: usds,
859
+ data: encodeFunctionData({
860
+ abi: erc20ApproveAbi,
861
+ functionName: "approve",
862
+ args: [engine, maxApproval]
863
+ }),
864
+ value: 0n,
865
+ meta: {
866
+ destinationAddress: usds,
867
+ signatureText: JSON.stringify({
868
+ kind: "Sky",
869
+ name: "ERC20.approve USDS (max)",
870
+ spender: engine,
871
+ note: "Close position: wipeAll then free SKY."
872
+ }),
873
+ evm: { type: "sky_lockstake_usds_approve", version: 1, chainId: "1" },
874
+ sky: { step: "approve_usds_max", engine }
875
+ }
876
+ },
877
+ {
878
+ to: engine,
879
+ data: encodeFunctionData({
880
+ abi: engineAbi,
881
+ functionName: "wipeAll",
882
+ args: [executor, args.urnIndex]
883
+ }),
884
+ value: 0n,
885
+ meta: {
886
+ destinationAddress: engine,
887
+ signatureText: JSON.stringify({
888
+ kind: "Sky",
889
+ name: "LockstakeEngine.wipeAll",
890
+ urnIndex: args.urnIndex.toString()
891
+ }),
892
+ evm: { type: "sky_lockstake_wipe_all", version: 1, chainId: "1" },
893
+ sky: { step: "wipeAll", engine }
894
+ }
895
+ },
896
+ {
897
+ to: engine,
898
+ data: encodeFunctionData({
899
+ abi: engineAbi,
900
+ functionName: "freeSky",
901
+ args: [executor, args.urnIndex, toSky, args.inkWei]
902
+ }),
903
+ value: 0n,
904
+ meta: {
905
+ destinationAddress: engine,
906
+ signatureText: JSON.stringify({
907
+ kind: "Sky",
908
+ name: "LockstakeEngine.freeSky",
909
+ urnIndex: args.urnIndex.toString(),
910
+ to: toSky,
911
+ inkWei: args.inkWei.toString(),
912
+ note: "Exit fee may reduce SKY received."
913
+ }),
914
+ evm: { type: "sky_lockstake_free_sky", version: 1, chainId: "1" },
915
+ sky: { step: "freeSky", engine }
916
+ }
917
+ }
918
+ ];
919
+ const purposePrefix = (() => {
920
+ const t = args.purposeText.trim();
921
+ const suffix = "Sky LockStake: close position \u2014 wipeAll debt, free SKY collateral.";
922
+ return (t ? `${t}
923
+
924
+ ` : "") + suffix;
925
+ })();
926
+ return appendSerializedSkyBatch({
927
+ steps,
928
+ publicClient,
929
+ executor,
930
+ chainId: 1,
931
+ rpcUrl: rpc,
932
+ chainDetail: args.chainDetail,
933
+ useCustomGas: args.useCustomGas,
934
+ customGasChainDetails: args.customGasChainDetails,
935
+ purposePrefix,
936
+ keyGen: args.keyGen,
937
+ destinationChainID: "1"
938
+ });
939
+ }
940
+ var SKY_FRAGILE_ETH_ESTIMATE_GAS_BATCH_EVMS = /* @__PURE__ */ new Set([
941
+ "sky_lockstake_engine_multicall_stake",
942
+ "sky_lockstake_wipe",
943
+ "sky_lockstake_wipe_all",
944
+ "sky_susds_deposit"
945
+ ]);
946
+ function parseSkyExtraJsonObject(detail) {
947
+ if (!detail) return null;
948
+ const raw = detail.ExtraJSON ?? detail.extraJSON;
949
+ if (raw == null) return null;
950
+ if (typeof raw === "object" && !Array.isArray(raw)) return raw;
951
+ if (typeof raw === "string" && raw.trim()) {
952
+ try {
953
+ const p = JSON.parse(raw);
954
+ if (p && typeof p === "object" && !Array.isArray(p)) return p;
955
+ } catch {
956
+ return null;
957
+ }
958
+ }
959
+ return null;
960
+ }
961
+ function parseOptionalSkyGasLimitString(raw) {
962
+ if (raw == null) return null;
963
+ const s = String(raw).trim();
964
+ if (!s) return null;
965
+ try {
966
+ if (/^0x[0-9a-fA-F]+$/.test(s)) return BigInt(s);
967
+ return BigInt(s);
968
+ } catch {
969
+ return null;
970
+ }
971
+ }
972
+ function isSkyLockstakeIsolationEstimateGasFragileBatchStep(detail, batchIndex) {
973
+ if (batchIndex < 0) return false;
974
+ const ex = parseSkyExtraJsonObject(detail);
975
+ const bm = ex?.batchMeta;
976
+ if (!Array.isArray(bm) || !bm[batchIndex] || typeof bm[batchIndex] !== "object" || Array.isArray(bm[batchIndex])) {
977
+ return false;
978
+ }
979
+ const evm = bm[batchIndex].evm;
980
+ return SKY_FRAGILE_ETH_ESTIMATE_GAS_BATCH_EVMS.has(String(evm?.type ?? ""));
981
+ }
982
+ function resolveSkyLockstakeBatchStepGasFromSignRequest(detail, batchIndex) {
983
+ if (!detail || batchIndex < 0) return null;
984
+ const propBatch = detail.proposal_tx_params ?? detail.proposalTxParams ?? detail.ProposalTxParams;
985
+ if (!Array.isArray(propBatch) || propBatch.length <= batchIndex) return null;
986
+ const row = propBatch[batchIndex];
987
+ if (!row || typeof row !== "object" || Array.isArray(row)) return null;
988
+ return parseOptionalSkyGasLimitString(row.gasLimit);
989
+ }
990
+ var erc20AllowanceAbi2 = parseAbi([
991
+ "function allowance(address owner, address spender) view returns (uint256)",
992
+ "function decimals() view returns (uint8)"
993
+ ]);
994
+ var erc20ApproveAbi2 = parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]);
995
+ var susdsVaultAbi = parseAbi([
996
+ "function asset() view returns (address)",
997
+ "function decimals() view returns (uint8)",
998
+ "function deposit(uint256 assets, address receiver) returns (uint256 shares)",
999
+ "function redeem(uint256 shares, address receiver, address owner) returns (uint256 assets)"
1000
+ ]);
1001
+ async function buildSkySusdsRedeemToUsdsBatch(args) {
1002
+ if (args.chainId !== 1) throw new Error("Sky sUSDS redeem is only supported on Ethereum mainnet (chain id 1).");
1003
+ const rpc = args.rpcUrl.trim();
1004
+ const vault = getAddress(SUSDS_VAULT_ERC4626_MAINNET);
1005
+ const usdsCanonical = getAddress(USDS_ETHEREUM_MAINNET);
1006
+ const ch = defineChain({
1007
+ id: 1,
1008
+ name: "Ethereum",
1009
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1010
+ rpcUrls: { default: { http: [rpc] } }
1011
+ });
1012
+ const publicClient = createPublicClient({ chain: ch, transport: http(rpc) });
1013
+ const executor = getAddress(args.executorAddress);
1014
+ const receiver = getAddress(args.receiver ?? executor);
1015
+ const underlying = await publicClient.readContract({
1016
+ address: vault,
1017
+ abi: susdsVaultAbi,
1018
+ functionName: "asset"
1019
+ });
1020
+ if (getAddress(underlying).toLowerCase() !== usdsCanonical.toLowerCase()) {
1021
+ throw new Error("sUSDS vault underlying does not match canonical USDS on Ethereum.");
1022
+ }
1023
+ const dec = await publicClient.readContract({
1024
+ address: vault,
1025
+ abi: susdsVaultAbi,
1026
+ functionName: "decimals"
1027
+ });
1028
+ const sharesWei = parseUnits(args.sharesHuman, Number(dec));
1029
+ if (sharesWei === 0n) throw new Error("Redeem amount is zero.");
1030
+ const data = encodeFunctionData({
1031
+ abi: susdsVaultAbi,
1032
+ functionName: "redeem",
1033
+ args: [sharesWei, receiver, executor]
1034
+ });
1035
+ const steps = [
1036
+ {
1037
+ to: vault,
1038
+ data,
1039
+ value: 0n,
1040
+ meta: {
1041
+ destinationAddress: vault,
1042
+ signatureText: JSON.stringify({
1043
+ kind: "Sky",
1044
+ name: "sUSDS (ERC-4626).redeem",
1045
+ function: "redeem(uint256 shares, address receiver, address owner)",
1046
+ sharesWei: sharesWei.toString(),
1047
+ sharesHuman: args.sharesHuman,
1048
+ receiver,
1049
+ owner: executor,
1050
+ vault,
1051
+ underlying: usdsCanonical
1052
+ }),
1053
+ evm: { type: "sky_susds_redeem", version: 1, chainId: "1" },
1054
+ sky: { step: "susds_redeem", vault }
1055
+ }
1056
+ }
1057
+ ];
1058
+ const purposePrefix = (() => {
1059
+ const t = args.purposeText.trim();
1060
+ const suffix = "Sky sUSDS: redeem Savings USDS shares to USDS via ERC-4626 redeem (1 tx).";
1061
+ return (t ? `${t}
1062
+
1063
+ ` : "") + suffix;
1064
+ })();
1065
+ return appendSerializedSkyBatch({
1066
+ steps,
1067
+ publicClient,
1068
+ executor,
1069
+ chainId: 1,
1070
+ rpcUrl: rpc,
1071
+ chainDetail: args.chainDetail,
1072
+ useCustomGas: args.useCustomGas,
1073
+ customGasChainDetails: args.customGasChainDetails,
1074
+ purposePrefix,
1075
+ keyGen: args.keyGen,
1076
+ destinationChainID: "1"
1077
+ });
1078
+ }
1079
+ async function buildSkySusdsDepositFromUsdsBatch(args) {
1080
+ if (args.chainId !== 1) throw new Error("Sky sUSDS deposit is only supported on Ethereum mainnet (chain id 1).");
1081
+ const rpc = args.rpcUrl.trim();
1082
+ const vault = getAddress(SUSDS_VAULT_ERC4626_MAINNET);
1083
+ const usdsCanonical = getAddress(USDS_ETHEREUM_MAINNET);
1084
+ const ch = defineChain({
1085
+ id: 1,
1086
+ name: "Ethereum",
1087
+ nativeCurrency: { decimals: 18, name: "Ether", symbol: "ETH" },
1088
+ rpcUrls: { default: { http: [rpc] } }
1089
+ });
1090
+ const publicClient = createPublicClient({ chain: ch, transport: http(rpc) });
1091
+ const executor = getAddress(args.executorAddress);
1092
+ const underlying = await publicClient.readContract({
1093
+ address: vault,
1094
+ abi: susdsVaultAbi,
1095
+ functionName: "asset"
1096
+ });
1097
+ if (getAddress(underlying).toLowerCase() !== usdsCanonical.toLowerCase()) {
1098
+ throw new Error("sUSDS vault underlying does not match canonical USDS on Ethereum.");
1099
+ }
1100
+ const dec = await publicClient.readContract({
1101
+ address: usdsCanonical,
1102
+ abi: erc20AllowanceAbi2,
1103
+ functionName: "decimals"
1104
+ });
1105
+ const assetsWei = parseUnits(args.assetsHuman.trim(), Number(dec));
1106
+ if (assetsWei === 0n) throw new Error("Deposit amount is zero.");
1107
+ const steps = [];
1108
+ const currentAllowance = await publicClient.readContract({
1109
+ address: usdsCanonical,
1110
+ abi: erc20AllowanceAbi2,
1111
+ functionName: "allowance",
1112
+ args: [executor, vault]
1113
+ });
1114
+ if (currentAllowance < assetsWei) {
1115
+ if (currentAllowance > 0n) {
1116
+ steps.push({
1117
+ to: usdsCanonical,
1118
+ data: encodeFunctionData({
1119
+ abi: erc20ApproveAbi2,
1120
+ functionName: "approve",
1121
+ args: [vault, 0n]
1122
+ }),
1123
+ value: 0n,
1124
+ meta: {
1125
+ destinationAddress: usdsCanonical,
1126
+ signatureText: JSON.stringify({
1127
+ kind: "Sky",
1128
+ name: "ERC20.approve USDS reset",
1129
+ spender: vault
1130
+ }),
1131
+ evm: { type: "sky_susds_usds_approve", version: 1, chainId: "1" },
1132
+ sky: { step: "approve_usds_reset_susds_vault", vault }
1133
+ }
1134
+ });
1135
+ }
1136
+ steps.push({
1137
+ to: usdsCanonical,
1138
+ data: encodeFunctionData({
1139
+ abi: erc20ApproveAbi2,
1140
+ functionName: "approve",
1141
+ args: [vault, assetsWei]
1142
+ }),
1143
+ value: 0n,
1144
+ meta: {
1145
+ destinationAddress: usdsCanonical,
1146
+ signatureText: JSON.stringify({
1147
+ kind: "Sky",
1148
+ name: "ERC20.approve USDS",
1149
+ spender: vault,
1150
+ amountWei: assetsWei.toString()
1151
+ }),
1152
+ evm: { type: "sky_susds_usds_approve", version: 1, chainId: "1" },
1153
+ sky: { step: "approve_usds_susds_vault", vault }
1154
+ }
1155
+ });
1156
+ }
1157
+ const depositData = encodeFunctionData({
1158
+ abi: susdsVaultAbi,
1159
+ functionName: "deposit",
1160
+ args: [assetsWei, executor]
1161
+ });
1162
+ steps.push({
1163
+ to: vault,
1164
+ data: depositData,
1165
+ value: 0n,
1166
+ meta: {
1167
+ destinationAddress: vault,
1168
+ signatureText: JSON.stringify({
1169
+ kind: "Sky",
1170
+ name: "sUSDS (ERC-4626).deposit",
1171
+ function: "deposit(uint256 assets, address receiver)",
1172
+ assetsWei: assetsWei.toString(),
1173
+ assetsHuman: args.assetsHuman.trim(),
1174
+ receiver: executor,
1175
+ vault,
1176
+ underlying: usdsCanonical
1177
+ }),
1178
+ evm: { type: "sky_susds_deposit", version: 1, chainId: "1" },
1179
+ sky: { step: "susds_deposit", vault }
1180
+ }
1181
+ });
1182
+ const purposePrefix = (() => {
1183
+ const t = args.purposeText.trim();
1184
+ const suffix = "Sky sUSDS: deposit USDS into Savings vault (ERC-4626 deposit \u2192 mint shares).";
1185
+ return (t ? `${t}
1186
+
1187
+ ` : "") + suffix;
1188
+ })();
1189
+ return appendSerializedSkyBatch({
1190
+ steps,
1191
+ publicClient,
1192
+ executor,
1193
+ chainId: 1,
1194
+ rpcUrl: rpc,
1195
+ chainDetail: args.chainDetail,
1196
+ useCustomGas: args.useCustomGas,
1197
+ customGasChainDetails: args.customGasChainDetails,
1198
+ purposePrefix,
1199
+ keyGen: args.keyGen,
1200
+ destinationChainID: "1"
1201
+ });
1202
+ }
1203
+
1204
+ // src/protocols/evm/sky/index.ts
1205
+ var SKY_PROTOCOL_ID = "sky";
1206
+ var skyProtocolModule = {
1207
+ id: SKY_PROTOCOL_ID,
1208
+ chainCategory: "evm",
1209
+ isChainSupported(ctx) {
1210
+ if (ctx.chainCategory !== "evm") return false;
1211
+ return Number(ctx.chainId) === SKY_ETHEREUM_MAINNET_CHAIN_ID;
1212
+ },
1213
+ isTokenSupported(token) {
1214
+ return token.category === "evm" && token.kind === "erc20";
1215
+ },
1216
+ actions: [
1217
+ { id: "sky.lockstake-stake", protocolId: SKY_PROTOCOL_ID, chainCategory: "evm", description: "Open Lockstake position", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
1218
+ { id: "sky.lockstake-draw", protocolId: SKY_PROTOCOL_ID, chainCategory: "evm", description: "Borrow USDS from Lockstake", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
1219
+ { id: "sky.lockstake-wipe", protocolId: SKY_PROTOCOL_ID, chainCategory: "evm", description: "Repay Lockstake debt", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
1220
+ { id: "sky.lockstake-close", protocolId: SKY_PROTOCOL_ID, chainCategory: "evm", description: "Close Lockstake position", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
1221
+ { id: "sky.susds-deposit", protocolId: SKY_PROTOCOL_ID, chainCategory: "evm", description: "Deposit USDS into sUSDS vault", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} },
1222
+ { id: "sky.susds-redeem", protocolId: SKY_PROTOCOL_ID, chainCategory: "evm", description: "Redeem sUSDS to USDS", commonParams: ["keyGen", "purposeText", "useCustomGas"], params: {} }
1223
+ ]
1224
+ };
1225
+ registerProtocolModule(skyProtocolModule);
1226
+
1227
+ export { SKY_ETHEREUM_MAINNET, SKY_ETHEREUM_MAINNET_CHAIN_ID, SKY_LOCKSTAKE_DEFAULT_FARM_MAINNET, SKY_LOCKSTAKE_DEFAULT_FARM_REF, SKY_LOCKSTAKE_ENGINE_MAINNET, SKY_LOCKSTAKE_MULTICALL_FALLBACK_GAS, SKY_LOCKSTAKE_UI_COLLATERAL_FACTOR, SKY_LS_SKY_ERC20_MAINNET, SKY_PROTOCOL_ID, SKY_TOKEN_DECIMALS, SUSDS_VAULT_ERC4626_MAINNET, USDS_DECIMALS, USDS_ETHEREUM_MAINNET, appendSerializedSkyBatch, buildSkyLockstakeCloseBatch, buildSkyLockstakeDrawBatch, buildSkyLockstakeGetRewardBatch, buildSkyLockstakeStakePositionBatch, buildSkyLockstakeWipeBatch, buildSkySusdsDepositFromUsdsBatch, buildSkySusdsRedeemToUsdsBatch, isSkyLockstakeIsolationEstimateGasFragileBatchStep, resolveSkyLockstakeBatchStepGasFromSignRequest, skyProtocolModule };
1228
+ //# sourceMappingURL=index.js.map
1229
+ //# sourceMappingURL=index.js.map