@eulerxyz/euler-v2-sdk 0.2.2-beta → 0.2.4-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/Account.d.ts +1 -1
- package/dist/src/entities/Account.d.ts.map +1 -1
- package/dist/src/entities/Account.js +28 -38
- package/dist/src/entities/Account.js.map +1 -1
- 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/Portfolio.d.ts +3 -3
- package/dist/src/entities/Portfolio.d.ts.map +1 -1
- package/dist/src/entities/Portfolio.js +8 -4
- package/dist/src/entities/Portfolio.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/accountService/adapters/accountV3Adapter/accountV3Adapter.d.ts +2 -2
- package/dist/src/services/accountService/adapters/accountV3Adapter/accountV3Adapter.d.ts.map +1 -1
- package/dist/src/services/accountService/adapters/accountV3Adapter/accountV3Adapter.js +3 -3
- package/dist/src/services/accountService/adapters/accountV3Adapter/accountV3Adapter.js.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 +390 -0
- package/dist/src/services/executionService/cowExecutor.js.map +1 -0
- package/dist/src/services/executionService/cowSwapHelpers.d.ts +254 -0
- package/dist/src/services/executionService/cowSwapHelpers.d.ts.map +1 -0
- package/dist/src/services/executionService/cowSwapHelpers.js +524 -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 +368 -15
- 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 +5 -3
- package/dist/src/services/executionService/index.d.ts.map +1 -1
- package/dist/src/services/executionService/index.js +3 -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/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 +2 -2
- package/dist/src/services/swapService/index.d.ts.map +1 -1
- package/dist/src/services/swapService/index.js.map +1 -1
- 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 +217 -7
- 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/adapters/eVaultV3Adapter/eVaultV3AdapterConversions.d.ts.map +1 -1
- package/dist/src/services/vaults/eVaultService/adapters/eVaultV3Adapter/eVaultV3AdapterConversions.js +103 -140
- package/dist/src/services/vaults/eVaultService/adapters/eVaultV3Adapter/eVaultV3AdapterConversions.js.map +1 -1
- package/dist/src/services/vaults/eVaultService/adapters/eVaultV3Adapter/eVaultV3AdapterTypes.d.ts +33 -33
- package/dist/src/services/vaults/eVaultService/adapters/eVaultV3Adapter/eVaultV3AdapterTypes.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/adapters/eulerEarnV3Adapter.d.ts +9 -9
- package/dist/src/services/vaults/eulerEarnService/adapters/eulerEarnV3Adapter.d.ts.map +1 -1
- package/dist/src/services/vaults/eulerEarnService/adapters/eulerEarnV3Adapter.js +10 -10
- package/dist/src/services/vaults/eulerEarnService/adapters/eulerEarnV3Adapter.js.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/accountComputations.d.ts.map +1 -1
- package/dist/src/utils/accountComputations.js +4 -2
- package/dist/src/utils/accountComputations.js.map +1 -1
- package/dist/src/utils/eulerLabels.d.ts.map +1 -1
- package/dist/src/utils/eulerLabels.js +4 -1
- package/dist/src/utils/eulerLabels.js.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/dist/src/utils/parsing.d.ts +17 -1
- package/dist/src/utils/parsing.d.ts.map +1 -1
- package/dist/src/utils/parsing.js +39 -0
- package/dist/src/utils/parsing.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,19 +1,115 @@
|
|
|
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, 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 { adjustForInterest, getSwapInputAmount, } from "../swapService/swapVerification.js";
|
|
13
|
-
import { SwapperMode } from "../swapService/swapServiceTypes.js";
|
|
14
16
|
const TOKENS_REQUIRING_ZERO_APPROVAL_RESET = {
|
|
15
17
|
1: [getAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7")],
|
|
16
18
|
};
|
|
19
|
+
const COWSWAP_ORDER_DEADLINE_SECONDS = 900;
|
|
20
|
+
function isCowSwapQuote(quote) {
|
|
21
|
+
return quote.route?.some((hop) => hop.providerName?.toLowerCase().includes("cow"));
|
|
22
|
+
}
|
|
23
|
+
function assertNonCowSwapQuote(quote, planFunctionName, cowPlanFunctionName) {
|
|
24
|
+
if (!isCowSwapQuote(quote))
|
|
25
|
+
return;
|
|
26
|
+
const suffix = cowPlanFunctionName
|
|
27
|
+
? ` Use ${cowPlanFunctionName} instead.`
|
|
28
|
+
: " Use the appropriate CoW plan function instead.";
|
|
29
|
+
throw new Error(`ExecutionService.${planFunctionName} does not support CoW swap quotes.${suffix}`);
|
|
30
|
+
}
|
|
31
|
+
function assertCowSwapQuote(quote, planFunctionName) {
|
|
32
|
+
if (isCowSwapQuote(quote))
|
|
33
|
+
return;
|
|
34
|
+
throw new Error(`ExecutionService.${planFunctionName} requires a CoW swap quote.`);
|
|
35
|
+
}
|
|
36
|
+
function parseCowSwapAmount(field, value) {
|
|
37
|
+
if (typeof value !== "string" || !/^\d+$/.test(value)) {
|
|
38
|
+
throw new Error(`CoW quote ${field} missing or invalid`);
|
|
39
|
+
}
|
|
40
|
+
return BigInt(value);
|
|
41
|
+
}
|
|
42
|
+
function normalizeCowSwapQuoteId(value) {
|
|
43
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
47
|
+
const parsed = Number(value);
|
|
48
|
+
if (Number.isSafeInteger(parsed))
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
function parseSlippagePercent(slippage) {
|
|
54
|
+
if (!Number.isFinite(slippage) || slippage <= 0) {
|
|
55
|
+
return { slippageUnits: 0n, denominator: 1n };
|
|
56
|
+
}
|
|
57
|
+
const slippageString = slippage.toLocaleString("en-US", {
|
|
58
|
+
useGrouping: false,
|
|
59
|
+
maximumFractionDigits: 20,
|
|
60
|
+
});
|
|
61
|
+
const [whole = "0", fraction = ""] = slippageString.split(".");
|
|
62
|
+
const scale = 10n ** BigInt(fraction.length);
|
|
63
|
+
const slippageUnits = BigInt(whole) * scale + BigInt(fraction || "0");
|
|
64
|
+
return {
|
|
65
|
+
slippageUnits,
|
|
66
|
+
denominator: 100n * scale,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function reduceBySlippage(amount, slippage = 0) {
|
|
70
|
+
const { slippageUnits, denominator } = parseSlippagePercent(slippage);
|
|
71
|
+
if (slippageUnits >= denominator)
|
|
72
|
+
return 0n;
|
|
73
|
+
return (amount * (denominator - slippageUnits)) / denominator;
|
|
74
|
+
}
|
|
75
|
+
function increaseBySlippage(amount, slippage = 0) {
|
|
76
|
+
const { slippageUnits, denominator } = parseSlippagePercent(slippage);
|
|
77
|
+
return ((amount * (denominator + slippageUnits) + denominator - 1n) / denominator);
|
|
78
|
+
}
|
|
79
|
+
function getCowSwapQuoteOrderAmounts(quote, options = {}) {
|
|
80
|
+
const providerData = quote.providerData;
|
|
81
|
+
const quoteSellAmount = parseCowSwapAmount("providerData.sellAmount", providerData?.sellAmount);
|
|
82
|
+
const buyAmount = parseCowSwapAmount("providerData.buyAmount", providerData?.buyAmount);
|
|
83
|
+
const feeAmount = parseCowSwapAmount("providerData.feeAmount", providerData?.feeAmount);
|
|
84
|
+
const quoteId = normalizeCowSwapQuoteId(providerData?.quoteId);
|
|
85
|
+
if (quoteSellAmount <= 0n || buyAmount <= 0n) {
|
|
86
|
+
throw new Error("CoW quote order amount must be positive");
|
|
87
|
+
}
|
|
88
|
+
const slippage = options.slippage ?? quote.slippage ?? 0;
|
|
89
|
+
const orderSellAmount = quoteSellAmount + feeAmount;
|
|
90
|
+
const slippageAdjustedSellAmount = options.slippageTarget === "sellAmount"
|
|
91
|
+
? increaseBySlippage(orderSellAmount, slippage)
|
|
92
|
+
: orderSellAmount;
|
|
93
|
+
const adjustedSellAmount = options.maxSellAmount !== undefined &&
|
|
94
|
+
slippageAdjustedSellAmount > options.maxSellAmount
|
|
95
|
+
? options.maxSellAmount
|
|
96
|
+
: slippageAdjustedSellAmount;
|
|
97
|
+
const adjustedBuyAmount = options.slippageTarget === "buyAmount"
|
|
98
|
+
? reduceBySlippage(buyAmount, slippage)
|
|
99
|
+
: buyAmount;
|
|
100
|
+
if (adjustedSellAmount <= 0n || adjustedBuyAmount <= 0n) {
|
|
101
|
+
throw new Error("CoW quote order amount must be positive");
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
sellAmount: adjustedSellAmount,
|
|
105
|
+
buyAmount: adjustedBuyAmount,
|
|
106
|
+
feeAmount,
|
|
107
|
+
quoteId,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function getCowSwapValidTo(validTo) {
|
|
111
|
+
return (validTo ?? Math.floor(Date.now() / 1000) + COWSWAP_ORDER_DEADLINE_SECONDS);
|
|
112
|
+
}
|
|
17
113
|
function requiresZeroApprovalReset(chainId, token) {
|
|
18
114
|
return (TOKENS_REQUIRING_ZERO_APPROVAL_RESET[chainId]?.some((resetToken) => resetToken === getAddress(token)) ?? false);
|
|
19
115
|
}
|
|
@@ -35,7 +131,9 @@ function hasShareConversion(vault) {
|
|
|
35
131
|
return (typeof vault === "object" &&
|
|
36
132
|
vault !== null &&
|
|
37
133
|
"convertToShares" in vault &&
|
|
38
|
-
typeof vault.convertToShares === "function"
|
|
134
|
+
typeof vault.convertToShares === "function" &&
|
|
135
|
+
"previewWithdraw" in vault &&
|
|
136
|
+
typeof vault.previewWithdraw === "function");
|
|
39
137
|
}
|
|
40
138
|
function getStateTransition(item) {
|
|
41
139
|
try {
|
|
@@ -199,16 +297,57 @@ export class ExecutionService {
|
|
|
199
297
|
}
|
|
200
298
|
/** Derive storage overrides needed to simulate the plan against the current account state. */
|
|
201
299
|
async deriveStateOverrides(chainId, account, transactionPlan, options) {
|
|
300
|
+
assertNoCowSwapPlanItems(transactionPlan, "deriveStateOverrides");
|
|
202
301
|
return deriveStateOverrides(this.getSimulationContext(), chainId, account, transactionPlan, options);
|
|
203
302
|
}
|
|
204
303
|
/** Simulate the full transaction plan, including approval resolution and plugin-aware batch execution. */
|
|
205
304
|
async simulateTransactionPlan(chainId, account, transactionPlan, options) {
|
|
305
|
+
assertNoCowSwapPlanItems(transactionPlan, "simulateTransactionPlan");
|
|
206
306
|
const processedPlan = await this.processPlanPlugins(transactionPlan, account, chainId);
|
|
207
307
|
const owner = this.getAccountOwner(account);
|
|
208
308
|
return simulateTransactionPlan(this.getSimulationContext(), chainId, owner, processedPlan, options);
|
|
209
309
|
}
|
|
310
|
+
/**
|
|
311
|
+
* Simulate a {@link TransactionPlanPrepared} envelope. Plugins and approval
|
|
312
|
+
* resolution have already run via {@link prepareTransactionPlan}, so this
|
|
313
|
+
* skips the plugin pipeline and uses the envelope's chainId/account context
|
|
314
|
+
* directly — no re-fetches of plugin-side data on each click.
|
|
315
|
+
*/
|
|
316
|
+
async simulatePreparedTransactionPlan(prepared, options) {
|
|
317
|
+
assertNoCowSwapPlanItems(prepared.plan, "simulatePreparedTransactionPlan");
|
|
318
|
+
const owner = this.getAccountOwner(prepared.account);
|
|
319
|
+
return simulateTransactionPlan(this.getSimulationContext(), prepared.chainId, owner, prepared.plan, options);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Run plugins and resolve required approvals up front, packaging the result
|
|
323
|
+
* with its execution context. Simulate and execute can be called against the
|
|
324
|
+
* returned envelope repeatedly without re-running plugins or refetching
|
|
325
|
+
* wallet allowances.
|
|
326
|
+
*/
|
|
327
|
+
async prepareTransactionPlan(args) {
|
|
328
|
+
const { plan, chainId, account, usePermit2 = true, unlimitedApproval = false, } = args;
|
|
329
|
+
assertNoCowSwapPlanItems(plan, "prepareTransactionPlan");
|
|
330
|
+
const processedPlan = await this.processPlanPlugins(plan, account, chainId);
|
|
331
|
+
const owner = this.getAccountOwner(account);
|
|
332
|
+
const resolvedPlan = await this.resolveRequiredApprovals({
|
|
333
|
+
plan: processedPlan,
|
|
334
|
+
chainId,
|
|
335
|
+
account: owner,
|
|
336
|
+
usePermit2,
|
|
337
|
+
unlimitedApproval,
|
|
338
|
+
});
|
|
339
|
+
return {
|
|
340
|
+
__prepared: true,
|
|
341
|
+
plan: resolvedPlan,
|
|
342
|
+
chainId,
|
|
343
|
+
account,
|
|
344
|
+
usePermit2,
|
|
345
|
+
unlimitedApproval,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
210
348
|
/** Estimate gas for the full transaction plan after applying the same simulation pipeline used for execution. */
|
|
211
349
|
async estimateGasForTransactionPlan(chainId, account, transactionPlan, options) {
|
|
350
|
+
assertNoCowSwapPlanItems(transactionPlan, "estimateGasForTransactionPlan");
|
|
212
351
|
const processedPlan = await this.processPlanPlugins(transactionPlan, account, chainId);
|
|
213
352
|
const owner = this.getAccountOwner(account);
|
|
214
353
|
return estimateGasForTransactionPlan(this.getSimulationContext(), chainId, owner, processedPlan, options);
|
|
@@ -219,15 +358,58 @@ export class ExecutionService {
|
|
|
219
358
|
if (!providerService) {
|
|
220
359
|
throw new Error("ExecutionService.executeTransactionPlan requires a providerService. Pass it to the ExecutionService constructor or call setProviderService().");
|
|
221
360
|
}
|
|
361
|
+
assertNoCowSwapPlanItems(args.plan, "executeTransactionPlan");
|
|
362
|
+
const processedPlan = await this.processPlanPlugins(args.plan, args.account, args.chainId);
|
|
363
|
+
assertNoCowSwapPlanItems(processedPlan, "executeTransactionPlan");
|
|
222
364
|
const helperArgs = {
|
|
223
365
|
...args,
|
|
224
|
-
plan:
|
|
366
|
+
plan: processedPlan,
|
|
225
367
|
executionService: this,
|
|
226
368
|
deploymentService: this.deploymentService,
|
|
227
369
|
providerService,
|
|
228
370
|
};
|
|
229
371
|
return executeTransactionPlan(helperArgs);
|
|
230
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Execute a {@link TransactionPlanPrepared} envelope. Plugins and approval
|
|
375
|
+
* resolution were already applied by {@link prepareTransactionPlan}, so
|
|
376
|
+
* this skips both — no per-execute plugin re-runs or wallet re-fetch.
|
|
377
|
+
*/
|
|
378
|
+
async executePreparedTransactionPlan(args) {
|
|
379
|
+
const { providerService } = this;
|
|
380
|
+
if (!providerService) {
|
|
381
|
+
throw new Error("ExecutionService.executePreparedTransactionPlan requires a providerService. Pass it to the ExecutionService constructor or call setProviderService().");
|
|
382
|
+
}
|
|
383
|
+
const { prepared, sendTransaction, signTypedData, onProgress } = args;
|
|
384
|
+
assertNoCowSwapPlanItems(prepared.plan, "executePreparedTransactionPlan");
|
|
385
|
+
const helperArgs = {
|
|
386
|
+
plan: prepared.plan,
|
|
387
|
+
chainId: prepared.chainId,
|
|
388
|
+
account: prepared.account,
|
|
389
|
+
usePermit2: prepared.usePermit2,
|
|
390
|
+
unlimitedApproval: prepared.unlimitedApproval,
|
|
391
|
+
sendTransaction,
|
|
392
|
+
signTypedData,
|
|
393
|
+
onProgress,
|
|
394
|
+
alreadyResolved: true,
|
|
395
|
+
executionService: this,
|
|
396
|
+
deploymentService: this.deploymentService,
|
|
397
|
+
providerService,
|
|
398
|
+
};
|
|
399
|
+
return executeTransactionPlan(helperArgs);
|
|
400
|
+
}
|
|
401
|
+
async executeCowSwapTransactionPlan(args) {
|
|
402
|
+
const { providerService } = this;
|
|
403
|
+
if (!providerService) {
|
|
404
|
+
throw new Error("ExecutionService.executeCowSwapTransactionPlan requires a providerService. Pass it to the ExecutionService constructor or call setProviderService().");
|
|
405
|
+
}
|
|
406
|
+
return executeCowSwapTransactionPlan({
|
|
407
|
+
...args,
|
|
408
|
+
plan: await this.processPlanPlugins(args.plan, args.account, args.chainId),
|
|
409
|
+
deploymentService: this.deploymentService,
|
|
410
|
+
providerService,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
231
413
|
getSimulationContext() {
|
|
232
414
|
return {
|
|
233
415
|
deploymentService: this.deploymentService,
|
|
@@ -786,6 +968,9 @@ export class ExecutionService {
|
|
|
786
968
|
});
|
|
787
969
|
}
|
|
788
970
|
}
|
|
971
|
+
else if (isCowSwapPlanItem(item)) {
|
|
972
|
+
throw new Error("ExecutionService.mergePlans cannot merge CoW swap plan items. Merge these plans manually to preserve CoW order boundaries.");
|
|
973
|
+
}
|
|
789
974
|
else {
|
|
790
975
|
throw new Error("ExecutionService.mergePlans cannot merge contractCall plan items. Merge these plans manually to preserve call boundaries.");
|
|
791
976
|
}
|
|
@@ -882,6 +1067,7 @@ export class ExecutionService {
|
|
|
882
1067
|
*/
|
|
883
1068
|
resolveRequiredApprovalsWithWallet(args) {
|
|
884
1069
|
const { plan, wallet, chainId, usePermit2 = true, unlimitedApproval = false, } = args;
|
|
1070
|
+
assertNoCowSwapPlanItems(plan, "resolveRequiredApprovalsWithWallet");
|
|
885
1071
|
const deployment = this.deploymentService.getDeployment(chainId);
|
|
886
1072
|
const permit2 = deployment.addresses.coreAddrs.permit2;
|
|
887
1073
|
for (const item of plan) {
|
|
@@ -988,11 +1174,17 @@ export class ExecutionService {
|
|
|
988
1174
|
*/
|
|
989
1175
|
async resolveRequiredApprovals(args) {
|
|
990
1176
|
const { plan, chainId, account, usePermit2 = true, unlimitedApproval = false, } = args;
|
|
1177
|
+
assertNoCowSwapPlanItems(plan, "resolveRequiredApprovals");
|
|
1178
|
+
// Filter transaction plan for only RequiredApproval items
|
|
1179
|
+
const requiredApprovals = plan.filter((item) => item.type === "requiredApproval");
|
|
1180
|
+
// No approvals in the plan → nothing to resolve, no wallet fetch needed.
|
|
1181
|
+
// Withdraw/redeem flows always hit this path (they don't transfer assets in).
|
|
1182
|
+
if (requiredApprovals.length === 0) {
|
|
1183
|
+
return plan;
|
|
1184
|
+
}
|
|
991
1185
|
if (!this.walletService) {
|
|
992
1186
|
throw new Error("ExecutionService.resolveRequiredApprovals requires a walletService. Pass it to the ExecutionService constructor or call setWalletService().");
|
|
993
1187
|
}
|
|
994
|
-
// Filter transaction plan for only RequiredApproval items
|
|
995
|
-
const requiredApprovals = plan.filter((item) => item.type === "requiredApproval");
|
|
996
1188
|
// Transform RequiredApprovals into AssetWithSpenders
|
|
997
1189
|
const assetSpendersMap = new Map();
|
|
998
1190
|
for (const approval of requiredApprovals) {
|
|
@@ -1196,7 +1388,7 @@ export class ExecutionService {
|
|
|
1196
1388
|
* @returns Array of transaction plan items (EVC batch; no approvals needed for redeem)
|
|
1197
1389
|
*/
|
|
1198
1390
|
planRedeem(args) {
|
|
1199
|
-
const { vault, receiver, owner, account, disableCollateral = false
|
|
1391
|
+
const { vault, receiver, owner, account, disableCollateral = false } = args;
|
|
1200
1392
|
const plan = [];
|
|
1201
1393
|
// Get position to check collateral state
|
|
1202
1394
|
const position = account?.getPosition(owner, vault);
|
|
@@ -1218,13 +1410,14 @@ export class ExecutionService {
|
|
|
1218
1410
|
return args.shares;
|
|
1219
1411
|
const vault = position?.vault;
|
|
1220
1412
|
if (!hasShareConversion(vault)) {
|
|
1221
|
-
throw new Error("planRedeem with assets requires account position vault state with
|
|
1413
|
+
throw new Error("planRedeem with assets requires account position vault state with previewWithdraw. Populate the account vaults before planning asset-denominated redeem.");
|
|
1222
1414
|
}
|
|
1223
|
-
// Asset-denominated redeem
|
|
1224
|
-
//
|
|
1225
|
-
//
|
|
1226
|
-
//
|
|
1227
|
-
|
|
1415
|
+
// Asset-denominated redeem must round shares UP so the user receives at
|
|
1416
|
+
// least the requested assets. `convertToShares` rounds down, leaving a
|
|
1417
|
+
// rounding dust shortfall. `previewWithdraw` mirrors the on-chain
|
|
1418
|
+
// previewWithdraw(uint256) — Math.mulDiv with Rounding.Ceil over the
|
|
1419
|
+
// virtual-deposit-adjusted total{Shares,Assets}.
|
|
1420
|
+
return vault.previewWithdraw(args.assets);
|
|
1228
1421
|
}
|
|
1229
1422
|
/**
|
|
1230
1423
|
* Builds a transaction plan for borrowing from a liability vault.
|
|
@@ -1497,6 +1690,7 @@ export class ExecutionService {
|
|
|
1497
1690
|
*/
|
|
1498
1691
|
planRepayWithSwap(args) {
|
|
1499
1692
|
const { swapQuote, account, cleanupOnMax = false, swapperMode } = args;
|
|
1693
|
+
assertNonCowSwapQuote(swapQuote, "planRepayWithSwap", "planClosePositionWithCow");
|
|
1500
1694
|
const plan = [];
|
|
1501
1695
|
const liabilityPosition = account?.getPosition(swapQuote.accountOut, swapQuote.receiver);
|
|
1502
1696
|
const fromPosition = account?.getPosition(swapQuote.accountIn, swapQuote.vaultIn);
|
|
@@ -1529,6 +1723,74 @@ export class ExecutionService {
|
|
|
1529
1723
|
plan.push(...this.convertBatchItemsToPlan(batchItems, "repayWithSwap"));
|
|
1530
1724
|
return plan;
|
|
1531
1725
|
}
|
|
1726
|
+
planClosePositionWithCow(args) {
|
|
1727
|
+
const { account, swapQuote, swapperMode = SwapperMode.TARGET_DEBT, slippage, validTo, orderKind, maxSellAmount, } = args;
|
|
1728
|
+
assertCowSwapQuote(swapQuote, "planClosePositionWithCow");
|
|
1729
|
+
const isTargetDebt = swapperMode === SwapperMode.TARGET_DEBT;
|
|
1730
|
+
const sourcePosition = account.getPosition(swapQuote.accountIn, swapQuote.vaultIn);
|
|
1731
|
+
const resolvedMaxSellAmount = maxSellAmount ?? (isTargetDebt ? sourcePosition?.shares : undefined);
|
|
1732
|
+
const orderAmounts = getCowSwapQuoteOrderAmounts(swapQuote, {
|
|
1733
|
+
slippage,
|
|
1734
|
+
slippageTarget: "sellAmount",
|
|
1735
|
+
maxSellAmount: resolvedMaxSellAmount,
|
|
1736
|
+
});
|
|
1737
|
+
const resolvedValidTo = getCowSwapValidTo(validTo);
|
|
1738
|
+
const params = {
|
|
1739
|
+
chainId: account.chainId,
|
|
1740
|
+
sellToken: swapQuote.vaultIn,
|
|
1741
|
+
buyToken: swapQuote.tokenOut.address,
|
|
1742
|
+
sellAmount: orderAmounts.sellAmount,
|
|
1743
|
+
buyAmount: orderAmounts.buyAmount,
|
|
1744
|
+
feeAmount: orderAmounts.feeAmount,
|
|
1745
|
+
quoteId: orderAmounts.quoteId,
|
|
1746
|
+
slippageBips: Math.round((slippage ?? swapQuote.slippage) * 100),
|
|
1747
|
+
validTo: resolvedValidTo,
|
|
1748
|
+
orderKind: orderKind ?? (isTargetDebt ? "buy" : "sell"),
|
|
1749
|
+
wrapper: {
|
|
1750
|
+
owner: account.owner,
|
|
1751
|
+
account: swapQuote.accountOut,
|
|
1752
|
+
deadline: resolvedValidTo,
|
|
1753
|
+
borrowVault: swapQuote.receiver,
|
|
1754
|
+
collateralVault: swapQuote.vaultIn,
|
|
1755
|
+
collateralAmount: orderAmounts.sellAmount,
|
|
1756
|
+
},
|
|
1757
|
+
};
|
|
1758
|
+
return [
|
|
1759
|
+
{
|
|
1760
|
+
type: "cowSwap",
|
|
1761
|
+
kind: "closePosition",
|
|
1762
|
+
chainId: account.chainId,
|
|
1763
|
+
params,
|
|
1764
|
+
},
|
|
1765
|
+
];
|
|
1766
|
+
}
|
|
1767
|
+
planCancelClosePositionWithCow(args) {
|
|
1768
|
+
const config = getCowSwapChainConfig(args.chainId);
|
|
1769
|
+
if (!config && (!args.wrapperAddress || args.nonceNamespace == null)) {
|
|
1770
|
+
throw new Error(`CoW Swap not supported on chain ${args.chainId}`);
|
|
1771
|
+
}
|
|
1772
|
+
const wrapperAddress = args.wrapperAddress ?? config?.closePositionWrapper;
|
|
1773
|
+
const nonceNamespace = args.nonceNamespace ??
|
|
1774
|
+
(wrapperAddress ? computeNonceNamespace(wrapperAddress) : undefined);
|
|
1775
|
+
if (nonceNamespace == null) {
|
|
1776
|
+
throw new Error("nonceNamespace is required when wrapperAddress is not provided");
|
|
1777
|
+
}
|
|
1778
|
+
const params = {
|
|
1779
|
+
chainId: args.chainId,
|
|
1780
|
+
owner: getAddress(args.owner),
|
|
1781
|
+
nonce: args.nonce,
|
|
1782
|
+
nonceNamespace,
|
|
1783
|
+
wrapperAddress,
|
|
1784
|
+
};
|
|
1785
|
+
return [
|
|
1786
|
+
{
|
|
1787
|
+
type: "cowSwap",
|
|
1788
|
+
kind: "cancelClosePosition",
|
|
1789
|
+
chainId: args.chainId,
|
|
1790
|
+
params,
|
|
1791
|
+
},
|
|
1792
|
+
];
|
|
1793
|
+
}
|
|
1532
1794
|
/**
|
|
1533
1795
|
* Builds a transaction plan for depositing into a vault using tokens from the user's wallet, going through a swap.
|
|
1534
1796
|
* The approval is given to SwapVerifier (not the vault), then transferFromSender is used in the batch
|
|
@@ -1544,6 +1806,7 @@ export class ExecutionService {
|
|
|
1544
1806
|
*/
|
|
1545
1807
|
planDepositWithSwapFromWallet(args) {
|
|
1546
1808
|
const { swapQuote, amount, tokenIn, account, enableCollateral, wrappedNativeInfo, } = args;
|
|
1809
|
+
assertNonCowSwapQuote(swapQuote, "planDepositWithSwapFromWallet");
|
|
1547
1810
|
const plan = [];
|
|
1548
1811
|
// Approval goes to the transferFromSender contract (which uses permit2 transferFrom internally)
|
|
1549
1812
|
plan.push({
|
|
@@ -1585,6 +1848,7 @@ export class ExecutionService {
|
|
|
1585
1848
|
*/
|
|
1586
1849
|
planSwapFromWallet(args) {
|
|
1587
1850
|
const { swapQuote, amount, tokenIn, account, wrappedNativeInfo } = args;
|
|
1851
|
+
assertNonCowSwapQuote(swapQuote, "planSwapFromWallet");
|
|
1588
1852
|
const plan = [];
|
|
1589
1853
|
plan.push({
|
|
1590
1854
|
type: "requiredApproval",
|
|
@@ -1605,6 +1869,7 @@ export class ExecutionService {
|
|
|
1605
1869
|
}
|
|
1606
1870
|
planSwapAndBorrowFromWallet(args) {
|
|
1607
1871
|
const { swapQuote, amount, tokenIn, account, borrowVault, borrowAmount, borrowAccount = swapQuote.accountOut, receiver = account.owner, collateralVault = swapQuote.receiver, wrappedNativeInfo, } = args;
|
|
1872
|
+
assertNonCowSwapQuote(swapQuote, "planSwapAndBorrowFromWallet");
|
|
1608
1873
|
const plan = [];
|
|
1609
1874
|
plan.push({
|
|
1610
1875
|
type: "requiredApproval",
|
|
@@ -1636,6 +1901,7 @@ export class ExecutionService {
|
|
|
1636
1901
|
}
|
|
1637
1902
|
planSwapAndRepayFromWallet(args) {
|
|
1638
1903
|
const { swapQuote, amount, tokenIn, account, liabilityVault = swapQuote.receiver, repayAccount = swapQuote.accountOut, isMax, cleanupOnMax = false, wrappedNativeInfo, } = args;
|
|
1904
|
+
assertNonCowSwapQuote(swapQuote, "planSwapAndRepayFromWallet");
|
|
1639
1905
|
const plan = [];
|
|
1640
1906
|
plan.push({
|
|
1641
1907
|
type: "requiredApproval",
|
|
@@ -1672,6 +1938,7 @@ export class ExecutionService {
|
|
|
1672
1938
|
}
|
|
1673
1939
|
planWithdrawAndSwap(args) {
|
|
1674
1940
|
const { account, vault, assets, owner, swapQuote } = args;
|
|
1941
|
+
assertNonCowSwapQuote(args.swapQuote, "planWithdrawAndSwap");
|
|
1675
1942
|
const batchItems = this.encodeWithdrawAndSwap({
|
|
1676
1943
|
chainId: account.chainId,
|
|
1677
1944
|
vault,
|
|
@@ -1684,6 +1951,7 @@ export class ExecutionService {
|
|
|
1684
1951
|
}
|
|
1685
1952
|
planRedeemAndSwap(args) {
|
|
1686
1953
|
const { account, vault, shares, owner, swapQuote } = args;
|
|
1954
|
+
assertNonCowSwapQuote(args.swapQuote, "planRedeemAndSwap");
|
|
1687
1955
|
const batchItems = this.encodeRedeemAndSwap({
|
|
1688
1956
|
chainId: account.chainId,
|
|
1689
1957
|
vault,
|
|
@@ -1704,6 +1972,7 @@ export class ExecutionService {
|
|
|
1704
1972
|
*/
|
|
1705
1973
|
planSwapCollateral(args) {
|
|
1706
1974
|
const { swapQuote, account, swapperMode } = args;
|
|
1975
|
+
assertNonCowSwapQuote(swapQuote, "planSwapCollateral", "planSwapCollateralWithCoW");
|
|
1707
1976
|
const plan = [];
|
|
1708
1977
|
// Check if source collateral needs to be disabled (when all is swapped)
|
|
1709
1978
|
// Default: when position is not available, assume amounts are zero, so we don't disable collateral
|
|
@@ -1727,6 +1996,47 @@ export class ExecutionService {
|
|
|
1727
1996
|
plan.push(...this.convertBatchItemsToPlan(batchItems, "swapCollateral"));
|
|
1728
1997
|
return plan;
|
|
1729
1998
|
}
|
|
1999
|
+
planSwapCollateralWithCoW(args) {
|
|
2000
|
+
const { swapQuote, account, slippage, validTo, disableSourceCollateral } = args;
|
|
2001
|
+
assertCowSwapQuote(swapQuote, "planSwapCollateralWithCoW");
|
|
2002
|
+
const orderAmounts = getCowSwapQuoteOrderAmounts(swapQuote, {
|
|
2003
|
+
slippage,
|
|
2004
|
+
slippageTarget: "buyAmount",
|
|
2005
|
+
});
|
|
2006
|
+
const sourcePosition = account.getPosition(swapQuote.accountIn, swapQuote.vaultIn);
|
|
2007
|
+
const resolvedValidTo = getCowSwapValidTo(validTo);
|
|
2008
|
+
const params = {
|
|
2009
|
+
chainId: account.chainId,
|
|
2010
|
+
sellToken: swapQuote.vaultIn,
|
|
2011
|
+
buyToken: swapQuote.receiver,
|
|
2012
|
+
sellAmount: orderAmounts.sellAmount,
|
|
2013
|
+
buyAmount: orderAmounts.buyAmount,
|
|
2014
|
+
feeAmount: orderAmounts.feeAmount,
|
|
2015
|
+
quoteId: orderAmounts.quoteId,
|
|
2016
|
+
slippageBips: Math.round((slippage ?? swapQuote.slippage) * 100),
|
|
2017
|
+
validTo: resolvedValidTo,
|
|
2018
|
+
wrapper: {
|
|
2019
|
+
owner: account.owner,
|
|
2020
|
+
account: swapQuote.accountOut,
|
|
2021
|
+
deadline: resolvedValidTo,
|
|
2022
|
+
fromVault: swapQuote.vaultIn,
|
|
2023
|
+
toVault: swapQuote.receiver,
|
|
2024
|
+
fromAmount: orderAmounts.sellAmount,
|
|
2025
|
+
disableSourceCollateral: disableSourceCollateral ??
|
|
2026
|
+
(sourcePosition
|
|
2027
|
+
? sourcePosition.shares <= orderAmounts.sellAmount
|
|
2028
|
+
: false),
|
|
2029
|
+
},
|
|
2030
|
+
};
|
|
2031
|
+
return [
|
|
2032
|
+
{
|
|
2033
|
+
type: "cowSwap",
|
|
2034
|
+
kind: "swapCollateral",
|
|
2035
|
+
chainId: account.chainId,
|
|
2036
|
+
params,
|
|
2037
|
+
},
|
|
2038
|
+
];
|
|
2039
|
+
}
|
|
1730
2040
|
/**
|
|
1731
2041
|
* Builds a transaction plan for swapping debt from one liability vault to another (borrow from source → swap → repay to destination).
|
|
1732
2042
|
*
|
|
@@ -1737,6 +2047,7 @@ export class ExecutionService {
|
|
|
1737
2047
|
*/
|
|
1738
2048
|
planSwapDebt(args) {
|
|
1739
2049
|
const { swapQuote, account, swapperMode } = args;
|
|
2050
|
+
assertNonCowSwapQuote(swapQuote, "planSwapDebt");
|
|
1740
2051
|
const plan = [];
|
|
1741
2052
|
const liabilityPosition = account?.getPosition(swapQuote.accountIn, swapQuote.receiver);
|
|
1742
2053
|
const isMax = liabilityPosition
|
|
@@ -1925,6 +2236,7 @@ export class ExecutionService {
|
|
|
1925
2236
|
*/
|
|
1926
2237
|
planMultiplyWithSwap(args) {
|
|
1927
2238
|
const { collateralVault, collateralAmount, collateralAsset, collateralShareSource, collateralWrappedNativeInfo, account, swapQuote, swapperMode, } = args;
|
|
2239
|
+
assertNonCowSwapQuote(swapQuote, "planMultiplyWithSwap", "planOpenPositionWithCoW");
|
|
1928
2240
|
const plan = [];
|
|
1929
2241
|
// 1. Check if collateral approval is needed (only if depositing collateral)
|
|
1930
2242
|
if (!collateralShareSource && collateralAmount > 0n) {
|
|
@@ -1985,6 +2297,47 @@ export class ExecutionService {
|
|
|
1985
2297
|
plan.push(...this.convertBatchItemsToPlan(batchItems, "multiplyWithSwap"));
|
|
1986
2298
|
return plan;
|
|
1987
2299
|
}
|
|
2300
|
+
planOpenPositionWithCoW(args) {
|
|
2301
|
+
const { account, collateralVault, collateralAmount, collateralAsset, swapQuote, slippage, validTo, } = args;
|
|
2302
|
+
assertCowSwapQuote(swapQuote, "planOpenPositionWithCoW");
|
|
2303
|
+
if (swapQuote.accountIn !== swapQuote.accountOut) {
|
|
2304
|
+
throw new Error("Account in and account out must be the same");
|
|
2305
|
+
}
|
|
2306
|
+
const orderAmounts = getCowSwapQuoteOrderAmounts(swapQuote, {
|
|
2307
|
+
slippage,
|
|
2308
|
+
slippageTarget: "buyAmount",
|
|
2309
|
+
});
|
|
2310
|
+
const resolvedValidTo = getCowSwapValidTo(validTo);
|
|
2311
|
+
const params = {
|
|
2312
|
+
chainId: account.chainId,
|
|
2313
|
+
sellToken: swapQuote.tokenIn.address,
|
|
2314
|
+
buyToken: swapQuote.receiver,
|
|
2315
|
+
sellAmount: orderAmounts.sellAmount,
|
|
2316
|
+
buyAmount: orderAmounts.buyAmount,
|
|
2317
|
+
feeAmount: orderAmounts.feeAmount,
|
|
2318
|
+
quoteId: orderAmounts.quoteId,
|
|
2319
|
+
slippageBips: Math.round((slippage ?? swapQuote.slippage) * 100),
|
|
2320
|
+
validTo: resolvedValidTo,
|
|
2321
|
+
collateralToken: collateralAsset,
|
|
2322
|
+
wrapper: {
|
|
2323
|
+
owner: account.owner,
|
|
2324
|
+
account: swapQuote.accountIn,
|
|
2325
|
+
deadline: resolvedValidTo,
|
|
2326
|
+
collateralVault,
|
|
2327
|
+
borrowVault: swapQuote.vaultIn,
|
|
2328
|
+
collateralAmount,
|
|
2329
|
+
borrowAmount: orderAmounts.sellAmount,
|
|
2330
|
+
},
|
|
2331
|
+
};
|
|
2332
|
+
return [
|
|
2333
|
+
{
|
|
2334
|
+
type: "cowSwap",
|
|
2335
|
+
kind: "openPosition",
|
|
2336
|
+
chainId: account.chainId,
|
|
2337
|
+
params,
|
|
2338
|
+
},
|
|
2339
|
+
];
|
|
2340
|
+
}
|
|
1988
2341
|
/**
|
|
1989
2342
|
* Builds a transaction plan for a multiply/leverage position when liability and long asset are the same (no swap).
|
|
1990
2343
|
* Flow: optional collateral deposit → enable controller → borrow to long vault → skim → enable collateral on long vault.
|