@eulerxyz/euler-v2-sdk 0.2.3-beta → 0.2.5-beta
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 +14 -11
- package/dist/src/entities/ERC4626Vault.d.ts +8 -0
- package/dist/src/entities/ERC4626Vault.d.ts.map +1 -1
- package/dist/src/entities/ERC4626Vault.js +4 -0
- package/dist/src/entities/ERC4626Vault.js.map +1 -1
- package/dist/src/entities/EVault.d.ts +6 -0
- package/dist/src/entities/EVault.d.ts.map +1 -1
- package/dist/src/entities/EVault.js +11 -0
- package/dist/src/entities/EVault.js.map +1 -1
- package/dist/src/entities/SecuritizeCollateralVault.d.ts +2 -0
- package/dist/src/entities/SecuritizeCollateralVault.d.ts.map +1 -1
- package/dist/src/entities/SecuritizeCollateralVault.js +4 -0
- package/dist/src/entities/SecuritizeCollateralVault.js.map +1 -1
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/sdk/buildSDK.d.ts +8 -0
- package/dist/src/sdk/buildSDK.d.ts.map +1 -1
- package/dist/src/sdk/buildSDK.js +309 -101
- package/dist/src/sdk/buildSDK.js.map +1 -1
- package/dist/src/sdk/config.d.ts +8 -2
- package/dist/src/sdk/config.d.ts.map +1 -1
- package/dist/src/sdk/config.js +6 -1
- package/dist/src/sdk/config.js.map +1 -1
- package/dist/src/sdk/sdk.d.ts +3 -0
- package/dist/src/sdk/sdk.d.ts.map +1 -1
- package/dist/src/sdk/sdk.js +2 -0
- package/dist/src/sdk/sdk.js.map +1 -1
- package/dist/src/services/accountService/accountServiceConfig.d.ts +2 -2
- package/dist/src/services/accountService/accountServiceConfig.d.ts.map +1 -1
- package/dist/src/services/executionService/abis/cowSwapWrapperAbi.d.ts +199 -0
- package/dist/src/services/executionService/abis/cowSwapWrapperAbi.d.ts.map +1 -0
- package/dist/src/services/executionService/abis/cowSwapWrapperAbi.js +95 -0
- package/dist/src/services/executionService/abis/cowSwapWrapperAbi.js.map +1 -0
- package/dist/src/services/executionService/cowExecutor.d.ts +53 -0
- package/dist/src/services/executionService/cowExecutor.d.ts.map +1 -0
- package/dist/src/services/executionService/cowExecutor.js +392 -0
- package/dist/src/services/executionService/cowExecutor.js.map +1 -0
- package/dist/src/services/executionService/cowSwapHelpers.d.ts +255 -0
- package/dist/src/services/executionService/cowSwapHelpers.d.ts.map +1 -0
- package/dist/src/services/executionService/cowSwapHelpers.js +525 -0
- package/dist/src/services/executionService/cowSwapHelpers.js.map +1 -0
- package/dist/src/services/executionService/execute.d.ts +18 -3
- package/dist/src/services/executionService/execute.d.ts.map +1 -1
- package/dist/src/services/executionService/execute.js +9 -2
- package/dist/src/services/executionService/execute.js.map +1 -1
- package/dist/src/services/executionService/executionService.d.ts +53 -2
- package/dist/src/services/executionService/executionService.d.ts.map +1 -1
- package/dist/src/services/executionService/executionService.js +367 -20
- package/dist/src/services/executionService/executionService.js.map +1 -1
- package/dist/src/services/executionService/executionServiceTypes.d.ts +130 -3
- package/dist/src/services/executionService/executionServiceTypes.d.ts.map +1 -1
- package/dist/src/services/executionService/executionServiceTypes.js +13 -0
- package/dist/src/services/executionService/executionServiceTypes.js.map +1 -1
- package/dist/src/services/executionService/index.d.ts +6 -3
- package/dist/src/services/executionService/index.d.ts.map +1 -1
- package/dist/src/services/executionService/index.js +4 -2
- package/dist/src/services/executionService/index.js.map +1 -1
- package/dist/src/services/executionService/simulate.d.ts +1 -1
- package/dist/src/services/executionService/simulate.d.ts.map +1 -1
- package/dist/src/services/executionService/simulate.js +25 -9
- package/dist/src/services/executionService/simulate.js.map +1 -1
- package/dist/src/services/executionService/tokenApprovalReset.d.ts +3 -0
- package/dist/src/services/executionService/tokenApprovalReset.d.ts.map +1 -0
- package/dist/src/services/executionService/tokenApprovalReset.js +8 -0
- package/dist/src/services/executionService/tokenApprovalReset.js.map +1 -0
- package/dist/src/services/priceService/backendClient.d.ts +3 -1
- package/dist/src/services/priceService/backendClient.d.ts.map +1 -1
- package/dist/src/services/priceService/backendClient.js +30 -12
- package/dist/src/services/priceService/backendClient.js.map +1 -1
- package/dist/src/services/reulLockService/index.d.ts +3 -0
- package/dist/src/services/reulLockService/index.d.ts.map +1 -0
- package/dist/src/services/reulLockService/index.js +2 -0
- package/dist/src/services/reulLockService/index.js.map +1 -0
- package/dist/src/services/reulLockService/reulLockService.d.ts +82 -0
- package/dist/src/services/reulLockService/reulLockService.d.ts.map +1 -0
- package/dist/src/services/reulLockService/reulLockService.js +120 -0
- package/dist/src/services/reulLockService/reulLockService.js.map +1 -0
- package/dist/src/services/reulLockService/reulLockServiceTypes.d.ts +41 -0
- package/dist/src/services/reulLockService/reulLockServiceTypes.d.ts.map +1 -0
- package/dist/src/services/reulLockService/reulLockServiceTypes.js +2 -0
- package/dist/src/services/reulLockService/reulLockServiceTypes.js.map +1 -0
- package/dist/src/services/rewardsService/adapters/rewardsDirectAdapter/rewardsDirectAdapter.d.ts.map +1 -1
- package/dist/src/services/rewardsService/adapters/rewardsDirectAdapter/rewardsDirectAdapter.js +93 -5
- package/dist/src/services/rewardsService/adapters/rewardsDirectAdapter/rewardsDirectAdapter.js.map +1 -1
- package/dist/src/services/rewardsService/rewardsService.d.ts +1 -7
- package/dist/src/services/rewardsService/rewardsService.d.ts.map +1 -1
- package/dist/src/services/rewardsService/rewardsService.js +5 -130
- package/dist/src/services/rewardsService/rewardsService.js.map +1 -1
- package/dist/src/services/rewardsService/rewardsServiceTypes.d.ts +13 -2
- package/dist/src/services/rewardsService/rewardsServiceTypes.d.ts.map +1 -1
- package/dist/src/services/swapService/index.d.ts +3 -2
- package/dist/src/services/swapService/index.d.ts.map +1 -1
- package/dist/src/services/swapService/index.js +1 -0
- package/dist/src/services/swapService/index.js.map +1 -1
- package/dist/src/services/swapService/swapAllowlist.d.ts +16 -0
- package/dist/src/services/swapService/swapAllowlist.d.ts.map +1 -0
- package/dist/src/services/swapService/swapAllowlist.js +51 -0
- package/dist/src/services/swapService/swapAllowlist.js.map +1 -0
- package/dist/src/services/swapService/swapService.d.ts +15 -0
- package/dist/src/services/swapService/swapService.d.ts.map +1 -1
- package/dist/src/services/swapService/swapService.js +237 -12
- package/dist/src/services/swapService/swapService.js.map +1 -1
- package/dist/src/services/swapService/swapServiceTypes.d.ts +33 -0
- package/dist/src/services/swapService/swapServiceTypes.d.ts.map +1 -1
- package/dist/src/services/vaults/eVaultService/eVaultServiceConfig.d.ts +2 -2
- package/dist/src/services/vaults/eVaultService/eVaultServiceConfig.d.ts.map +1 -1
- package/dist/src/services/vaults/eulerEarnService/eulerEarnServiceConfig.d.ts +2 -2
- package/dist/src/services/vaults/eulerEarnService/eulerEarnServiceConfig.d.ts.map +1 -1
- package/dist/src/utils/fallbackAdapter.d.ts +69 -0
- package/dist/src/utils/fallbackAdapter.d.ts.map +1 -0
- package/dist/src/utils/fallbackAdapter.js +139 -0
- package/dist/src/utils/fallbackAdapter.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,21 +1,111 @@
|
|
|
1
|
-
import { decodeFunctionData, encodeFunctionData, erc20Abi, getAddress,
|
|
1
|
+
import { decodeFunctionData, encodeFunctionData, erc20Abi, getAddress, maxUint160, maxUint256, } from "viem";
|
|
2
2
|
import { resolveBorrowCollateralPositions } from "../../utils/accountPositionClassification.js";
|
|
3
|
+
import { SwapperMode } from "../swapService/swapServiceTypes.js";
|
|
4
|
+
import { adjustForInterest, getSwapInputAmount, } from "../swapService/swapVerification.js";
|
|
3
5
|
import { ethereumVaultConnectorAbi } from "./abis/ethereumVaultConnectorAbi.js";
|
|
4
6
|
import { eVaultAbi } from "./abis/eVaultAbi.js";
|
|
5
7
|
import { permit2PermitAbi } from "./abis/permit2PermitAbi.js";
|
|
6
8
|
import { swapperAbi } from "./abis/swapperAbi.js";
|
|
7
9
|
import { swapVerifierAbi } from "./abis/swapVerifierAbi.js";
|
|
10
|
+
import { executeCowSwapTransactionPlan, } from "./cowExecutor.js";
|
|
11
|
+
import { computeNonceNamespace, COWSWAP_ORDER_DEADLINE_SECONDS, getCowSwapChainConfig, } from "./cowSwapHelpers.js";
|
|
8
12
|
import * as encodeHelpers from "./encode.js";
|
|
9
|
-
import { isEVCBatchOperation } from "./executionServiceTypes.js";
|
|
10
13
|
import { executeTransactionPlan, } from "./execute.js";
|
|
14
|
+
import { assertNoCowSwapPlanItems, isCowSwapPlanItem, isEVCBatchOperation, } from "./executionServiceTypes.js";
|
|
11
15
|
import { deriveStateOverrides, estimateGasForTransactionPlan, simulateTransactionPlan, } from "./simulate.js";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
import { requiresZeroApprovalReset } from "./tokenApprovalReset.js";
|
|
17
|
+
function isCowSwapQuote(quote) {
|
|
18
|
+
return quote.route?.some((hop) => hop.providerName?.toLowerCase().includes("cow"));
|
|
19
|
+
}
|
|
20
|
+
function assertNonCowSwapQuote(quote, planFunctionName, cowPlanFunctionName) {
|
|
21
|
+
if (!isCowSwapQuote(quote))
|
|
22
|
+
return;
|
|
23
|
+
const suffix = cowPlanFunctionName
|
|
24
|
+
? ` Use ${cowPlanFunctionName} instead.`
|
|
25
|
+
: " Use the appropriate CoW plan function instead.";
|
|
26
|
+
throw new Error(`ExecutionService.${planFunctionName} does not support CoW swap quotes.${suffix}`);
|
|
27
|
+
}
|
|
28
|
+
function assertCowSwapQuote(quote, planFunctionName) {
|
|
29
|
+
if (isCowSwapQuote(quote))
|
|
30
|
+
return;
|
|
31
|
+
throw new Error(`ExecutionService.${planFunctionName} requires a CoW swap quote.`);
|
|
32
|
+
}
|
|
33
|
+
function parseCowSwapAmount(field, value) {
|
|
34
|
+
if (typeof value !== "string" || !/^\d+$/.test(value)) {
|
|
35
|
+
throw new Error(`CoW quote ${field} missing or invalid`);
|
|
36
|
+
}
|
|
37
|
+
return BigInt(value);
|
|
38
|
+
}
|
|
39
|
+
function normalizeCowSwapQuoteId(value) {
|
|
40
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
44
|
+
const parsed = Number(value);
|
|
45
|
+
if (Number.isSafeInteger(parsed))
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
function parseSlippagePercent(slippage) {
|
|
51
|
+
if (!Number.isFinite(slippage) || slippage <= 0) {
|
|
52
|
+
return { slippageUnits: 0n, denominator: 1n };
|
|
53
|
+
}
|
|
54
|
+
const slippageString = slippage.toLocaleString("en-US", {
|
|
55
|
+
useGrouping: false,
|
|
56
|
+
maximumFractionDigits: 20,
|
|
57
|
+
});
|
|
58
|
+
const [whole = "0", fraction = ""] = slippageString.split(".");
|
|
59
|
+
const scale = 10n ** BigInt(fraction.length);
|
|
60
|
+
const slippageUnits = BigInt(whole) * scale + BigInt(fraction || "0");
|
|
61
|
+
return {
|
|
62
|
+
slippageUnits,
|
|
63
|
+
denominator: 100n * scale,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function reduceBySlippage(amount, slippage = 0) {
|
|
67
|
+
const { slippageUnits, denominator } = parseSlippagePercent(slippage);
|
|
68
|
+
if (slippageUnits >= denominator)
|
|
69
|
+
return 0n;
|
|
70
|
+
return (amount * (denominator - slippageUnits)) / denominator;
|
|
71
|
+
}
|
|
72
|
+
function increaseBySlippage(amount, slippage = 0) {
|
|
73
|
+
const { slippageUnits, denominator } = parseSlippagePercent(slippage);
|
|
74
|
+
return ((amount * (denominator + slippageUnits) + denominator - 1n) / denominator);
|
|
75
|
+
}
|
|
76
|
+
function getCowSwapQuoteOrderAmounts(quote, options = {}) {
|
|
77
|
+
const providerData = quote.providerData;
|
|
78
|
+
const quoteSellAmount = parseCowSwapAmount("providerData.sellAmount", providerData?.sellAmount);
|
|
79
|
+
const buyAmount = parseCowSwapAmount("providerData.buyAmount", providerData?.buyAmount);
|
|
80
|
+
const feeAmount = parseCowSwapAmount("providerData.feeAmount", providerData?.feeAmount);
|
|
81
|
+
const quoteId = normalizeCowSwapQuoteId(providerData?.quoteId);
|
|
82
|
+
if (quoteSellAmount <= 0n || buyAmount <= 0n) {
|
|
83
|
+
throw new Error("CoW quote order amount must be positive");
|
|
84
|
+
}
|
|
85
|
+
const slippage = options.slippage ?? quote.slippage ?? 0;
|
|
86
|
+
const orderSellAmount = quoteSellAmount + feeAmount;
|
|
87
|
+
const slippageAdjustedSellAmount = options.slippageTarget === "sellAmount"
|
|
88
|
+
? increaseBySlippage(orderSellAmount, slippage)
|
|
89
|
+
: orderSellAmount;
|
|
90
|
+
const adjustedSellAmount = options.maxSellAmount !== undefined &&
|
|
91
|
+
slippageAdjustedSellAmount > options.maxSellAmount
|
|
92
|
+
? options.maxSellAmount
|
|
93
|
+
: slippageAdjustedSellAmount;
|
|
94
|
+
const adjustedBuyAmount = options.slippageTarget === "buyAmount"
|
|
95
|
+
? reduceBySlippage(buyAmount, slippage)
|
|
96
|
+
: buyAmount;
|
|
97
|
+
if (adjustedSellAmount <= 0n || adjustedBuyAmount <= 0n) {
|
|
98
|
+
throw new Error("CoW quote order amount must be positive");
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
sellAmount: adjustedSellAmount,
|
|
102
|
+
buyAmount: adjustedBuyAmount,
|
|
103
|
+
feeAmount,
|
|
104
|
+
quoteId,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function getCowSwapValidTo(validTo) {
|
|
108
|
+
return (validTo ?? Math.floor(Date.now() / 1000) + COWSWAP_ORDER_DEADLINE_SECONDS);
|
|
19
109
|
}
|
|
20
110
|
function isSavingsCollateral(collateral) {
|
|
21
111
|
return collateral?.source === "savings";
|
|
@@ -35,7 +125,9 @@ function hasShareConversion(vault) {
|
|
|
35
125
|
return (typeof vault === "object" &&
|
|
36
126
|
vault !== null &&
|
|
37
127
|
"convertToShares" in vault &&
|
|
38
|
-
typeof vault.convertToShares === "function"
|
|
128
|
+
typeof vault.convertToShares === "function" &&
|
|
129
|
+
"previewWithdraw" in vault &&
|
|
130
|
+
typeof vault.previewWithdraw === "function");
|
|
39
131
|
}
|
|
40
132
|
function getStateTransition(item) {
|
|
41
133
|
try {
|
|
@@ -199,16 +291,57 @@ export class ExecutionService {
|
|
|
199
291
|
}
|
|
200
292
|
/** Derive storage overrides needed to simulate the plan against the current account state. */
|
|
201
293
|
async deriveStateOverrides(chainId, account, transactionPlan, options) {
|
|
294
|
+
assertNoCowSwapPlanItems(transactionPlan, "deriveStateOverrides");
|
|
202
295
|
return deriveStateOverrides(this.getSimulationContext(), chainId, account, transactionPlan, options);
|
|
203
296
|
}
|
|
204
297
|
/** Simulate the full transaction plan, including approval resolution and plugin-aware batch execution. */
|
|
205
298
|
async simulateTransactionPlan(chainId, account, transactionPlan, options) {
|
|
299
|
+
assertNoCowSwapPlanItems(transactionPlan, "simulateTransactionPlan");
|
|
206
300
|
const processedPlan = await this.processPlanPlugins(transactionPlan, account, chainId);
|
|
207
301
|
const owner = this.getAccountOwner(account);
|
|
208
302
|
return simulateTransactionPlan(this.getSimulationContext(), chainId, owner, processedPlan, options);
|
|
209
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Simulate a {@link TransactionPlanPrepared} envelope. Plugins and approval
|
|
306
|
+
* resolution have already run via {@link prepareTransactionPlan}, so this
|
|
307
|
+
* skips the plugin pipeline and uses the envelope's chainId/account context
|
|
308
|
+
* directly — no re-fetches of plugin-side data on each click.
|
|
309
|
+
*/
|
|
310
|
+
async simulatePreparedTransactionPlan(prepared, options) {
|
|
311
|
+
assertNoCowSwapPlanItems(prepared.plan, "simulatePreparedTransactionPlan");
|
|
312
|
+
const owner = this.getAccountOwner(prepared.account);
|
|
313
|
+
return simulateTransactionPlan(this.getSimulationContext(), prepared.chainId, owner, prepared.plan, options);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Run plugins and resolve required approvals up front, packaging the result
|
|
317
|
+
* with its execution context. Simulate and execute can be called against the
|
|
318
|
+
* returned envelope repeatedly without re-running plugins or refetching
|
|
319
|
+
* wallet allowances.
|
|
320
|
+
*/
|
|
321
|
+
async prepareTransactionPlan(args) {
|
|
322
|
+
const { plan, chainId, account, usePermit2 = true, unlimitedApproval = false, } = args;
|
|
323
|
+
assertNoCowSwapPlanItems(plan, "prepareTransactionPlan");
|
|
324
|
+
const processedPlan = await this.processPlanPlugins(plan, account, chainId);
|
|
325
|
+
const owner = this.getAccountOwner(account);
|
|
326
|
+
const resolvedPlan = await this.resolveRequiredApprovals({
|
|
327
|
+
plan: processedPlan,
|
|
328
|
+
chainId,
|
|
329
|
+
account: owner,
|
|
330
|
+
usePermit2,
|
|
331
|
+
unlimitedApproval,
|
|
332
|
+
});
|
|
333
|
+
return {
|
|
334
|
+
__prepared: true,
|
|
335
|
+
plan: resolvedPlan,
|
|
336
|
+
chainId,
|
|
337
|
+
account,
|
|
338
|
+
usePermit2,
|
|
339
|
+
unlimitedApproval,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
210
342
|
/** Estimate gas for the full transaction plan after applying the same simulation pipeline used for execution. */
|
|
211
343
|
async estimateGasForTransactionPlan(chainId, account, transactionPlan, options) {
|
|
344
|
+
assertNoCowSwapPlanItems(transactionPlan, "estimateGasForTransactionPlan");
|
|
212
345
|
const processedPlan = await this.processPlanPlugins(transactionPlan, account, chainId);
|
|
213
346
|
const owner = this.getAccountOwner(account);
|
|
214
347
|
return estimateGasForTransactionPlan(this.getSimulationContext(), chainId, owner, processedPlan, options);
|
|
@@ -219,15 +352,58 @@ export class ExecutionService {
|
|
|
219
352
|
if (!providerService) {
|
|
220
353
|
throw new Error("ExecutionService.executeTransactionPlan requires a providerService. Pass it to the ExecutionService constructor or call setProviderService().");
|
|
221
354
|
}
|
|
355
|
+
assertNoCowSwapPlanItems(args.plan, "executeTransactionPlan");
|
|
356
|
+
const processedPlan = await this.processPlanPlugins(args.plan, args.account, args.chainId);
|
|
357
|
+
assertNoCowSwapPlanItems(processedPlan, "executeTransactionPlan");
|
|
222
358
|
const helperArgs = {
|
|
223
359
|
...args,
|
|
224
|
-
plan:
|
|
360
|
+
plan: processedPlan,
|
|
225
361
|
executionService: this,
|
|
226
362
|
deploymentService: this.deploymentService,
|
|
227
363
|
providerService,
|
|
228
364
|
};
|
|
229
365
|
return executeTransactionPlan(helperArgs);
|
|
230
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Execute a {@link TransactionPlanPrepared} envelope. Plugins and approval
|
|
369
|
+
* resolution were already applied by {@link prepareTransactionPlan}, so
|
|
370
|
+
* this skips both — no per-execute plugin re-runs or wallet re-fetch.
|
|
371
|
+
*/
|
|
372
|
+
async executePreparedTransactionPlan(args) {
|
|
373
|
+
const { providerService } = this;
|
|
374
|
+
if (!providerService) {
|
|
375
|
+
throw new Error("ExecutionService.executePreparedTransactionPlan requires a providerService. Pass it to the ExecutionService constructor or call setProviderService().");
|
|
376
|
+
}
|
|
377
|
+
const { prepared, sendTransaction, signTypedData, onProgress } = args;
|
|
378
|
+
assertNoCowSwapPlanItems(prepared.plan, "executePreparedTransactionPlan");
|
|
379
|
+
const helperArgs = {
|
|
380
|
+
plan: prepared.plan,
|
|
381
|
+
chainId: prepared.chainId,
|
|
382
|
+
account: prepared.account,
|
|
383
|
+
usePermit2: prepared.usePermit2,
|
|
384
|
+
unlimitedApproval: prepared.unlimitedApproval,
|
|
385
|
+
sendTransaction,
|
|
386
|
+
signTypedData,
|
|
387
|
+
onProgress,
|
|
388
|
+
alreadyResolved: true,
|
|
389
|
+
executionService: this,
|
|
390
|
+
deploymentService: this.deploymentService,
|
|
391
|
+
providerService,
|
|
392
|
+
};
|
|
393
|
+
return executeTransactionPlan(helperArgs);
|
|
394
|
+
}
|
|
395
|
+
async executeCowSwapTransactionPlan(args) {
|
|
396
|
+
const { providerService } = this;
|
|
397
|
+
if (!providerService) {
|
|
398
|
+
throw new Error("ExecutionService.executeCowSwapTransactionPlan requires a providerService. Pass it to the ExecutionService constructor or call setProviderService().");
|
|
399
|
+
}
|
|
400
|
+
return executeCowSwapTransactionPlan({
|
|
401
|
+
...args,
|
|
402
|
+
plan: await this.processPlanPlugins(args.plan, args.account, args.chainId),
|
|
403
|
+
deploymentService: this.deploymentService,
|
|
404
|
+
providerService,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
231
407
|
getSimulationContext() {
|
|
232
408
|
return {
|
|
233
409
|
deploymentService: this.deploymentService,
|
|
@@ -786,6 +962,9 @@ export class ExecutionService {
|
|
|
786
962
|
});
|
|
787
963
|
}
|
|
788
964
|
}
|
|
965
|
+
else if (isCowSwapPlanItem(item)) {
|
|
966
|
+
throw new Error("ExecutionService.mergePlans cannot merge CoW swap plan items. Merge these plans manually to preserve CoW order boundaries.");
|
|
967
|
+
}
|
|
789
968
|
else {
|
|
790
969
|
throw new Error("ExecutionService.mergePlans cannot merge contractCall plan items. Merge these plans manually to preserve call boundaries.");
|
|
791
970
|
}
|
|
@@ -882,6 +1061,7 @@ export class ExecutionService {
|
|
|
882
1061
|
*/
|
|
883
1062
|
resolveRequiredApprovalsWithWallet(args) {
|
|
884
1063
|
const { plan, wallet, chainId, usePermit2 = true, unlimitedApproval = false, } = args;
|
|
1064
|
+
assertNoCowSwapPlanItems(plan, "resolveRequiredApprovalsWithWallet");
|
|
885
1065
|
const deployment = this.deploymentService.getDeployment(chainId);
|
|
886
1066
|
const permit2 = deployment.addresses.coreAddrs.permit2;
|
|
887
1067
|
for (const item of plan) {
|
|
@@ -988,11 +1168,17 @@ export class ExecutionService {
|
|
|
988
1168
|
*/
|
|
989
1169
|
async resolveRequiredApprovals(args) {
|
|
990
1170
|
const { plan, chainId, account, usePermit2 = true, unlimitedApproval = false, } = args;
|
|
1171
|
+
assertNoCowSwapPlanItems(plan, "resolveRequiredApprovals");
|
|
1172
|
+
// Filter transaction plan for only RequiredApproval items
|
|
1173
|
+
const requiredApprovals = plan.filter((item) => item.type === "requiredApproval");
|
|
1174
|
+
// No approvals in the plan → nothing to resolve, no wallet fetch needed.
|
|
1175
|
+
// Withdraw/redeem flows always hit this path (they don't transfer assets in).
|
|
1176
|
+
if (requiredApprovals.length === 0) {
|
|
1177
|
+
return plan;
|
|
1178
|
+
}
|
|
991
1179
|
if (!this.walletService) {
|
|
992
1180
|
throw new Error("ExecutionService.resolveRequiredApprovals requires a walletService. Pass it to the ExecutionService constructor or call setWalletService().");
|
|
993
1181
|
}
|
|
994
|
-
// Filter transaction plan for only RequiredApproval items
|
|
995
|
-
const requiredApprovals = plan.filter((item) => item.type === "requiredApproval");
|
|
996
1182
|
// Transform RequiredApprovals into AssetWithSpenders
|
|
997
1183
|
const assetSpendersMap = new Map();
|
|
998
1184
|
for (const approval of requiredApprovals) {
|
|
@@ -1196,7 +1382,7 @@ export class ExecutionService {
|
|
|
1196
1382
|
* @returns Array of transaction plan items (EVC batch; no approvals needed for redeem)
|
|
1197
1383
|
*/
|
|
1198
1384
|
planRedeem(args) {
|
|
1199
|
-
const { vault, receiver, owner, account, disableCollateral = false
|
|
1385
|
+
const { vault, receiver, owner, account, disableCollateral = false } = args;
|
|
1200
1386
|
const plan = [];
|
|
1201
1387
|
// Get position to check collateral state
|
|
1202
1388
|
const position = account?.getPosition(owner, vault);
|
|
@@ -1218,13 +1404,14 @@ export class ExecutionService {
|
|
|
1218
1404
|
return args.shares;
|
|
1219
1405
|
const vault = position?.vault;
|
|
1220
1406
|
if (!hasShareConversion(vault)) {
|
|
1221
|
-
throw new Error("planRedeem with assets requires account position vault state with
|
|
1407
|
+
throw new Error("planRedeem with assets requires account position vault state with previewWithdraw. Populate the account vaults before planning asset-denominated redeem.");
|
|
1222
1408
|
}
|
|
1223
|
-
// Asset-denominated redeem
|
|
1224
|
-
//
|
|
1225
|
-
//
|
|
1226
|
-
//
|
|
1227
|
-
|
|
1409
|
+
// Asset-denominated redeem must round shares UP so the user receives at
|
|
1410
|
+
// least the requested assets. `convertToShares` rounds down, leaving a
|
|
1411
|
+
// rounding dust shortfall. `previewWithdraw` mirrors the on-chain
|
|
1412
|
+
// previewWithdraw(uint256) — Math.mulDiv with Rounding.Ceil over the
|
|
1413
|
+
// virtual-deposit-adjusted total{Shares,Assets}.
|
|
1414
|
+
return vault.previewWithdraw(args.assets);
|
|
1228
1415
|
}
|
|
1229
1416
|
/**
|
|
1230
1417
|
* Builds a transaction plan for borrowing from a liability vault.
|
|
@@ -1497,6 +1684,7 @@ export class ExecutionService {
|
|
|
1497
1684
|
*/
|
|
1498
1685
|
planRepayWithSwap(args) {
|
|
1499
1686
|
const { swapQuote, account, cleanupOnMax = false, swapperMode } = args;
|
|
1687
|
+
assertNonCowSwapQuote(swapQuote, "planRepayWithSwap", "planClosePositionWithCow");
|
|
1500
1688
|
const plan = [];
|
|
1501
1689
|
const liabilityPosition = account?.getPosition(swapQuote.accountOut, swapQuote.receiver);
|
|
1502
1690
|
const fromPosition = account?.getPosition(swapQuote.accountIn, swapQuote.vaultIn);
|
|
@@ -1529,6 +1717,74 @@ export class ExecutionService {
|
|
|
1529
1717
|
plan.push(...this.convertBatchItemsToPlan(batchItems, "repayWithSwap"));
|
|
1530
1718
|
return plan;
|
|
1531
1719
|
}
|
|
1720
|
+
planClosePositionWithCow(args) {
|
|
1721
|
+
const { account, swapQuote, swapperMode = SwapperMode.TARGET_DEBT, slippage, validTo, orderKind, maxSellAmount, } = args;
|
|
1722
|
+
assertCowSwapQuote(swapQuote, "planClosePositionWithCow");
|
|
1723
|
+
const isTargetDebt = swapperMode === SwapperMode.TARGET_DEBT;
|
|
1724
|
+
const sourcePosition = account.getPosition(swapQuote.accountIn, swapQuote.vaultIn);
|
|
1725
|
+
const resolvedMaxSellAmount = maxSellAmount ?? (isTargetDebt ? sourcePosition?.shares : undefined);
|
|
1726
|
+
const orderAmounts = getCowSwapQuoteOrderAmounts(swapQuote, {
|
|
1727
|
+
slippage,
|
|
1728
|
+
slippageTarget: "sellAmount",
|
|
1729
|
+
maxSellAmount: resolvedMaxSellAmount,
|
|
1730
|
+
});
|
|
1731
|
+
const resolvedValidTo = getCowSwapValidTo(validTo);
|
|
1732
|
+
const params = {
|
|
1733
|
+
chainId: account.chainId,
|
|
1734
|
+
sellToken: swapQuote.vaultIn,
|
|
1735
|
+
buyToken: swapQuote.tokenOut.address,
|
|
1736
|
+
sellAmount: orderAmounts.sellAmount,
|
|
1737
|
+
buyAmount: orderAmounts.buyAmount,
|
|
1738
|
+
feeAmount: orderAmounts.feeAmount,
|
|
1739
|
+
quoteId: orderAmounts.quoteId,
|
|
1740
|
+
slippageBips: Math.round((slippage ?? swapQuote.slippage) * 100),
|
|
1741
|
+
validTo: resolvedValidTo,
|
|
1742
|
+
orderKind: orderKind ?? (isTargetDebt ? "buy" : "sell"),
|
|
1743
|
+
wrapper: {
|
|
1744
|
+
owner: account.owner,
|
|
1745
|
+
account: swapQuote.accountOut,
|
|
1746
|
+
deadline: resolvedValidTo,
|
|
1747
|
+
borrowVault: swapQuote.receiver,
|
|
1748
|
+
collateralVault: swapQuote.vaultIn,
|
|
1749
|
+
collateralAmount: orderAmounts.sellAmount,
|
|
1750
|
+
},
|
|
1751
|
+
};
|
|
1752
|
+
return [
|
|
1753
|
+
{
|
|
1754
|
+
type: "cowSwap",
|
|
1755
|
+
kind: "closePosition",
|
|
1756
|
+
chainId: account.chainId,
|
|
1757
|
+
params,
|
|
1758
|
+
},
|
|
1759
|
+
];
|
|
1760
|
+
}
|
|
1761
|
+
planCancelClosePositionWithCow(args) {
|
|
1762
|
+
const config = getCowSwapChainConfig(args.chainId);
|
|
1763
|
+
if (!config && (!args.wrapperAddress || args.nonceNamespace == null)) {
|
|
1764
|
+
throw new Error(`CoW Swap not supported on chain ${args.chainId}`);
|
|
1765
|
+
}
|
|
1766
|
+
const wrapperAddress = args.wrapperAddress ?? config?.closePositionWrapper;
|
|
1767
|
+
const nonceNamespace = args.nonceNamespace ??
|
|
1768
|
+
(wrapperAddress ? computeNonceNamespace(wrapperAddress) : undefined);
|
|
1769
|
+
if (nonceNamespace == null) {
|
|
1770
|
+
throw new Error("nonceNamespace is required when wrapperAddress is not provided");
|
|
1771
|
+
}
|
|
1772
|
+
const params = {
|
|
1773
|
+
chainId: args.chainId,
|
|
1774
|
+
owner: getAddress(args.owner),
|
|
1775
|
+
nonce: args.nonce,
|
|
1776
|
+
nonceNamespace,
|
|
1777
|
+
wrapperAddress,
|
|
1778
|
+
};
|
|
1779
|
+
return [
|
|
1780
|
+
{
|
|
1781
|
+
type: "cowSwap",
|
|
1782
|
+
kind: "cancelClosePosition",
|
|
1783
|
+
chainId: args.chainId,
|
|
1784
|
+
params,
|
|
1785
|
+
},
|
|
1786
|
+
];
|
|
1787
|
+
}
|
|
1532
1788
|
/**
|
|
1533
1789
|
* Builds a transaction plan for depositing into a vault using tokens from the user's wallet, going through a swap.
|
|
1534
1790
|
* The approval is given to SwapVerifier (not the vault), then transferFromSender is used in the batch
|
|
@@ -1544,6 +1800,7 @@ export class ExecutionService {
|
|
|
1544
1800
|
*/
|
|
1545
1801
|
planDepositWithSwapFromWallet(args) {
|
|
1546
1802
|
const { swapQuote, amount, tokenIn, account, enableCollateral, wrappedNativeInfo, } = args;
|
|
1803
|
+
assertNonCowSwapQuote(swapQuote, "planDepositWithSwapFromWallet");
|
|
1547
1804
|
const plan = [];
|
|
1548
1805
|
// Approval goes to the transferFromSender contract (which uses permit2 transferFrom internally)
|
|
1549
1806
|
plan.push({
|
|
@@ -1585,6 +1842,7 @@ export class ExecutionService {
|
|
|
1585
1842
|
*/
|
|
1586
1843
|
planSwapFromWallet(args) {
|
|
1587
1844
|
const { swapQuote, amount, tokenIn, account, wrappedNativeInfo } = args;
|
|
1845
|
+
assertNonCowSwapQuote(swapQuote, "planSwapFromWallet");
|
|
1588
1846
|
const plan = [];
|
|
1589
1847
|
plan.push({
|
|
1590
1848
|
type: "requiredApproval",
|
|
@@ -1605,6 +1863,7 @@ export class ExecutionService {
|
|
|
1605
1863
|
}
|
|
1606
1864
|
planSwapAndBorrowFromWallet(args) {
|
|
1607
1865
|
const { swapQuote, amount, tokenIn, account, borrowVault, borrowAmount, borrowAccount = swapQuote.accountOut, receiver = account.owner, collateralVault = swapQuote.receiver, wrappedNativeInfo, } = args;
|
|
1866
|
+
assertNonCowSwapQuote(swapQuote, "planSwapAndBorrowFromWallet");
|
|
1608
1867
|
const plan = [];
|
|
1609
1868
|
plan.push({
|
|
1610
1869
|
type: "requiredApproval",
|
|
@@ -1636,6 +1895,7 @@ export class ExecutionService {
|
|
|
1636
1895
|
}
|
|
1637
1896
|
planSwapAndRepayFromWallet(args) {
|
|
1638
1897
|
const { swapQuote, amount, tokenIn, account, liabilityVault = swapQuote.receiver, repayAccount = swapQuote.accountOut, isMax, cleanupOnMax = false, wrappedNativeInfo, } = args;
|
|
1898
|
+
assertNonCowSwapQuote(swapQuote, "planSwapAndRepayFromWallet");
|
|
1639
1899
|
const plan = [];
|
|
1640
1900
|
plan.push({
|
|
1641
1901
|
type: "requiredApproval",
|
|
@@ -1672,6 +1932,7 @@ export class ExecutionService {
|
|
|
1672
1932
|
}
|
|
1673
1933
|
planWithdrawAndSwap(args) {
|
|
1674
1934
|
const { account, vault, assets, owner, swapQuote } = args;
|
|
1935
|
+
assertNonCowSwapQuote(args.swapQuote, "planWithdrawAndSwap");
|
|
1675
1936
|
const batchItems = this.encodeWithdrawAndSwap({
|
|
1676
1937
|
chainId: account.chainId,
|
|
1677
1938
|
vault,
|
|
@@ -1684,6 +1945,7 @@ export class ExecutionService {
|
|
|
1684
1945
|
}
|
|
1685
1946
|
planRedeemAndSwap(args) {
|
|
1686
1947
|
const { account, vault, shares, owner, swapQuote } = args;
|
|
1948
|
+
assertNonCowSwapQuote(args.swapQuote, "planRedeemAndSwap");
|
|
1687
1949
|
const batchItems = this.encodeRedeemAndSwap({
|
|
1688
1950
|
chainId: account.chainId,
|
|
1689
1951
|
vault,
|
|
@@ -1704,6 +1966,7 @@ export class ExecutionService {
|
|
|
1704
1966
|
*/
|
|
1705
1967
|
planSwapCollateral(args) {
|
|
1706
1968
|
const { swapQuote, account, swapperMode } = args;
|
|
1969
|
+
assertNonCowSwapQuote(swapQuote, "planSwapCollateral", "planSwapCollateralWithCoW");
|
|
1707
1970
|
const plan = [];
|
|
1708
1971
|
// Check if source collateral needs to be disabled (when all is swapped)
|
|
1709
1972
|
// Default: when position is not available, assume amounts are zero, so we don't disable collateral
|
|
@@ -1727,6 +1990,47 @@ export class ExecutionService {
|
|
|
1727
1990
|
plan.push(...this.convertBatchItemsToPlan(batchItems, "swapCollateral"));
|
|
1728
1991
|
return plan;
|
|
1729
1992
|
}
|
|
1993
|
+
planSwapCollateralWithCoW(args) {
|
|
1994
|
+
const { swapQuote, account, slippage, validTo, disableSourceCollateral } = args;
|
|
1995
|
+
assertCowSwapQuote(swapQuote, "planSwapCollateralWithCoW");
|
|
1996
|
+
const orderAmounts = getCowSwapQuoteOrderAmounts(swapQuote, {
|
|
1997
|
+
slippage,
|
|
1998
|
+
slippageTarget: "buyAmount",
|
|
1999
|
+
});
|
|
2000
|
+
const sourcePosition = account.getPosition(swapQuote.accountIn, swapQuote.vaultIn);
|
|
2001
|
+
const resolvedValidTo = getCowSwapValidTo(validTo);
|
|
2002
|
+
const params = {
|
|
2003
|
+
chainId: account.chainId,
|
|
2004
|
+
sellToken: swapQuote.vaultIn,
|
|
2005
|
+
buyToken: swapQuote.receiver,
|
|
2006
|
+
sellAmount: orderAmounts.sellAmount,
|
|
2007
|
+
buyAmount: orderAmounts.buyAmount,
|
|
2008
|
+
feeAmount: orderAmounts.feeAmount,
|
|
2009
|
+
quoteId: orderAmounts.quoteId,
|
|
2010
|
+
slippageBips: Math.round((slippage ?? swapQuote.slippage) * 100),
|
|
2011
|
+
validTo: resolvedValidTo,
|
|
2012
|
+
wrapper: {
|
|
2013
|
+
owner: account.owner,
|
|
2014
|
+
account: swapQuote.accountOut,
|
|
2015
|
+
deadline: resolvedValidTo,
|
|
2016
|
+
fromVault: swapQuote.vaultIn,
|
|
2017
|
+
toVault: swapQuote.receiver,
|
|
2018
|
+
fromAmount: orderAmounts.sellAmount,
|
|
2019
|
+
disableSourceCollateral: disableSourceCollateral ??
|
|
2020
|
+
(sourcePosition
|
|
2021
|
+
? sourcePosition.shares <= orderAmounts.sellAmount
|
|
2022
|
+
: false),
|
|
2023
|
+
},
|
|
2024
|
+
};
|
|
2025
|
+
return [
|
|
2026
|
+
{
|
|
2027
|
+
type: "cowSwap",
|
|
2028
|
+
kind: "swapCollateral",
|
|
2029
|
+
chainId: account.chainId,
|
|
2030
|
+
params,
|
|
2031
|
+
},
|
|
2032
|
+
];
|
|
2033
|
+
}
|
|
1730
2034
|
/**
|
|
1731
2035
|
* Builds a transaction plan for swapping debt from one liability vault to another (borrow from source → swap → repay to destination).
|
|
1732
2036
|
*
|
|
@@ -1737,6 +2041,7 @@ export class ExecutionService {
|
|
|
1737
2041
|
*/
|
|
1738
2042
|
planSwapDebt(args) {
|
|
1739
2043
|
const { swapQuote, account, swapperMode } = args;
|
|
2044
|
+
assertNonCowSwapQuote(swapQuote, "planSwapDebt");
|
|
1740
2045
|
const plan = [];
|
|
1741
2046
|
const liabilityPosition = account?.getPosition(swapQuote.accountIn, swapQuote.receiver);
|
|
1742
2047
|
const isMax = liabilityPosition
|
|
@@ -1925,6 +2230,7 @@ export class ExecutionService {
|
|
|
1925
2230
|
*/
|
|
1926
2231
|
planMultiplyWithSwap(args) {
|
|
1927
2232
|
const { collateralVault, collateralAmount, collateralAsset, collateralShareSource, collateralWrappedNativeInfo, account, swapQuote, swapperMode, } = args;
|
|
2233
|
+
assertNonCowSwapQuote(swapQuote, "planMultiplyWithSwap", "planOpenPositionWithCoW");
|
|
1928
2234
|
const plan = [];
|
|
1929
2235
|
// 1. Check if collateral approval is needed (only if depositing collateral)
|
|
1930
2236
|
if (!collateralShareSource && collateralAmount > 0n) {
|
|
@@ -1985,6 +2291,47 @@ export class ExecutionService {
|
|
|
1985
2291
|
plan.push(...this.convertBatchItemsToPlan(batchItems, "multiplyWithSwap"));
|
|
1986
2292
|
return plan;
|
|
1987
2293
|
}
|
|
2294
|
+
planOpenPositionWithCoW(args) {
|
|
2295
|
+
const { account, collateralVault, collateralAmount, collateralAsset, swapQuote, slippage, validTo, } = args;
|
|
2296
|
+
assertCowSwapQuote(swapQuote, "planOpenPositionWithCoW");
|
|
2297
|
+
if (swapQuote.accountIn !== swapQuote.accountOut) {
|
|
2298
|
+
throw new Error("Account in and account out must be the same");
|
|
2299
|
+
}
|
|
2300
|
+
const orderAmounts = getCowSwapQuoteOrderAmounts(swapQuote, {
|
|
2301
|
+
slippage,
|
|
2302
|
+
slippageTarget: "buyAmount",
|
|
2303
|
+
});
|
|
2304
|
+
const resolvedValidTo = getCowSwapValidTo(validTo);
|
|
2305
|
+
const params = {
|
|
2306
|
+
chainId: account.chainId,
|
|
2307
|
+
sellToken: swapQuote.tokenIn.address,
|
|
2308
|
+
buyToken: swapQuote.receiver,
|
|
2309
|
+
sellAmount: orderAmounts.sellAmount,
|
|
2310
|
+
buyAmount: orderAmounts.buyAmount,
|
|
2311
|
+
feeAmount: orderAmounts.feeAmount,
|
|
2312
|
+
quoteId: orderAmounts.quoteId,
|
|
2313
|
+
slippageBips: Math.round((slippage ?? swapQuote.slippage) * 100),
|
|
2314
|
+
validTo: resolvedValidTo,
|
|
2315
|
+
collateralToken: collateralAsset,
|
|
2316
|
+
wrapper: {
|
|
2317
|
+
owner: account.owner,
|
|
2318
|
+
account: swapQuote.accountIn,
|
|
2319
|
+
deadline: resolvedValidTo,
|
|
2320
|
+
collateralVault,
|
|
2321
|
+
borrowVault: swapQuote.vaultIn,
|
|
2322
|
+
collateralAmount,
|
|
2323
|
+
borrowAmount: orderAmounts.sellAmount,
|
|
2324
|
+
},
|
|
2325
|
+
};
|
|
2326
|
+
return [
|
|
2327
|
+
{
|
|
2328
|
+
type: "cowSwap",
|
|
2329
|
+
kind: "openPosition",
|
|
2330
|
+
chainId: account.chainId,
|
|
2331
|
+
params,
|
|
2332
|
+
},
|
|
2333
|
+
];
|
|
2334
|
+
}
|
|
1988
2335
|
/**
|
|
1989
2336
|
* Builds a transaction plan for a multiply/leverage position when liability and long asset are the same (no swap).
|
|
1990
2337
|
* Flow: optional collateral deposit → enable controller → borrow to long vault → skim → enable collateral on long vault.
|