@eulerxyz/euler-v2-sdk 0.1.2-beta → 0.1.3-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 +1 -1
- package/dist/src/entities/Account.d.ts.map +1 -1
- package/dist/src/entities/Account.js +11 -4
- package/dist/src/entities/Account.js.map +1 -1
- package/dist/src/entities/Portfolio.d.ts +21 -1
- package/dist/src/entities/Portfolio.d.ts.map +1 -1
- package/dist/src/entities/Portfolio.js +48 -17
- package/dist/src/entities/Portfolio.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugins/keyring/keyringPlugin.d.ts +1 -1
- package/dist/src/plugins/keyring/keyringPlugin.d.ts.map +1 -1
- package/dist/src/plugins/keyring/keyringPlugin.js +12 -3
- package/dist/src/plugins/keyring/keyringPlugin.js.map +1 -1
- package/dist/src/sdk/buildSDK.d.ts +1 -3
- package/dist/src/sdk/buildSDK.d.ts.map +1 -1
- package/dist/src/sdk/buildSDK.js +10 -6
- package/dist/src/sdk/buildSDK.js.map +1 -1
- package/dist/src/sdk/defaultConfig.js +7 -7
- package/dist/src/sdk/defaultConfig.js.map +1 -1
- package/dist/src/sdk/sdk.d.ts +2 -5
- package/dist/src/sdk/sdk.d.ts.map +1 -1
- package/dist/src/sdk/sdk.js +0 -2
- package/dist/src/sdk/sdk.js.map +1 -1
- package/dist/src/services/accountService/adapters/accountV3Adapter/accountV3Adapter.d.ts +7 -1
- package/dist/src/services/accountService/adapters/accountV3Adapter/accountV3Adapter.d.ts.map +1 -1
- package/dist/src/services/accountService/adapters/accountV3Adapter/accountV3Adapter.js +100 -18
- package/dist/src/services/accountService/adapters/accountV3Adapter/accountV3Adapter.js.map +1 -1
- package/dist/src/services/executionService/encode.d.ts +30 -0
- package/dist/src/services/executionService/encode.d.ts.map +1 -0
- package/dist/src/services/executionService/encode.js +841 -0
- package/dist/src/services/executionService/encode.js.map +1 -0
- package/dist/src/services/executionService/execute.d.ts +64 -0
- package/dist/src/services/executionService/execute.d.ts.map +1 -0
- package/dist/src/services/executionService/execute.js +181 -0
- package/dist/src/services/executionService/execute.js.map +1 -0
- package/dist/src/services/executionService/executionService.d.ts +163 -37
- package/dist/src/services/executionService/executionService.d.ts.map +1 -1
- package/dist/src/services/executionService/executionService.js +493 -893
- package/dist/src/services/executionService/executionService.js.map +1 -1
- package/dist/src/services/executionService/executionServiceTypes.d.ts +93 -9
- package/dist/src/services/executionService/executionServiceTypes.d.ts.map +1 -1
- package/dist/src/services/executionService/executionServiceTypes.js +6 -0
- package/dist/src/services/executionService/executionServiceTypes.js.map +1 -1
- package/dist/src/services/executionService/index.d.ts +5 -2
- package/dist/src/services/executionService/index.d.ts.map +1 -1
- package/dist/src/services/executionService/index.js +3 -1
- package/dist/src/services/executionService/index.js.map +1 -1
- package/dist/src/services/{simulationService/simulationService.d.ts → executionService/simulate.d.ts} +26 -40
- package/dist/src/services/executionService/simulate.d.ts.map +1 -0
- package/dist/src/services/executionService/simulate.js +531 -0
- package/dist/src/services/executionService/simulate.js.map +1 -0
- package/dist/src/services/intrinsicApyService/adapters/intrinsicApyV3Adapter/intrinsicApyV3AdapterTypes.d.ts +1 -1
- package/dist/src/services/intrinsicApyService/adapters/intrinsicApyV3Adapter/intrinsicApyV3AdapterTypes.d.ts.map +1 -1
- package/dist/src/services/priceService/backendClient.d.ts +0 -2
- package/dist/src/services/priceService/backendClient.d.ts.map +1 -1
- package/dist/src/services/priceService/backendClient.js.map +1 -1
- package/dist/src/services/rewardsService/adapters/rewardsV3Adapter/rewardsV3Adapter.d.ts.map +1 -1
- package/dist/src/services/rewardsService/adapters/rewardsV3Adapter/rewardsV3Adapter.js +26 -13
- package/dist/src/services/rewardsService/adapters/rewardsV3Adapter/rewardsV3Adapter.js.map +1 -1
- package/dist/src/services/rewardsService/adapters/rewardsV3Adapter/rewardsV3AdapterTypes.d.ts +10 -10
- package/dist/src/services/rewardsService/adapters/rewardsV3Adapter/rewardsV3AdapterTypes.d.ts.map +1 -1
- package/dist/src/services/rewardsService/rewardsServiceTypes.d.ts +1 -1
- package/dist/src/services/rewardsService/rewardsServiceTypes.d.ts.map +1 -1
- package/dist/src/services/vaults/eVaultService/adapters/eVaultV3Adapter/eVaultV3Adapter.d.ts +1 -0
- package/dist/src/services/vaults/eVaultService/adapters/eVaultV3Adapter/eVaultV3Adapter.d.ts.map +1 -1
- package/dist/src/services/vaults/eVaultService/adapters/eVaultV3Adapter/eVaultV3Adapter.js +20 -3
- package/dist/src/services/vaults/eVaultService/adapters/eVaultV3Adapter/eVaultV3Adapter.js.map +1 -1
- package/dist/src/services/vaults/eVaultService/eVaultServiceConfig.d.ts +1 -1
- package/dist/src/services/vaults/eVaultService/eVaultServiceConfig.d.ts.map +1 -1
- package/dist/src/services/vaults/eulerEarnService/eulerEarnServiceConfig.d.ts +1 -1
- package/dist/src/services/vaults/eulerEarnService/eulerEarnServiceConfig.d.ts.map +1 -1
- package/dist/src/services/vaults/vaultMetaService/adapters/VaultTypeV3Adapter.d.ts +1 -1
- package/dist/src/services/vaults/vaultMetaService/adapters/VaultTypeV3Adapter.d.ts.map +1 -1
- package/dist/src/utils/accountComputations.d.ts +29 -0
- package/dist/src/utils/accountComputations.d.ts.map +1 -1
- package/dist/src/utils/accountComputations.js +84 -4
- package/dist/src/utils/accountComputations.js.map +1 -1
- package/dist/src/utils/accountPositionClassification.d.ts +15 -0
- package/dist/src/utils/accountPositionClassification.d.ts.map +1 -0
- package/dist/src/utils/accountPositionClassification.js +27 -0
- package/dist/src/utils/accountPositionClassification.js.map +1 -0
- package/package.json +2 -2
- package/dist/src/services/simulationService/index.d.ts +0 -3
- package/dist/src/services/simulationService/index.d.ts.map +0 -1
- package/dist/src/services/simulationService/index.js +0 -2
- package/dist/src/services/simulationService/index.js.map +0 -1
- package/dist/src/services/simulationService/simulationService.d.ts.map +0 -1
- package/dist/src/services/simulationService/simulationService.js +0 -533
- package/dist/src/services/simulationService/simulationService.js.map +0 -1
|
@@ -1,34 +1,120 @@
|
|
|
1
|
-
import { encodeFunctionData, getAddress, maxUint256,
|
|
1
|
+
import { decodeFunctionData, encodeFunctionData, erc20Abi, getAddress, maxUint256, maxUint160, } from "viem";
|
|
2
|
+
import { resolveBorrowCollateralPositions } from "../../utils/accountPositionClassification.js";
|
|
2
3
|
import { ethereumVaultConnectorAbi } from "./abis/ethereumVaultConnectorAbi.js";
|
|
3
4
|
import { eVaultAbi } from "./abis/eVaultAbi.js";
|
|
4
5
|
import { permit2PermitAbi } from "./abis/permit2PermitAbi.js";
|
|
5
6
|
import { swapperAbi } from "./abis/swapperAbi.js";
|
|
6
7
|
import { swapVerifierAbi } from "./abis/swapVerifierAbi.js";
|
|
7
|
-
import
|
|
8
|
+
import * as encodeHelpers from "./encode.js";
|
|
9
|
+
import { isEVCBatchOperation } from "./executionServiceTypes.js";
|
|
10
|
+
import { executeTransactionPlan, } from "./execute.js";
|
|
11
|
+
import { deriveStateOverrides, estimateGasForTransactionPlan, simulateTransactionPlan, } from "./simulate.js";
|
|
8
12
|
const TOKENS_REQUIRING_ZERO_APPROVAL_RESET = {
|
|
9
13
|
1: [getAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7")],
|
|
10
14
|
};
|
|
11
15
|
function requiresZeroApprovalReset(chainId, token) {
|
|
12
16
|
return (TOKENS_REQUIRING_ZERO_APPROVAL_RESET[chainId]?.some((resetToken) => resetToken === getAddress(token)) ?? false);
|
|
13
17
|
}
|
|
14
|
-
|
|
18
|
+
function isSavingsCollateral(collateral) {
|
|
19
|
+
return collateral?.source === "savings";
|
|
20
|
+
}
|
|
21
|
+
function isWalletCollateral(collateral) {
|
|
22
|
+
return !!collateral && !isSavingsCollateral(collateral);
|
|
23
|
+
}
|
|
24
|
+
function cloneBatchEntries(entries) {
|
|
25
|
+
return entries.map((entry) => "type" in entry && entry.type === "operation"
|
|
26
|
+
? { type: "operation", name: entry.name, items: [...entry.items] }
|
|
27
|
+
: entry);
|
|
28
|
+
}
|
|
15
29
|
const WAD = 10n ** 18n;
|
|
16
30
|
// TODO explain how this service is coupled to the concrete abis of ERC4626, permit2 and EVK.
|
|
17
31
|
// this is a helper service, not a generic one.
|
|
18
32
|
export class ExecutionService {
|
|
19
33
|
deploymentService;
|
|
20
|
-
walletService;
|
|
21
34
|
plugins = [];
|
|
22
|
-
|
|
35
|
+
walletService;
|
|
36
|
+
providerService;
|
|
37
|
+
vaultMetaService;
|
|
38
|
+
priceService;
|
|
39
|
+
rewardsService;
|
|
40
|
+
intrinsicApyService;
|
|
41
|
+
eulerLabelsService;
|
|
42
|
+
constructor(deploymentService, walletService, providerService, vaultMetaService, priceService, rewardsService, intrinsicApyService, eulerLabelsService) {
|
|
23
43
|
this.deploymentService = deploymentService;
|
|
24
44
|
this.walletService = walletService;
|
|
45
|
+
this.providerService = providerService;
|
|
46
|
+
this.vaultMetaService = vaultMetaService;
|
|
47
|
+
this.priceService = priceService;
|
|
48
|
+
this.rewardsService = rewardsService;
|
|
49
|
+
this.intrinsicApyService = intrinsicApyService;
|
|
50
|
+
this.eulerLabelsService = eulerLabelsService;
|
|
25
51
|
}
|
|
26
52
|
setWalletService(walletService) {
|
|
27
53
|
this.walletService = walletService;
|
|
28
54
|
}
|
|
55
|
+
setProviderService(providerService) {
|
|
56
|
+
this.providerService = providerService;
|
|
57
|
+
}
|
|
58
|
+
setVaultMetaService(vaultMetaService) {
|
|
59
|
+
this.vaultMetaService = vaultMetaService;
|
|
60
|
+
}
|
|
61
|
+
setPriceService(priceService) {
|
|
62
|
+
this.priceService = priceService;
|
|
63
|
+
}
|
|
64
|
+
setRewardsService(rewardsService) {
|
|
65
|
+
this.rewardsService = rewardsService;
|
|
66
|
+
}
|
|
67
|
+
setIntrinsicApyService(intrinsicApyService) {
|
|
68
|
+
this.intrinsicApyService = intrinsicApyService;
|
|
69
|
+
}
|
|
70
|
+
setEulerLabelsService(eulerLabelsService) {
|
|
71
|
+
this.eulerLabelsService = eulerLabelsService;
|
|
72
|
+
}
|
|
29
73
|
setPlugins(plugins) {
|
|
30
74
|
this.plugins = plugins;
|
|
31
75
|
}
|
|
76
|
+
/** Derive storage overrides needed to simulate the plan against the current account state. */
|
|
77
|
+
async deriveStateOverrides(chainId, account, transactionPlan, options) {
|
|
78
|
+
return deriveStateOverrides(this.getSimulationContext(), chainId, account, transactionPlan, options);
|
|
79
|
+
}
|
|
80
|
+
/** Simulate the full transaction plan, including approval resolution and plugin-aware batch execution. */
|
|
81
|
+
async simulateTransactionPlan(chainId, account, transactionPlan, options) {
|
|
82
|
+
return simulateTransactionPlan(this.getSimulationContext(), chainId, account, transactionPlan, options);
|
|
83
|
+
}
|
|
84
|
+
/** Estimate gas for the full transaction plan after applying the same simulation pipeline used for execution. */
|
|
85
|
+
async estimateGasForTransactionPlan(chainId, account, transactionPlan, options) {
|
|
86
|
+
return estimateGasForTransactionPlan(this.getSimulationContext(), chainId, account, transactionPlan, options);
|
|
87
|
+
}
|
|
88
|
+
/** Execute a transaction plan using caller-provided signing and send callbacks. */
|
|
89
|
+
async executeTransactionPlan(args) {
|
|
90
|
+
const { providerService } = this;
|
|
91
|
+
if (!providerService) {
|
|
92
|
+
throw new Error("ExecutionService.executeTransactionPlan requires a providerService. Pass it to the ExecutionService constructor or call setProviderService().");
|
|
93
|
+
}
|
|
94
|
+
const helperArgs = {
|
|
95
|
+
...args,
|
|
96
|
+
executionService: this,
|
|
97
|
+
deploymentService: this.deploymentService,
|
|
98
|
+
providerService,
|
|
99
|
+
};
|
|
100
|
+
return executeTransactionPlan(helperArgs);
|
|
101
|
+
}
|
|
102
|
+
getSimulationContext() {
|
|
103
|
+
return {
|
|
104
|
+
deploymentService: this.deploymentService,
|
|
105
|
+
walletService: this.walletService,
|
|
106
|
+
providerService: this.providerService,
|
|
107
|
+
vaultMetaService: this.vaultMetaService,
|
|
108
|
+
priceService: this.priceService,
|
|
109
|
+
rewardsService: this.rewardsService,
|
|
110
|
+
intrinsicApyService: this.intrinsicApyService,
|
|
111
|
+
eulerLabelsService: this.eulerLabelsService,
|
|
112
|
+
describeBatch: (batch) => this.describeBatch(batch),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
getCoreAddresses(chainId) {
|
|
116
|
+
return this.deploymentService.getDeployment(chainId).addresses.coreAddrs;
|
|
117
|
+
}
|
|
32
118
|
/**
|
|
33
119
|
* Encodes an array of EVC batch items into a single calldata hex for `EVC.batch()`.
|
|
34
120
|
*
|
|
@@ -36,11 +122,7 @@ export class ExecutionService {
|
|
|
36
122
|
* @returns Encoded calldata hex for the EVC batch call
|
|
37
123
|
*/
|
|
38
124
|
encodeBatch(items) {
|
|
39
|
-
return
|
|
40
|
-
abi: ethereumVaultConnectorAbi,
|
|
41
|
-
functionName: "batch",
|
|
42
|
-
args: [items],
|
|
43
|
-
});
|
|
125
|
+
return encodeHelpers.encodeBatch(items);
|
|
44
126
|
}
|
|
45
127
|
/**
|
|
46
128
|
* Encodes EVC batch items for depositing underlying assets into a vault (mints shares to receiver).
|
|
@@ -55,33 +137,9 @@ export class ExecutionService {
|
|
|
55
137
|
* @param args.permit2 - Optional Permit2 message + signature; if set, prepends a permit2 permit call so transferFrom can be used
|
|
56
138
|
* @returns Array of EVC batch items (optional permit2, optional enableCollateral, deposit)
|
|
57
139
|
*/
|
|
58
|
-
encodeDeposit({ chainId,
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
const permit2Call = this.encodePermit2Call({
|
|
62
|
-
chainId,
|
|
63
|
-
owner,
|
|
64
|
-
message: permit2.message,
|
|
65
|
-
signature: permit2.signature,
|
|
66
|
-
});
|
|
67
|
-
items.push(permit2Call);
|
|
68
|
-
}
|
|
69
|
-
// Add enable collateral if flag is set
|
|
70
|
-
if (enableCollateral) {
|
|
71
|
-
items.push(this.encodeEnableCollateral(chainId, receiver, vault));
|
|
72
|
-
}
|
|
73
|
-
// Add deposit operation
|
|
74
|
-
items.push({
|
|
75
|
-
targetContract: vault,
|
|
76
|
-
onBehalfOfAccount: owner,
|
|
77
|
-
value: 0n,
|
|
78
|
-
data: encodeFunctionData({
|
|
79
|
-
abi: eVaultAbi,
|
|
80
|
-
functionName: "deposit",
|
|
81
|
-
args: [amount, receiver],
|
|
82
|
-
}),
|
|
83
|
-
});
|
|
84
|
-
return items;
|
|
140
|
+
encodeDeposit({ chainId, ...args }) {
|
|
141
|
+
const { evc, permit2 } = this.getCoreAddresses(chainId);
|
|
142
|
+
return encodeHelpers.encodeDeposit(evc, permit2, { chainId, ...args });
|
|
85
143
|
}
|
|
86
144
|
/**
|
|
87
145
|
* Encodes EVC batch items for minting vault shares by depositing underlying assets.
|
|
@@ -96,33 +154,9 @@ export class ExecutionService {
|
|
|
96
154
|
* @param args.permit2 - Optional Permit2 message + signature for transferFrom
|
|
97
155
|
* @returns Array of EVC batch items (optional permit2, optional enableCollateral, mint)
|
|
98
156
|
*/
|
|
99
|
-
encodeMint({ chainId,
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
const permit2Call = this.encodePermit2Call({
|
|
103
|
-
chainId,
|
|
104
|
-
owner,
|
|
105
|
-
message: permit2.message,
|
|
106
|
-
signature: permit2.signature,
|
|
107
|
-
});
|
|
108
|
-
items.push(permit2Call);
|
|
109
|
-
}
|
|
110
|
-
// Add enable collateral if flag is set
|
|
111
|
-
if (enableCollateral) {
|
|
112
|
-
items.push(this.encodeEnableCollateral(chainId, receiver, vault));
|
|
113
|
-
}
|
|
114
|
-
// Add mint operation
|
|
115
|
-
items.push({
|
|
116
|
-
targetContract: vault,
|
|
117
|
-
onBehalfOfAccount: owner,
|
|
118
|
-
value: 0n,
|
|
119
|
-
data: encodeFunctionData({
|
|
120
|
-
abi: eVaultAbi,
|
|
121
|
-
functionName: "mint",
|
|
122
|
-
args: [shares, receiver],
|
|
123
|
-
}),
|
|
124
|
-
});
|
|
125
|
-
return items;
|
|
157
|
+
encodeMint({ chainId, ...args }) {
|
|
158
|
+
const { evc, permit2 } = this.getCoreAddresses(chainId);
|
|
159
|
+
return encodeHelpers.encodeMint(evc, permit2, { chainId, ...args });
|
|
126
160
|
}
|
|
127
161
|
/**
|
|
128
162
|
* Encodes EVC batch items for withdrawing underlying assets from a vault (burns shares).
|
|
@@ -136,24 +170,9 @@ export class ExecutionService {
|
|
|
136
170
|
* @param args.disableCollateral - If true, appends disableCollateral( owner, vault ) via EVC before withdraw
|
|
137
171
|
* @returns Array of EVC batch items (optional disableCollateral, withdraw)
|
|
138
172
|
*/
|
|
139
|
-
encodeWithdraw({ chainId,
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
if (disableCollateral) {
|
|
143
|
-
items.push(this.encodeDisableCollateral(chainId, owner, vault));
|
|
144
|
-
}
|
|
145
|
-
// Add withdraw operation
|
|
146
|
-
items.push({
|
|
147
|
-
targetContract: vault,
|
|
148
|
-
onBehalfOfAccount: owner,
|
|
149
|
-
value: 0n,
|
|
150
|
-
data: encodeFunctionData({
|
|
151
|
-
abi: eVaultAbi,
|
|
152
|
-
functionName: "withdraw",
|
|
153
|
-
args: [assets, receiver, owner],
|
|
154
|
-
}),
|
|
155
|
-
});
|
|
156
|
-
return items;
|
|
173
|
+
encodeWithdraw({ chainId, ...args }) {
|
|
174
|
+
const { evc } = this.getCoreAddresses(chainId);
|
|
175
|
+
return encodeHelpers.encodeWithdraw(evc, { chainId, ...args });
|
|
157
176
|
}
|
|
158
177
|
/**
|
|
159
178
|
* Encodes EVC batch items for redeeming vault shares for underlying assets.
|
|
@@ -167,24 +186,9 @@ export class ExecutionService {
|
|
|
167
186
|
* @param args.disableCollateral - If true, prepends disableCollateral( owner, vault ) via EVC
|
|
168
187
|
* @returns Array of EVC batch items (optional disableCollateral, redeem)
|
|
169
188
|
*/
|
|
170
|
-
encodeRedeem({ chainId,
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
if (disableCollateral) {
|
|
174
|
-
items.push(this.encodeDisableCollateral(chainId, owner, vault));
|
|
175
|
-
}
|
|
176
|
-
// Add redeem operation
|
|
177
|
-
items.push({
|
|
178
|
-
targetContract: vault,
|
|
179
|
-
onBehalfOfAccount: owner,
|
|
180
|
-
value: 0n,
|
|
181
|
-
data: encodeFunctionData({
|
|
182
|
-
abi: eVaultAbi,
|
|
183
|
-
functionName: "redeem",
|
|
184
|
-
args: [shares, receiver, owner],
|
|
185
|
-
}),
|
|
186
|
-
});
|
|
187
|
-
return items;
|
|
189
|
+
encodeRedeem({ chainId, ...args }) {
|
|
190
|
+
const { evc } = this.getCoreAddresses(chainId);
|
|
191
|
+
return encodeHelpers.encodeRedeem(evc, { chainId, ...args });
|
|
188
192
|
}
|
|
189
193
|
/**
|
|
190
194
|
* Encodes EVC batch items for borrowing from a liability vault, optionally depositing collateral in the same batch.
|
|
@@ -205,42 +209,8 @@ export class ExecutionService {
|
|
|
205
209
|
* @returns Array of EVC batch items (optional deposit, optional disableController, optional enableController, borrow)
|
|
206
210
|
*/
|
|
207
211
|
encodeBorrow(args) {
|
|
208
|
-
const {
|
|
209
|
-
|
|
210
|
-
// Add collateral if provided
|
|
211
|
-
if (collateralVault &&
|
|
212
|
-
collateralAmount !== undefined &&
|
|
213
|
-
collateralAmount > 0n) {
|
|
214
|
-
const depositItems = this.encodeDeposit({
|
|
215
|
-
chainId,
|
|
216
|
-
vault: collateralVault,
|
|
217
|
-
amount: collateralAmount,
|
|
218
|
-
receiver: borrowAccount,
|
|
219
|
-
enableCollateral,
|
|
220
|
-
permit2: collateralPermit2,
|
|
221
|
-
owner: owner,
|
|
222
|
-
});
|
|
223
|
-
items.push(...depositItems);
|
|
224
|
-
}
|
|
225
|
-
// Add disable controller if there's a different controller enabled
|
|
226
|
-
if (currentController && currentController !== vault) {
|
|
227
|
-
items.push(this.encodeDisableController(currentController, borrowAccount));
|
|
228
|
-
}
|
|
229
|
-
if (enableController) {
|
|
230
|
-
items.push(this.encodeEnableController(chainId, borrowAccount, vault));
|
|
231
|
-
}
|
|
232
|
-
// Add borrow operation
|
|
233
|
-
items.push({
|
|
234
|
-
targetContract: vault,
|
|
235
|
-
onBehalfOfAccount: borrowAccount,
|
|
236
|
-
value: 0n,
|
|
237
|
-
data: encodeFunctionData({
|
|
238
|
-
abi: eVaultAbi,
|
|
239
|
-
functionName: "borrow",
|
|
240
|
-
args: [amount, receiver],
|
|
241
|
-
}),
|
|
242
|
-
});
|
|
243
|
-
return items;
|
|
212
|
+
const { evc, permit2 } = this.getCoreAddresses(args.chainId);
|
|
213
|
+
return encodeHelpers.encodeBorrow(evc, permit2, args);
|
|
244
214
|
}
|
|
245
215
|
/**
|
|
246
216
|
* Encodes EVC batch items for liquidating an undercollateralized account (repay debt, seize collateral).
|
|
@@ -257,28 +227,9 @@ export class ExecutionService {
|
|
|
257
227
|
* @param args.enableCollateral - If true, enables collateral vault for liquidator sub-account after liquidate (default true)
|
|
258
228
|
* @returns Array of EVC batch items (optional enableController, liquidate, optional enableCollateral)
|
|
259
229
|
*/
|
|
260
|
-
encodeLiquidation({ chainId,
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
if (enableController) {
|
|
264
|
-
items.push(this.encodeEnableController(chainId, liquidatorSubAccountAddress, vault));
|
|
265
|
-
}
|
|
266
|
-
// Perform the liquidation
|
|
267
|
-
items.push({
|
|
268
|
-
targetContract: vault,
|
|
269
|
-
onBehalfOfAccount: liquidatorSubAccountAddress,
|
|
270
|
-
value: 0n,
|
|
271
|
-
data: encodeFunctionData({
|
|
272
|
-
abi: eVaultAbi,
|
|
273
|
-
functionName: "liquidate",
|
|
274
|
-
args: [violator, collateral, repayAssets, minYieldBalance],
|
|
275
|
-
}),
|
|
276
|
-
});
|
|
277
|
-
// Optionally enable collateral for the seized collateral vault on the liquidator account
|
|
278
|
-
if (enableCollateral) {
|
|
279
|
-
items.push(this.encodeEnableCollateral(chainId, liquidatorSubAccountAddress, collateral));
|
|
280
|
-
}
|
|
281
|
-
return items;
|
|
230
|
+
encodeLiquidation({ chainId, ...args }) {
|
|
231
|
+
const { evc } = this.getCoreAddresses(chainId);
|
|
232
|
+
return encodeHelpers.encodeLiquidation(evc, { chainId, ...args });
|
|
282
233
|
}
|
|
283
234
|
/**
|
|
284
235
|
* Encodes EVC batch items for pulling debt from one sub-account to another on the same liability vault.
|
|
@@ -292,24 +243,9 @@ export class ExecutionService {
|
|
|
292
243
|
* @param args.enableController - If true, enables vault as controller for `to` via EVC before pullDebt (default true)
|
|
293
244
|
* @returns Array of EVC batch items (optional enableController, pullDebt)
|
|
294
245
|
*/
|
|
295
|
-
encodePullDebt({ chainId,
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
if (enableController) {
|
|
299
|
-
items.push(this.encodeEnableController(chainId, to, vault));
|
|
300
|
-
}
|
|
301
|
-
// Add pullDebt operation
|
|
302
|
-
items.push({
|
|
303
|
-
targetContract: vault,
|
|
304
|
-
onBehalfOfAccount: to,
|
|
305
|
-
value: 0n,
|
|
306
|
-
data: encodeFunctionData({
|
|
307
|
-
abi: eVaultAbi,
|
|
308
|
-
functionName: "pullDebt",
|
|
309
|
-
args: [amount, from],
|
|
310
|
-
}),
|
|
311
|
-
});
|
|
312
|
-
return items;
|
|
246
|
+
encodePullDebt({ chainId, ...args }) {
|
|
247
|
+
const { evc } = this.getCoreAddresses(chainId);
|
|
248
|
+
return encodeHelpers.encodePullDebt(evc, { chainId, ...args });
|
|
313
249
|
}
|
|
314
250
|
/**
|
|
315
251
|
* Encodes EVC batch items for a multiply/leverage operation when liability and long asset differ (swap required).
|
|
@@ -333,77 +269,8 @@ export class ExecutionService {
|
|
|
333
269
|
* @returns Array of EVC batch items
|
|
334
270
|
*/
|
|
335
271
|
encodeMultiplyWithSwap(args) {
|
|
336
|
-
const {
|
|
337
|
-
|
|
338
|
-
// 1. Add permit2 for collateral if provided
|
|
339
|
-
if (collateralPermit2) {
|
|
340
|
-
const permit2Call = this.encodePermit2Call({
|
|
341
|
-
chainId,
|
|
342
|
-
owner,
|
|
343
|
-
message: collateralPermit2.message,
|
|
344
|
-
signature: collateralPermit2.signature,
|
|
345
|
-
});
|
|
346
|
-
items.push(permit2Call);
|
|
347
|
-
}
|
|
348
|
-
// 2. Deposit initial collateral if amount > 0
|
|
349
|
-
if (collateralAmount > 0n) {
|
|
350
|
-
// Enable collateral for collateral vault
|
|
351
|
-
if (enableCollateral) {
|
|
352
|
-
items.push(this.encodeEnableCollateral(chainId, receiver, collateralVault));
|
|
353
|
-
}
|
|
354
|
-
// Deposit collateral
|
|
355
|
-
items.push({
|
|
356
|
-
targetContract: collateralVault,
|
|
357
|
-
onBehalfOfAccount: owner,
|
|
358
|
-
value: 0n,
|
|
359
|
-
data: encodeFunctionData({
|
|
360
|
-
abi: eVaultAbi,
|
|
361
|
-
functionName: "deposit",
|
|
362
|
-
args: [collateralAmount, receiver],
|
|
363
|
-
}),
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
// 3. Disable current controller if there's a different one enabled
|
|
367
|
-
if (currentController && currentController !== liabilityVault) {
|
|
368
|
-
items.push(this.encodeDisableController(currentController, receiver));
|
|
369
|
-
}
|
|
370
|
-
// 4. Enable controller for liability vault
|
|
371
|
-
if (enableController) {
|
|
372
|
-
items.push(this.encodeEnableController(chainId, receiver, liabilityVault));
|
|
373
|
-
}
|
|
374
|
-
// 5. Borrow from liability vault to swapper
|
|
375
|
-
items.push({
|
|
376
|
-
targetContract: liabilityVault,
|
|
377
|
-
onBehalfOfAccount: receiver,
|
|
378
|
-
value: 0n,
|
|
379
|
-
data: encodeFunctionData({
|
|
380
|
-
abi: eVaultAbi,
|
|
381
|
-
functionName: "borrow",
|
|
382
|
-
args: [liabilityAmount, swapQuote.swap.swapperAddress],
|
|
383
|
-
}),
|
|
384
|
-
});
|
|
385
|
-
// 6. Execute swap multicall
|
|
386
|
-
items.push({
|
|
387
|
-
targetContract: swapQuote.swap.swapperAddress,
|
|
388
|
-
onBehalfOfAccount: receiver,
|
|
389
|
-
value: 0n,
|
|
390
|
-
data: swapQuote.swap.swapperData,
|
|
391
|
-
});
|
|
392
|
-
// 7. Verify swap and skim to long vault
|
|
393
|
-
if (swapQuote.verify.type !== "skimMin") {
|
|
394
|
-
throw new Error("Invalid swap quote type for multiply - must be skimMin");
|
|
395
|
-
}
|
|
396
|
-
items.push({
|
|
397
|
-
targetContract: swapQuote.verify.verifierAddress,
|
|
398
|
-
onBehalfOfAccount: receiver,
|
|
399
|
-
value: 0n,
|
|
400
|
-
data: swapQuote.verify.verifierData,
|
|
401
|
-
});
|
|
402
|
-
// 8. Enable collateral on long vault
|
|
403
|
-
if (enableCollateralLong && collateralVault !== longVault) {
|
|
404
|
-
items.push(this.encodeEnableCollateral(chainId, receiver, longVault));
|
|
405
|
-
}
|
|
406
|
-
return items;
|
|
272
|
+
const { evc, permit2 } = this.getCoreAddresses(args.chainId);
|
|
273
|
+
return encodeHelpers.encodeMultiplyWithSwap(evc, permit2, args);
|
|
407
274
|
}
|
|
408
275
|
/**
|
|
409
276
|
* Encodes EVC batch items for a multiply/leverage operation when liability and long asset are the same (no swap).
|
|
@@ -426,71 +293,8 @@ export class ExecutionService {
|
|
|
426
293
|
* @returns Array of EVC batch items
|
|
427
294
|
*/
|
|
428
295
|
encodeMultiplySameAsset(args) {
|
|
429
|
-
const {
|
|
430
|
-
|
|
431
|
-
// 1. Add permit2 for collateral if provided
|
|
432
|
-
if (collateralPermit2) {
|
|
433
|
-
const permit2Call = this.encodePermit2Call({
|
|
434
|
-
chainId,
|
|
435
|
-
owner,
|
|
436
|
-
message: collateralPermit2.message,
|
|
437
|
-
signature: collateralPermit2.signature,
|
|
438
|
-
});
|
|
439
|
-
items.push(permit2Call);
|
|
440
|
-
}
|
|
441
|
-
// 2. Deposit initial collateral if amount > 0
|
|
442
|
-
if (collateralAmount > 0n) {
|
|
443
|
-
// Enable collateral for collateral vault
|
|
444
|
-
if (enableCollateral) {
|
|
445
|
-
items.push(this.encodeEnableCollateral(chainId, receiver, collateralVault));
|
|
446
|
-
}
|
|
447
|
-
// Deposit collateral
|
|
448
|
-
items.push({
|
|
449
|
-
targetContract: collateralVault,
|
|
450
|
-
onBehalfOfAccount: owner,
|
|
451
|
-
value: 0n,
|
|
452
|
-
data: encodeFunctionData({
|
|
453
|
-
abi: eVaultAbi,
|
|
454
|
-
functionName: "deposit",
|
|
455
|
-
args: [collateralAmount, receiver],
|
|
456
|
-
}),
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
// 3. Disable current controller if there's a different one enabled
|
|
460
|
-
if (currentController && currentController !== liabilityVault) {
|
|
461
|
-
items.push(this.encodeDisableController(currentController, receiver));
|
|
462
|
-
}
|
|
463
|
-
// 4. Enable controller for liability vault
|
|
464
|
-
if (enableController) {
|
|
465
|
-
items.push(this.encodeEnableController(chainId, receiver, liabilityVault));
|
|
466
|
-
}
|
|
467
|
-
// 5. Borrow from liability vault directly to long vault
|
|
468
|
-
items.push({
|
|
469
|
-
targetContract: liabilityVault,
|
|
470
|
-
onBehalfOfAccount: receiver,
|
|
471
|
-
value: 0n,
|
|
472
|
-
data: encodeFunctionData({
|
|
473
|
-
abi: eVaultAbi,
|
|
474
|
-
functionName: "borrow",
|
|
475
|
-
args: [liabilityAmount, longVault],
|
|
476
|
-
}),
|
|
477
|
-
});
|
|
478
|
-
// 6. Skim borrowed assets to position
|
|
479
|
-
items.push({
|
|
480
|
-
targetContract: longVault,
|
|
481
|
-
onBehalfOfAccount: receiver,
|
|
482
|
-
value: 0n,
|
|
483
|
-
data: encodeFunctionData({
|
|
484
|
-
abi: eVaultAbi,
|
|
485
|
-
functionName: "skim",
|
|
486
|
-
args: [liabilityAmount, receiver],
|
|
487
|
-
}),
|
|
488
|
-
});
|
|
489
|
-
// 7. Enable collateral on long vault
|
|
490
|
-
if (enableCollateralLong) {
|
|
491
|
-
items.push(this.encodeEnableCollateral(chainId, receiver, longVault));
|
|
492
|
-
}
|
|
493
|
-
return items;
|
|
296
|
+
const { evc, permit2 } = this.getCoreAddresses(args.chainId);
|
|
297
|
+
return encodeHelpers.encodeMultiplySameAsset(evc, permit2, args);
|
|
494
298
|
}
|
|
495
299
|
/**
|
|
496
300
|
* Encodes EVC batch items for repaying debt using assets from the sender's wallet (transferFrom to vault then repay).
|
|
@@ -507,41 +311,16 @@ export class ExecutionService {
|
|
|
507
311
|
* @returns Array of EVC batch items (optional permit2, repay, optional disableController)
|
|
508
312
|
*/
|
|
509
313
|
encodeRepayFromWallet(args) {
|
|
510
|
-
const {
|
|
511
|
-
|
|
512
|
-
if (permit2) {
|
|
513
|
-
const permit2Call = this.encodePermit2Call({
|
|
514
|
-
chainId,
|
|
515
|
-
owner: sender,
|
|
516
|
-
message: permit2.message,
|
|
517
|
-
signature: permit2.signature,
|
|
518
|
-
});
|
|
519
|
-
items.push(permit2Call);
|
|
520
|
-
}
|
|
521
|
-
// Repay operation
|
|
522
|
-
items.push({
|
|
523
|
-
targetContract: liabilityVault,
|
|
524
|
-
onBehalfOfAccount: sender,
|
|
525
|
-
value: 0n,
|
|
526
|
-
data: encodeFunctionData({
|
|
527
|
-
abi: eVaultAbi,
|
|
528
|
-
functionName: "repay",
|
|
529
|
-
args: [isMax ? maxUint256 : liabilityAmount, receiver],
|
|
530
|
-
}),
|
|
531
|
-
});
|
|
532
|
-
// Disable controller if needed (for max repay)
|
|
533
|
-
// Sender must be allowed to act on behalf of receiver (sender is subaccount of receiver or is an operator)
|
|
534
|
-
if (disableControllerOnMax && isMax) {
|
|
535
|
-
items.push(this.encodeDisableController(liabilityVault, receiver));
|
|
536
|
-
}
|
|
537
|
-
return items;
|
|
314
|
+
const { permit2 } = this.getCoreAddresses(args.chainId);
|
|
315
|
+
return encodeHelpers.encodeRepayFromWallet(permit2, args);
|
|
538
316
|
}
|
|
539
317
|
/**
|
|
540
318
|
* Encodes EVC batch items for repaying debt from a deposit (same-asset only).
|
|
541
|
-
* Path 1: same asset and same vault → repayWithShares.
|
|
319
|
+
* Path 1: same asset and same vault → repayWithShares.
|
|
320
|
+
* Path 2: same asset, different vault → withdraw to liability vault, skim, then repayWithShares.
|
|
542
321
|
*
|
|
543
322
|
* @param args - Repay-from-deposit encoding arguments
|
|
544
|
-
* @param args.chainId - Chain ID
|
|
323
|
+
* @param args.chainId - Chain ID used for EVC controller calls
|
|
545
324
|
* @param args.liabilityVault - Vault (liability) to repay debt to
|
|
546
325
|
* @param args.liabilityAsset - Underlying asset address of the liability vault
|
|
547
326
|
* @param args.liabilityAmount - Amount of liability to repay (maxUint256 with isMax for full repay)
|
|
@@ -554,31 +333,7 @@ export class ExecutionService {
|
|
|
554
333
|
* @returns Array of EVC batch items. Throws if fromAsset !== liabilityAsset.
|
|
555
334
|
*/
|
|
556
335
|
encodeRepayFromDeposit(args) {
|
|
557
|
-
|
|
558
|
-
// PATH 1: Same asset, same vault - use repayWithShares
|
|
559
|
-
if (fromAsset === liabilityAsset && fromVault === liabilityVault) {
|
|
560
|
-
return this.encodeRepayWithSharesSameAssetAndVault({
|
|
561
|
-
chainId,
|
|
562
|
-
vault: liabilityVault,
|
|
563
|
-
amount: liabilityAmount,
|
|
564
|
-
receiver,
|
|
565
|
-
from,
|
|
566
|
-
disableController: isMax && disableControllerOnMax,
|
|
567
|
-
});
|
|
568
|
-
}
|
|
569
|
-
// PATH 2: Same asset, different vault
|
|
570
|
-
if (fromAsset === liabilityAsset) {
|
|
571
|
-
return this.encodeRepayWithSharesSameAssetDifferentVault({
|
|
572
|
-
fromVault,
|
|
573
|
-
toVault: liabilityVault,
|
|
574
|
-
amount: liabilityAmount,
|
|
575
|
-
receiver,
|
|
576
|
-
from,
|
|
577
|
-
isMax,
|
|
578
|
-
disableControllerOnMax,
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
throw new Error("encodeRepayFromDeposit only supports same-asset paths");
|
|
336
|
+
return encodeHelpers.encodeRepayFromDeposit(args);
|
|
582
337
|
}
|
|
583
338
|
/**
|
|
584
339
|
* Encodes EVC batch items for repaying debt by swapping collateral (withdraw from vaultIn → swap → verify/repay debtMax).
|
|
@@ -593,50 +348,7 @@ export class ExecutionService {
|
|
|
593
348
|
* @returns Array of EVC batch items (withdraw, swap, verify/repay, optional disableController)
|
|
594
349
|
*/
|
|
595
350
|
encodeRepayWithSwap(args) {
|
|
596
|
-
|
|
597
|
-
const items = [];
|
|
598
|
-
// Determine withdraw amount (cap to available amount if provided)
|
|
599
|
-
const withdrawAmount = maxWithdraw &&
|
|
600
|
-
maxWithdraw < BigInt(swapQuote.amountInMax || swapQuote.amountIn)
|
|
601
|
-
? maxWithdraw
|
|
602
|
-
: BigInt(swapQuote.amountInMax || swapQuote.amountIn);
|
|
603
|
-
// 1. Withdraw collateral from vault to swapper
|
|
604
|
-
items.push({
|
|
605
|
-
targetContract: swapQuote.vaultIn,
|
|
606
|
-
onBehalfOfAccount: swapQuote.accountIn,
|
|
607
|
-
value: 0n,
|
|
608
|
-
data: encodeFunctionData({
|
|
609
|
-
abi: eVaultAbi,
|
|
610
|
-
functionName: "withdraw",
|
|
611
|
-
args: [
|
|
612
|
-
withdrawAmount,
|
|
613
|
-
swapQuote.swap.swapperAddress,
|
|
614
|
-
swapQuote.accountIn,
|
|
615
|
-
],
|
|
616
|
-
}),
|
|
617
|
-
});
|
|
618
|
-
// 2. Execute swap multicall
|
|
619
|
-
items.push({
|
|
620
|
-
targetContract: swapQuote.swap.swapperAddress,
|
|
621
|
-
onBehalfOfAccount: swapQuote.accountIn,
|
|
622
|
-
value: 0n,
|
|
623
|
-
data: swapQuote.swap.swapperData,
|
|
624
|
-
});
|
|
625
|
-
// 3. Verify swap and repay (verifyDebtMax handles the repay)
|
|
626
|
-
if (swapQuote.verify.type !== "debtMax") {
|
|
627
|
-
throw new Error("Invalid swap quote type for repay - must be debtMax");
|
|
628
|
-
}
|
|
629
|
-
items.push({
|
|
630
|
-
targetContract: swapQuote.verify.verifierAddress,
|
|
631
|
-
onBehalfOfAccount: swapQuote.verify.account,
|
|
632
|
-
value: 0n,
|
|
633
|
-
data: swapQuote.verify.verifierData,
|
|
634
|
-
});
|
|
635
|
-
// 4. Disable controller if needed (for max repay)
|
|
636
|
-
if (isMax && disableControllerOnMax) {
|
|
637
|
-
items.push(this.encodeDisableController(swapQuote.receiver, swapQuote.accountOut));
|
|
638
|
-
}
|
|
639
|
-
return items;
|
|
351
|
+
return encodeHelpers.encodeRepayWithSwap(args);
|
|
640
352
|
}
|
|
641
353
|
/**
|
|
642
354
|
* Encodes EVC batch items for depositing into a vault using tokens from the user's wallet, going through a swap.
|
|
@@ -651,42 +363,8 @@ export class ExecutionService {
|
|
|
651
363
|
* @returns Array of EVC batch items (transferFromSender, swap, verify, optional enableCollateral)
|
|
652
364
|
*/
|
|
653
365
|
encodeDepositWithSwapFromWallet(args) {
|
|
654
|
-
const {
|
|
655
|
-
|
|
656
|
-
// 1. Transfer tokens from sender's wallet to swapper via SwapVerifier.transferFromSender
|
|
657
|
-
items.push({
|
|
658
|
-
targetContract: swapQuote.verify.verifierAddress,
|
|
659
|
-
onBehalfOfAccount: sender,
|
|
660
|
-
value: 0n,
|
|
661
|
-
data: encodeFunctionData({
|
|
662
|
-
abi: swapVerifierAbi,
|
|
663
|
-
functionName: "transferFromSender",
|
|
664
|
-
args: [
|
|
665
|
-
swapQuote.tokenIn.address,
|
|
666
|
-
amount,
|
|
667
|
-
swapQuote.swap.swapperAddress,
|
|
668
|
-
],
|
|
669
|
-
}),
|
|
670
|
-
});
|
|
671
|
-
// 2. Execute swap multicall
|
|
672
|
-
items.push({
|
|
673
|
-
targetContract: swapQuote.swap.swapperAddress,
|
|
674
|
-
onBehalfOfAccount: sender,
|
|
675
|
-
value: 0n,
|
|
676
|
-
data: swapQuote.swap.swapperData,
|
|
677
|
-
});
|
|
678
|
-
// 3. Verify swap
|
|
679
|
-
items.push({
|
|
680
|
-
targetContract: swapQuote.verify.verifierAddress,
|
|
681
|
-
onBehalfOfAccount: swapQuote.accountOut || sender,
|
|
682
|
-
value: 0n,
|
|
683
|
-
data: swapQuote.verify.verifierData,
|
|
684
|
-
});
|
|
685
|
-
// 4. Enable collateral if needed
|
|
686
|
-
if (enableCollateral && swapQuote.receiver) {
|
|
687
|
-
items.push(this.encodeEnableCollateral(chainId, swapQuote.accountOut || sender, swapQuote.receiver));
|
|
688
|
-
}
|
|
689
|
-
return items;
|
|
366
|
+
const { evc } = this.getCoreAddresses(args.chainId);
|
|
367
|
+
return encodeHelpers.encodeDepositWithSwapFromWallet(evc, args);
|
|
690
368
|
}
|
|
691
369
|
/**
|
|
692
370
|
* Encodes EVC batch items for swapping a token from the sender's wallet into another token
|
|
@@ -702,38 +380,7 @@ export class ExecutionService {
|
|
|
702
380
|
* @returns Array of EVC batch items (transferFromSender, swap, verify)
|
|
703
381
|
*/
|
|
704
382
|
encodeSwapFromWallet(args) {
|
|
705
|
-
|
|
706
|
-
const items = [];
|
|
707
|
-
if (swapQuote.verify.type !== "transferMin") {
|
|
708
|
-
throw new Error("Invalid swap quote type for wallet swap - must be transferMin");
|
|
709
|
-
}
|
|
710
|
-
items.push({
|
|
711
|
-
targetContract: swapQuote.verify.verifierAddress,
|
|
712
|
-
onBehalfOfAccount: sender,
|
|
713
|
-
value: 0n,
|
|
714
|
-
data: encodeFunctionData({
|
|
715
|
-
abi: swapVerifierAbi,
|
|
716
|
-
functionName: "transferFromSender",
|
|
717
|
-
args: [
|
|
718
|
-
swapQuote.tokenIn.address,
|
|
719
|
-
amount,
|
|
720
|
-
swapQuote.swap.swapperAddress,
|
|
721
|
-
],
|
|
722
|
-
}),
|
|
723
|
-
});
|
|
724
|
-
items.push({
|
|
725
|
-
targetContract: swapQuote.swap.swapperAddress,
|
|
726
|
-
onBehalfOfAccount: sender,
|
|
727
|
-
value: 0n,
|
|
728
|
-
data: swapQuote.swap.swapperData,
|
|
729
|
-
});
|
|
730
|
-
items.push({
|
|
731
|
-
targetContract: swapQuote.verify.verifierAddress,
|
|
732
|
-
onBehalfOfAccount: sender,
|
|
733
|
-
value: 0n,
|
|
734
|
-
data: swapQuote.verify.verifierData,
|
|
735
|
-
});
|
|
736
|
-
return items;
|
|
383
|
+
return encodeHelpers.encodeSwapFromWallet(args);
|
|
737
384
|
}
|
|
738
385
|
/**
|
|
739
386
|
* Encodes EVC batch items for swapping collateral: withdraw from vaultIn → swap → verify/skim to receiver; optional enable/disable collateral.
|
|
@@ -748,50 +395,8 @@ export class ExecutionService {
|
|
|
748
395
|
* @returns Array of EVC batch items (withdraw, swap, verify/skim, optional disableCollateral, optional enableCollateral)
|
|
749
396
|
*/
|
|
750
397
|
encodeSwapCollateral(args) {
|
|
751
|
-
const {
|
|
752
|
-
|
|
753
|
-
// 1. Withdraw from source vault to swapper
|
|
754
|
-
const withdrawAmount = BigInt(swapQuote.amountInMax || swapQuote.amountIn);
|
|
755
|
-
items.push({
|
|
756
|
-
targetContract: swapQuote.vaultIn,
|
|
757
|
-
onBehalfOfAccount: swapQuote.accountIn,
|
|
758
|
-
value: 0n,
|
|
759
|
-
data: encodeFunctionData({
|
|
760
|
-
abi: eVaultAbi,
|
|
761
|
-
functionName: "withdraw",
|
|
762
|
-
args: [
|
|
763
|
-
withdrawAmount,
|
|
764
|
-
swapQuote.swap.swapperAddress,
|
|
765
|
-
swapQuote.accountIn,
|
|
766
|
-
],
|
|
767
|
-
}),
|
|
768
|
-
});
|
|
769
|
-
// 2. Execute swap multicall
|
|
770
|
-
items.push({
|
|
771
|
-
targetContract: swapQuote.swap.swapperAddress,
|
|
772
|
-
onBehalfOfAccount: swapQuote.accountIn,
|
|
773
|
-
value: 0n,
|
|
774
|
-
data: swapQuote.swap.swapperData,
|
|
775
|
-
});
|
|
776
|
-
// 3. Verify swap and skim
|
|
777
|
-
if (swapQuote.verify.type !== "skimMin") {
|
|
778
|
-
throw new Error("Invalid swap quote type for swap collateral - must be skimMin");
|
|
779
|
-
}
|
|
780
|
-
items.push({
|
|
781
|
-
targetContract: swapQuote.verify.verifierAddress,
|
|
782
|
-
onBehalfOfAccount: swapQuote.accountOut,
|
|
783
|
-
value: 0n,
|
|
784
|
-
data: swapQuote.verify.verifierData,
|
|
785
|
-
});
|
|
786
|
-
// 4. Disable collateral if needed (for max swap)
|
|
787
|
-
if (isMax && disableCollateralOnMax) {
|
|
788
|
-
items.push(this.encodeDisableCollateral(chainId, swapQuote.accountIn, swapQuote.vaultIn));
|
|
789
|
-
}
|
|
790
|
-
// 5. Enable collateral if needed
|
|
791
|
-
if (enableCollateral) {
|
|
792
|
-
items.push(this.encodeEnableCollateral(chainId, swapQuote.accountOut, swapQuote.receiver));
|
|
793
|
-
}
|
|
794
|
-
return items;
|
|
398
|
+
const { evc } = this.getCoreAddresses(args.chainId);
|
|
399
|
+
return encodeHelpers.encodeSwapCollateral(evc, args);
|
|
795
400
|
}
|
|
796
401
|
/**
|
|
797
402
|
* Encodes EVC batch items for swapping debt: enableController → borrow from vaultIn → swap → verify/repay (debtMax).
|
|
@@ -805,46 +410,51 @@ export class ExecutionService {
|
|
|
805
410
|
* @param args.isMax - If true, treats as full debt swap (can trigger disableControllerOnMax)
|
|
806
411
|
* @returns Array of EVC batch items (optional enableController, borrow, swap, verify/repay, optional disableController)
|
|
807
412
|
*/
|
|
808
|
-
encodeSwapDebt(
|
|
809
|
-
const
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
413
|
+
encodeSwapDebt(args) {
|
|
414
|
+
const { evc } = this.getCoreAddresses(args.chainId);
|
|
415
|
+
return encodeHelpers.encodeSwapDebt(evc, args);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Encodes EVC batch items for migrating a supplied/collateral position between two same-asset vaults.
|
|
419
|
+
* Partial migration uses withdraw(amount, toVault, account) then skim(amount, account).
|
|
420
|
+
* Max migration uses redeem(maxShares || maxUint256, toVault, account) then skim(amount, account).
|
|
421
|
+
*
|
|
422
|
+
* @param args - Same-asset collateral migration encoding arguments
|
|
423
|
+
* @param args.chainId - Chain ID (used for EVC enable/disable collateral)
|
|
424
|
+
* @param args.fromVault - Source vault holding the supplied shares
|
|
425
|
+
* @param args.toVault - Destination vault with the same underlying asset
|
|
426
|
+
* @param args.amount - Asset amount expected to arrive at the destination vault and be skimmed
|
|
427
|
+
* @param args.account - Sub-account that owns the source shares and receives destination shares
|
|
428
|
+
* @param args.isMax - If true, redeems shares instead of withdrawing assets
|
|
429
|
+
* @param args.maxShares - Optional exact share amount for max migration; defaults to maxUint256
|
|
430
|
+
* @param args.enableCollateralTo - If true, enables the destination vault as collateral after skim
|
|
431
|
+
* @param args.disableCollateralFrom - If true, disables the source vault as collateral after enabling the destination
|
|
432
|
+
* @returns Array of EVC batch items
|
|
433
|
+
*/
|
|
434
|
+
encodeMigrateSameAssetCollateral(args) {
|
|
435
|
+
const { evc } = this.getCoreAddresses(args.chainId);
|
|
436
|
+
return encodeHelpers.encodeMigrateSameAssetCollateral(evc, args);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Encodes EVC batch items for migrating a full debt position between two same-asset liability vaults.
|
|
440
|
+
* Flow: enable new controller → borrow with interest cushion to old vault → skim → repay old debt with shares
|
|
441
|
+
* → disable old controller → optionally sweep cushion to the new vault → optionally transfer remaining new-vault shares.
|
|
442
|
+
*
|
|
443
|
+
* @param args - Same-asset debt migration encoding arguments
|
|
444
|
+
* @param args.chainId - Chain ID (used for EVC enable controller)
|
|
445
|
+
* @param args.oldLiabilityVault - Existing debt vault to fully repay and disable
|
|
446
|
+
* @param args.newLiabilityVault - New same-asset debt vault to borrow from
|
|
447
|
+
* @param args.amount - Current debt amount before applying the 0.01% interest cushion
|
|
448
|
+
* @param args.account - Sub-account that owns the debt position
|
|
449
|
+
* @param args.enableController - If true, enables the new liability vault as controller first
|
|
450
|
+
* @param args.disableController - If true, disables the old liability vault after repayment
|
|
451
|
+
* @param args.sweepExcess - If true, redeems any old-vault cushion shares back to the new vault and skims them
|
|
452
|
+
* @param args.transferRemainingSharesTo - If set, transfers all new-vault shares from the sub-account to this address
|
|
453
|
+
* @returns Array of EVC batch items
|
|
454
|
+
*/
|
|
455
|
+
encodeMigrateSameAssetDebt(args) {
|
|
456
|
+
const { evc } = this.getCoreAddresses(args.chainId);
|
|
457
|
+
return encodeHelpers.encodeMigrateSameAssetDebt(evc, args);
|
|
848
458
|
}
|
|
849
459
|
/**
|
|
850
460
|
* Encodes EVC batch items for transferring vault shares between sub-accounts.
|
|
@@ -859,28 +469,9 @@ export class ExecutionService {
|
|
|
859
469
|
* @param args.disableCollateralFrom - If true, prepends disableCollateral( from, vault ) via EVC before transfer
|
|
860
470
|
* @returns Array of EVC batch items (optional disableCollateralFrom, transfer, optional enableCollateralTo)
|
|
861
471
|
*/
|
|
862
|
-
encodeTransfer({ chainId,
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
if (disableCollateralFrom) {
|
|
866
|
-
items.push(this.encodeDisableCollateral(chainId, from, vault));
|
|
867
|
-
}
|
|
868
|
-
// Add transfer operation
|
|
869
|
-
items.push({
|
|
870
|
-
targetContract: vault,
|
|
871
|
-
onBehalfOfAccount: from,
|
|
872
|
-
value: 0n,
|
|
873
|
-
data: encodeFunctionData({
|
|
874
|
-
abi: eVaultAbi,
|
|
875
|
-
functionName: "transfer",
|
|
876
|
-
args: [to, amount],
|
|
877
|
-
}),
|
|
878
|
-
});
|
|
879
|
-
// Add enable collateral to receiver if flag is set
|
|
880
|
-
if (enableCollateralTo) {
|
|
881
|
-
items.push(this.encodeEnableCollateral(chainId, to, vault));
|
|
882
|
-
}
|
|
883
|
-
return items;
|
|
472
|
+
encodeTransfer({ chainId, ...args }) {
|
|
473
|
+
const { evc } = this.getCoreAddresses(chainId);
|
|
474
|
+
return encodeHelpers.encodeTransfer(evc, { chainId, ...args });
|
|
884
475
|
}
|
|
885
476
|
/**
|
|
886
477
|
* Encodes a single EVC batch item that calls Permit2's `permit` with the given message and signature.
|
|
@@ -894,73 +485,23 @@ export class ExecutionService {
|
|
|
894
485
|
* @returns Single EVC batch item (targetContract = Permit2, permit call)
|
|
895
486
|
*/
|
|
896
487
|
encodePermit2Call(args) {
|
|
897
|
-
const {
|
|
898
|
-
|
|
899
|
-
const permit2 = deployment.addresses.coreAddrs.permit2;
|
|
900
|
-
return {
|
|
901
|
-
targetContract: permit2,
|
|
902
|
-
onBehalfOfAccount: owner,
|
|
903
|
-
value: 0n,
|
|
904
|
-
data: encodeFunctionData({
|
|
905
|
-
abi: permit2PermitAbi,
|
|
906
|
-
functionName: "permit",
|
|
907
|
-
args: [owner, message, signature],
|
|
908
|
-
}),
|
|
909
|
-
};
|
|
488
|
+
const { permit2 } = this.getCoreAddresses(args.chainId);
|
|
489
|
+
return encodeHelpers.encodePermit2Call(permit2, args);
|
|
910
490
|
}
|
|
911
491
|
encodeEnableCollateral(chainId, account, vault) {
|
|
912
|
-
const
|
|
913
|
-
|
|
914
|
-
return {
|
|
915
|
-
targetContract: evc,
|
|
916
|
-
onBehalfOfAccount: zeroAddress,
|
|
917
|
-
value: 0n,
|
|
918
|
-
data: encodeFunctionData({
|
|
919
|
-
abi: ethereumVaultConnectorAbi,
|
|
920
|
-
functionName: "enableCollateral",
|
|
921
|
-
args: [account, vault],
|
|
922
|
-
}),
|
|
923
|
-
};
|
|
492
|
+
const { evc } = this.getCoreAddresses(chainId);
|
|
493
|
+
return encodeHelpers.encodeEnableCollateral(evc, account, vault);
|
|
924
494
|
}
|
|
925
495
|
encodeDisableCollateral(chainId, account, vault) {
|
|
926
|
-
const
|
|
927
|
-
|
|
928
|
-
return {
|
|
929
|
-
targetContract: evc,
|
|
930
|
-
onBehalfOfAccount: zeroAddress,
|
|
931
|
-
value: 0n,
|
|
932
|
-
data: encodeFunctionData({
|
|
933
|
-
abi: ethereumVaultConnectorAbi,
|
|
934
|
-
functionName: "disableCollateral",
|
|
935
|
-
args: [account, vault],
|
|
936
|
-
}),
|
|
937
|
-
};
|
|
496
|
+
const { evc } = this.getCoreAddresses(chainId);
|
|
497
|
+
return encodeHelpers.encodeDisableCollateral(evc, account, vault);
|
|
938
498
|
}
|
|
939
499
|
encodeEnableController(chainId, account, vault) {
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
return {
|
|
943
|
-
targetContract: evc,
|
|
944
|
-
onBehalfOfAccount: zeroAddress,
|
|
945
|
-
value: 0n,
|
|
946
|
-
data: encodeFunctionData({
|
|
947
|
-
abi: ethereumVaultConnectorAbi,
|
|
948
|
-
functionName: "enableController",
|
|
949
|
-
args: [account, vault],
|
|
950
|
-
}),
|
|
951
|
-
};
|
|
500
|
+
const { evc } = this.getCoreAddresses(chainId);
|
|
501
|
+
return encodeHelpers.encodeEnableController(evc, account, vault);
|
|
952
502
|
}
|
|
953
503
|
encodeDisableController(vault, account) {
|
|
954
|
-
return
|
|
955
|
-
targetContract: vault,
|
|
956
|
-
onBehalfOfAccount: account,
|
|
957
|
-
value: 0n,
|
|
958
|
-
data: encodeFunctionData({
|
|
959
|
-
abi: eVaultAbi,
|
|
960
|
-
functionName: "disableController",
|
|
961
|
-
args: [],
|
|
962
|
-
}),
|
|
963
|
-
};
|
|
504
|
+
return encodeHelpers.encodeDisableController(vault, account);
|
|
964
505
|
}
|
|
965
506
|
/**
|
|
966
507
|
* Builds EIP-712 typed data for a Permit2 PermitSingle signature (token approval for a spender).
|
|
@@ -977,46 +518,10 @@ export class ExecutionService {
|
|
|
977
518
|
* @returns EIP-712 typed data (domain, types, primaryType, message) for signing
|
|
978
519
|
*/
|
|
979
520
|
getPermit2TypedData(args) {
|
|
980
|
-
const
|
|
981
|
-
|
|
982
|
-
const deployment = this.deploymentService.getDeployment(chainId);
|
|
983
|
-
const permit2 = deployment.addresses.coreAddrs.permit2;
|
|
984
|
-
const permitSingle = {
|
|
985
|
-
details: {
|
|
986
|
-
token,
|
|
987
|
-
amount: amount > maxUint160 ? maxUint160 : amount,
|
|
988
|
-
expiration: expiration ?? Number(maxUint48),
|
|
989
|
-
nonce,
|
|
990
|
-
},
|
|
991
|
-
spender,
|
|
992
|
-
sigDeadline: sigDeadline ?? nowInSeconds() + PERMIT2_SIG_WINDOW,
|
|
993
|
-
};
|
|
994
|
-
return {
|
|
995
|
-
domain: {
|
|
996
|
-
name: "Permit2",
|
|
997
|
-
chainId,
|
|
998
|
-
verifyingContract: permit2,
|
|
999
|
-
},
|
|
1000
|
-
types: PERMIT2_TYPES,
|
|
1001
|
-
primaryType: "PermitSingle",
|
|
1002
|
-
message: permitSingle,
|
|
1003
|
-
};
|
|
521
|
+
const { permit2 } = this.getCoreAddresses(args.chainId);
|
|
522
|
+
return encodeHelpers.getPermit2TypedData(permit2, args);
|
|
1004
523
|
}
|
|
1005
|
-
/**
|
|
1006
|
-
* Decodes EVC batch items into human-readable function names and named arguments.
|
|
1007
|
-
* Tries known ABIs (EVC, eVault, Permit2, swapper, swapVerifier) to decode each item's data.
|
|
1008
|
-
*
|
|
1009
|
-
* @param batch - Array of EVC batch items (targetContract, onBehalfOfAccount, value, data) to decode
|
|
1010
|
-
* @param extraAbis - Optional extra ABIs to try first when decoding unknown batch items.
|
|
1011
|
-
* @returns Array of decoded items with targetContract, onBehalfOfAccount, functionName, and args (record of param name to value). Throws if any item cannot be decoded.
|
|
1012
|
-
* @example
|
|
1013
|
-
* const batchItems = executionService.encodeDeposit({ ... });
|
|
1014
|
-
* const described = executionService.describeBatch(batchItems);
|
|
1015
|
-
* console.log(described[0].functionName); // "deposit"
|
|
1016
|
-
* console.log(described[0].args); // { amount: 1000n, receiver: "0x..." }
|
|
1017
|
-
*/
|
|
1018
524
|
describeBatch(batch, extraAbis) {
|
|
1019
|
-
const decodedBatchItems = [];
|
|
1020
525
|
const executionDecodeAbis = [
|
|
1021
526
|
...(extraAbis ?? []),
|
|
1022
527
|
ethereumVaultConnectorAbi,
|
|
@@ -1025,7 +530,7 @@ export class ExecutionService {
|
|
|
1025
530
|
swapperAbi,
|
|
1026
531
|
swapVerifierAbi,
|
|
1027
532
|
];
|
|
1028
|
-
|
|
533
|
+
const decodeBatchItem = (item) => {
|
|
1029
534
|
let decoded = false;
|
|
1030
535
|
for (const abi of executionDecodeAbis) {
|
|
1031
536
|
try {
|
|
@@ -1050,14 +555,13 @@ export class ExecutionService {
|
|
|
1050
555
|
}
|
|
1051
556
|
});
|
|
1052
557
|
}
|
|
1053
|
-
|
|
558
|
+
decoded = true;
|
|
559
|
+
return {
|
|
1054
560
|
targetContract: item.targetContract,
|
|
1055
561
|
onBehalfOfAccount: item.onBehalfOfAccount,
|
|
1056
562
|
functionName: decodedData.functionName,
|
|
1057
563
|
args: namedArgs,
|
|
1058
|
-
}
|
|
1059
|
-
decoded = true;
|
|
1060
|
-
break;
|
|
564
|
+
};
|
|
1061
565
|
}
|
|
1062
566
|
catch { }
|
|
1063
567
|
}
|
|
@@ -1069,29 +573,35 @@ export class ExecutionService {
|
|
|
1069
573
|
try {
|
|
1070
574
|
const result = plugin.decodeBatchItem(item);
|
|
1071
575
|
if (result) {
|
|
1072
|
-
decodedBatchItems.push(result);
|
|
1073
576
|
decoded = true;
|
|
1074
|
-
|
|
577
|
+
return result;
|
|
1075
578
|
}
|
|
1076
579
|
}
|
|
1077
580
|
catch { }
|
|
1078
581
|
}
|
|
1079
582
|
}
|
|
1080
583
|
if (!decoded) {
|
|
1081
|
-
|
|
584
|
+
return {
|
|
1082
585
|
targetContract: item.targetContract,
|
|
1083
586
|
onBehalfOfAccount: item.onBehalfOfAccount,
|
|
1084
587
|
functionName: "Unknown",
|
|
1085
588
|
args: {},
|
|
1086
|
-
}
|
|
589
|
+
};
|
|
1087
590
|
}
|
|
1088
|
-
|
|
1089
|
-
|
|
591
|
+
throw new Error("unreachable batch item decode state");
|
|
592
|
+
};
|
|
593
|
+
return batch.map((entry) => isEVCBatchOperation(entry)
|
|
594
|
+
? {
|
|
595
|
+
type: "operation",
|
|
596
|
+
name: entry.name,
|
|
597
|
+
items: entry.items.map(decodeBatchItem),
|
|
598
|
+
}
|
|
599
|
+
: decodeBatchItem(entry));
|
|
1090
600
|
}
|
|
1091
601
|
/**
|
|
1092
602
|
* Merges multiple transaction plans into a single plan.
|
|
1093
603
|
* Required approvals for the same (token, owner, spender) are summed.
|
|
1094
|
-
* Executable items are preserved in order; adjacent EVC
|
|
604
|
+
* Executable items are preserved in order; adjacent EVC batches are concatenated without flattening operation groupings.
|
|
1095
605
|
* Can be used to construct a transaction queue.
|
|
1096
606
|
*
|
|
1097
607
|
* @param plans - Array of transaction plans to merge
|
|
@@ -1117,19 +627,37 @@ export class ExecutionService {
|
|
|
1117
627
|
else if (item.type === "evcBatch") {
|
|
1118
628
|
const previous = executableItems[executableItems.length - 1];
|
|
1119
629
|
if (previous?.type === "evcBatch") {
|
|
1120
|
-
previous.items.push(...item.items);
|
|
630
|
+
previous.items.push(...cloneBatchEntries(item.items));
|
|
1121
631
|
}
|
|
1122
632
|
else {
|
|
1123
|
-
executableItems.push({
|
|
633
|
+
executableItems.push({
|
|
634
|
+
type: "evcBatch",
|
|
635
|
+
items: cloneBatchEntries(item.items),
|
|
636
|
+
});
|
|
1124
637
|
}
|
|
1125
638
|
}
|
|
1126
639
|
else {
|
|
1127
|
-
|
|
640
|
+
throw new Error("ExecutionService.mergePlans cannot merge contractCall plan items. Merge these plans manually to preserve call boundaries.");
|
|
1128
641
|
}
|
|
1129
642
|
}
|
|
1130
643
|
}
|
|
1131
644
|
return [...approvalByKey.values(), ...executableItems];
|
|
1132
645
|
}
|
|
646
|
+
/**
|
|
647
|
+
* Appends a raw batch item to the last EVC batch in the plan, or creates one.
|
|
648
|
+
* Mutates and returns the provided plan.
|
|
649
|
+
*/
|
|
650
|
+
addBatchItemToPlan(plan, item) {
|
|
651
|
+
for (let index = plan.length - 1; index >= 0; index--) {
|
|
652
|
+
const planItem = plan[index];
|
|
653
|
+
if (planItem?.type === "evcBatch") {
|
|
654
|
+
planItem.items.push(item);
|
|
655
|
+
return plan;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
plan.push({ type: "evcBatch", items: [item] });
|
|
659
|
+
return plan;
|
|
660
|
+
}
|
|
1133
661
|
/**
|
|
1134
662
|
* Converts EVC batch items into a transaction plan.
|
|
1135
663
|
* Returns a plan with a single evcBatch containing the given items (no required approvals).
|
|
@@ -1138,127 +666,50 @@ export class ExecutionService {
|
|
|
1138
666
|
* @param items - EVC batch items to wrap in a plan
|
|
1139
667
|
* @returns Transaction plan containing one evcBatch with the items
|
|
1140
668
|
*/
|
|
1141
|
-
convertBatchItemsToPlan(items) {
|
|
669
|
+
convertBatchItemsToPlan(items, operationName) {
|
|
1142
670
|
if (items.length === 0)
|
|
1143
671
|
return [];
|
|
672
|
+
if (operationName) {
|
|
673
|
+
return [
|
|
674
|
+
{
|
|
675
|
+
type: "evcBatch",
|
|
676
|
+
items: [{ type: "operation", name: operationName, items }],
|
|
677
|
+
},
|
|
678
|
+
];
|
|
679
|
+
}
|
|
1144
680
|
return [{ type: "evcBatch", items }];
|
|
1145
681
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
*/
|
|
1149
|
-
encodeRepayWithSharesSameAssetAndVault({ chainId: _chainId, vault, amount, from, receiver, disableController, }) {
|
|
1150
|
-
const items = [];
|
|
1151
|
-
// Repay with shares
|
|
1152
|
-
items.push({
|
|
1153
|
-
targetContract: vault,
|
|
1154
|
-
onBehalfOfAccount: from,
|
|
1155
|
-
value: 0n,
|
|
1156
|
-
data: encodeFunctionData({
|
|
1157
|
-
abi: eVaultAbi,
|
|
1158
|
-
functionName: "repayWithShares",
|
|
1159
|
-
args: [amount, receiver],
|
|
1160
|
-
}),
|
|
1161
|
-
});
|
|
1162
|
-
// Disable controller if needed (for max repay)
|
|
1163
|
-
if (disableController) {
|
|
1164
|
-
items.push(this.encodeDisableController(vault, receiver));
|
|
1165
|
-
}
|
|
1166
|
-
return items;
|
|
682
|
+
encodeTransferFromMax(vault, from, to) {
|
|
683
|
+
return encodeHelpers.encodeTransferFromMax(vault, from, to);
|
|
1167
684
|
}
|
|
1168
685
|
/**
|
|
1169
|
-
*
|
|
686
|
+
* Appends post-full-repay cleanup calls owned by plan builders:
|
|
687
|
+
* disable each active collateral used by the repaid borrow, transfer those collateral shares to the owner,
|
|
688
|
+
* and, when a source deposit funded the repay, transfer any remaining source-vault shares to the owner.
|
|
1170
689
|
*/
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
const
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
if (
|
|
1183
|
-
|
|
1184
|
-
}
|
|
1185
|
-
// 1. Withdraw from collateral vault
|
|
1186
|
-
items.push({
|
|
1187
|
-
targetContract: fromVault,
|
|
1188
|
-
onBehalfOfAccount: from,
|
|
1189
|
-
value: 0n,
|
|
1190
|
-
data: encodeFunctionData({
|
|
1191
|
-
abi: eVaultAbi,
|
|
1192
|
-
functionName: "withdraw",
|
|
1193
|
-
args: [amountWithExtra, toVault, from],
|
|
1194
|
-
}),
|
|
1195
|
-
});
|
|
1196
|
-
// 2. Skim exact withdrawal amount to liability vault
|
|
1197
|
-
items.push({
|
|
1198
|
-
targetContract: toVault,
|
|
1199
|
-
onBehalfOfAccount: receiver,
|
|
1200
|
-
value: 0n,
|
|
1201
|
-
data: encodeFunctionData({
|
|
1202
|
-
abi: eVaultAbi,
|
|
1203
|
-
functionName: "skim",
|
|
1204
|
-
args: [amountWithExtra, receiver],
|
|
1205
|
-
}),
|
|
1206
|
-
});
|
|
1207
|
-
// 3. Repay with shares (max)
|
|
1208
|
-
items.push({
|
|
1209
|
-
targetContract: toVault,
|
|
1210
|
-
onBehalfOfAccount: receiver,
|
|
1211
|
-
value: 0n,
|
|
1212
|
-
data: encodeFunctionData({
|
|
1213
|
-
abi: eVaultAbi,
|
|
1214
|
-
functionName: "repayWithShares",
|
|
1215
|
-
// max is ok now, because skim deposited exact amount and it is the full debt,
|
|
1216
|
-
// so pre-existing balance will not be consumed
|
|
1217
|
-
args: [maxUint256, receiver],
|
|
1218
|
-
}),
|
|
1219
|
-
});
|
|
1220
|
-
// 4. Disable controller if needed
|
|
1221
|
-
if (disableControllerOnMax) {
|
|
1222
|
-
items.push(this.encodeDisableController(toVault, receiver));
|
|
690
|
+
appendMaxRepayCleanup(args) {
|
|
691
|
+
const { account, liabilityPosition, receiver, batchItems, sourceAccount, sourceVault, } = args;
|
|
692
|
+
const receiverSubAccount = typeof account.getSubAccount === "function"
|
|
693
|
+
? account.getSubAccount(receiver)
|
|
694
|
+
: undefined;
|
|
695
|
+
const activeCollaterals = receiverSubAccount
|
|
696
|
+
? resolveBorrowCollateralPositions(receiverSubAccount, liabilityPosition)
|
|
697
|
+
: [];
|
|
698
|
+
const transferredPositions = new Set();
|
|
699
|
+
for (const collateral of activeCollaterals) {
|
|
700
|
+
batchItems.push(this.encodeDisableCollateral(account.chainId, receiver, collateral.vaultAddress));
|
|
701
|
+
if (getAddress(receiver) !== getAddress(account.owner)) {
|
|
702
|
+
batchItems.push(this.encodeTransferFromMax(collateral.vaultAddress, receiver, account.owner));
|
|
1223
703
|
}
|
|
704
|
+
transferredPositions.add(`${getAddress(receiver)}:${getAddress(collateral.vaultAddress)}`);
|
|
1224
705
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
value: 0n,
|
|
1232
|
-
data: encodeFunctionData({
|
|
1233
|
-
abi: eVaultAbi,
|
|
1234
|
-
functionName: "withdraw",
|
|
1235
|
-
args: [amount, toVault, from],
|
|
1236
|
-
}),
|
|
1237
|
-
});
|
|
1238
|
-
// 2. Skim the received assets into the borrow sub-account.
|
|
1239
|
-
items.push({
|
|
1240
|
-
targetContract: toVault,
|
|
1241
|
-
onBehalfOfAccount: receiver,
|
|
1242
|
-
value: 0n,
|
|
1243
|
-
data: encodeFunctionData({
|
|
1244
|
-
abi: eVaultAbi,
|
|
1245
|
-
functionName: "skim",
|
|
1246
|
-
args: [amount, receiver],
|
|
1247
|
-
}),
|
|
1248
|
-
});
|
|
1249
|
-
// 3. Burn the skimmed shares to repay debt.
|
|
1250
|
-
items.push({
|
|
1251
|
-
targetContract: toVault,
|
|
1252
|
-
onBehalfOfAccount: receiver,
|
|
1253
|
-
value: 0n,
|
|
1254
|
-
data: encodeFunctionData({
|
|
1255
|
-
abi: eVaultAbi,
|
|
1256
|
-
functionName: "repayWithShares",
|
|
1257
|
-
args: [amount > 0n ? amount - 1n : 0n, receiver],
|
|
1258
|
-
}),
|
|
1259
|
-
});
|
|
706
|
+
if (sourceAccount && sourceVault) {
|
|
707
|
+
const sourcePositionKey = `${getAddress(sourceAccount)}:${getAddress(sourceVault)}`;
|
|
708
|
+
if (getAddress(sourceAccount) !== getAddress(account.owner) &&
|
|
709
|
+
!transferredPositions.has(sourcePositionKey)) {
|
|
710
|
+
batchItems.push(this.encodeTransferFromMax(sourceVault, sourceAccount, account.owner));
|
|
711
|
+
}
|
|
1260
712
|
}
|
|
1261
|
-
return items;
|
|
1262
713
|
}
|
|
1263
714
|
/**
|
|
1264
715
|
* Resolves RequiredApproval items in a transaction plan by filling in each item's `resolved` field.
|
|
@@ -1381,6 +832,9 @@ export class ExecutionService {
|
|
|
1381
832
|
*/
|
|
1382
833
|
async resolveRequiredApprovals(args) {
|
|
1383
834
|
const { plan, chainId, account, usePermit2 = true, unlimitedApproval = false, } = args;
|
|
835
|
+
if (!this.walletService) {
|
|
836
|
+
throw new Error("ExecutionService.resolveRequiredApprovals requires a walletService. Pass it to the ExecutionService constructor or call setWalletService().");
|
|
837
|
+
}
|
|
1384
838
|
// Filter transaction plan for only RequiredApproval items
|
|
1385
839
|
const requiredApprovals = plan.filter((item) => item.type === "requiredApproval");
|
|
1386
840
|
// Transform RequiredApprovals into AssetWithSpenders
|
|
@@ -1445,10 +899,7 @@ export class ExecutionService {
|
|
|
1445
899
|
enableCollateral: !isCollateralEnabled && enableCollateral,
|
|
1446
900
|
// Permit2 is handled separately in the plan
|
|
1447
901
|
});
|
|
1448
|
-
plan.push(
|
|
1449
|
-
type: "evcBatch",
|
|
1450
|
-
items: batchItems,
|
|
1451
|
-
});
|
|
902
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "deposit"));
|
|
1452
903
|
return plan;
|
|
1453
904
|
}
|
|
1454
905
|
/**
|
|
@@ -1490,10 +941,7 @@ export class ExecutionService {
|
|
|
1490
941
|
enableCollateral: !isCollateralEnabled && enableCollateral,
|
|
1491
942
|
// Permit2 is handled separately in the plan
|
|
1492
943
|
});
|
|
1493
|
-
plan.push(
|
|
1494
|
-
type: "evcBatch",
|
|
1495
|
-
items: batchItems,
|
|
1496
|
-
});
|
|
944
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "mint"));
|
|
1497
945
|
return plan;
|
|
1498
946
|
}
|
|
1499
947
|
/**
|
|
@@ -1522,10 +970,7 @@ export class ExecutionService {
|
|
|
1522
970
|
owner,
|
|
1523
971
|
disableCollateral: disableCollateral && (!position || position.isCollateral),
|
|
1524
972
|
});
|
|
1525
|
-
plan.push(
|
|
1526
|
-
type: "evcBatch",
|
|
1527
|
-
items: batchItems,
|
|
1528
|
-
});
|
|
973
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "withdraw"));
|
|
1529
974
|
return plan;
|
|
1530
975
|
}
|
|
1531
976
|
/**
|
|
@@ -1555,10 +1000,7 @@ export class ExecutionService {
|
|
|
1555
1000
|
owner,
|
|
1556
1001
|
disableCollateral: disableCollateral && (!position || position.isCollateral),
|
|
1557
1002
|
});
|
|
1558
|
-
plan.push(
|
|
1559
|
-
type: "evcBatch",
|
|
1560
|
-
items: batchItems,
|
|
1561
|
-
});
|
|
1003
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "redeem"));
|
|
1562
1004
|
return plan;
|
|
1563
1005
|
}
|
|
1564
1006
|
/**
|
|
@@ -1580,6 +1022,12 @@ export class ExecutionService {
|
|
|
1580
1022
|
planBorrow(args) {
|
|
1581
1023
|
const { vault, amount, receiver, borrowAccount, account, collateral } = args;
|
|
1582
1024
|
const plan = [];
|
|
1025
|
+
const savingsCollateral = isSavingsCollateral(collateral)
|
|
1026
|
+
? collateral
|
|
1027
|
+
: undefined;
|
|
1028
|
+
const walletCollateral = isWalletCollateral(collateral)
|
|
1029
|
+
? collateral
|
|
1030
|
+
: undefined;
|
|
1583
1031
|
const enableCollateral = collateral && collateral.amount > 0n
|
|
1584
1032
|
? !(account?.isCollateralEnabled(borrowAccount, collateral.vault) ??
|
|
1585
1033
|
false)
|
|
@@ -1588,15 +1036,15 @@ export class ExecutionService {
|
|
|
1588
1036
|
// Default: controller is not enabled when account/sub-account is not available
|
|
1589
1037
|
const currentController = account?.getCurrentController(borrowAccount);
|
|
1590
1038
|
const enableController = !(account?.isControllerEnabled(borrowAccount, vault) ?? false);
|
|
1591
|
-
if (
|
|
1039
|
+
if (walletCollateral && walletCollateral.amount > 0n) {
|
|
1592
1040
|
// Approval is needed from the account owner (who owns the wallet tokens)
|
|
1593
1041
|
// Add approval requirement (will be resolved later with Wallet data)
|
|
1594
1042
|
plan.push({
|
|
1595
1043
|
type: "requiredApproval",
|
|
1596
|
-
token:
|
|
1044
|
+
token: walletCollateral.asset,
|
|
1597
1045
|
owner: account.owner,
|
|
1598
|
-
spender:
|
|
1599
|
-
amount:
|
|
1046
|
+
spender: walletCollateral.vault,
|
|
1047
|
+
amount: walletCollateral.amount,
|
|
1600
1048
|
});
|
|
1601
1049
|
}
|
|
1602
1050
|
const batchItems = this.encodeBorrow({
|
|
@@ -1610,12 +1058,18 @@ export class ExecutionService {
|
|
|
1610
1058
|
currentController: currentController || undefined,
|
|
1611
1059
|
enableCollateral,
|
|
1612
1060
|
collateralVault: collateral?.vault,
|
|
1613
|
-
collateralAmount:
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1061
|
+
collateralAmount: walletCollateral?.amount,
|
|
1062
|
+
collateralShareSource: savingsCollateral
|
|
1063
|
+
? {
|
|
1064
|
+
from: savingsCollateral.from,
|
|
1065
|
+
shares: savingsCollateral.amount,
|
|
1066
|
+
disableCollateralFrom: savingsCollateral.disableCollateralFrom &&
|
|
1067
|
+
(account?.isCollateralEnabled(savingsCollateral.from, savingsCollateral.vault) ??
|
|
1068
|
+
false),
|
|
1069
|
+
}
|
|
1070
|
+
: undefined,
|
|
1618
1071
|
});
|
|
1072
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "borrow"));
|
|
1619
1073
|
return plan;
|
|
1620
1074
|
}
|
|
1621
1075
|
/**
|
|
@@ -1659,10 +1113,7 @@ export class ExecutionService {
|
|
|
1659
1113
|
enableController,
|
|
1660
1114
|
enableCollateral,
|
|
1661
1115
|
});
|
|
1662
|
-
plan.push(
|
|
1663
|
-
type: "evcBatch",
|
|
1664
|
-
items: batchItems,
|
|
1665
|
-
});
|
|
1116
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "liquidation"));
|
|
1666
1117
|
return plan;
|
|
1667
1118
|
}
|
|
1668
1119
|
/**
|
|
@@ -1674,10 +1125,11 @@ export class ExecutionService {
|
|
|
1674
1125
|
* @param args.liabilityAmount - Amount of liability asset to repay (use maxUint256 for "repay all")
|
|
1675
1126
|
* @param args.receiver - Sub-account address whose debt is being repaid
|
|
1676
1127
|
* @param args.account - Account entity; used for chainId, owner, and position (to resolve liability asset)
|
|
1128
|
+
* @param args.cleanupOnMax - When true and liabilityAmount is maxUint256, the batch disables active collaterals on the repaid sub-account and transfers their shares to the owner account (default false)
|
|
1677
1129
|
* @returns Array of transaction plan items (approval + EVC batch)
|
|
1678
1130
|
*/
|
|
1679
1131
|
planRepayFromWallet(args) {
|
|
1680
|
-
const { liabilityVault, liabilityAmount, receiver, account } = args;
|
|
1132
|
+
const { liabilityVault, liabilityAmount, receiver, account, cleanupOnMax = false, } = args;
|
|
1681
1133
|
const plan = [];
|
|
1682
1134
|
// Get position to determine asset
|
|
1683
1135
|
const position = account?.getPosition(receiver, liabilityVault);
|
|
@@ -1692,6 +1144,7 @@ export class ExecutionService {
|
|
|
1692
1144
|
spender: liabilityVault,
|
|
1693
1145
|
amount: liabilityAmount,
|
|
1694
1146
|
});
|
|
1147
|
+
const isMax = liabilityAmount === maxUint256;
|
|
1695
1148
|
// Build EVC batch items
|
|
1696
1149
|
const batchItems = this.encodeRepayFromWallet({
|
|
1697
1150
|
chainId: account.chainId,
|
|
@@ -1700,18 +1153,28 @@ export class ExecutionService {
|
|
|
1700
1153
|
liabilityAmount,
|
|
1701
1154
|
receiver,
|
|
1702
1155
|
disableControllerOnMax: true,
|
|
1703
|
-
isMax
|
|
1156
|
+
isMax,
|
|
1704
1157
|
// Permit2 is handled separately in the plan
|
|
1705
1158
|
});
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1159
|
+
if (cleanupOnMax && isMax) {
|
|
1160
|
+
this.appendMaxRepayCleanup({
|
|
1161
|
+
account,
|
|
1162
|
+
liabilityPosition: position,
|
|
1163
|
+
receiver,
|
|
1164
|
+
batchItems,
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "repayFromWallet"));
|
|
1710
1168
|
return plan;
|
|
1711
1169
|
}
|
|
1712
1170
|
/**
|
|
1713
1171
|
* Builds a transaction plan for repaying debt using assets from another vault deposit (same asset only).
|
|
1714
|
-
* Use `maxUint256` for `liabilityAmount` to
|
|
1172
|
+
* Use `maxUint256` for `liabilityAmount` to perform a full repay.
|
|
1173
|
+
* Full repay can opt into cleanup. For different-vault full repays, cleanup only redeems
|
|
1174
|
+
* liability-vault shares left from the repay cushion when the account snapshot shows no pre-existing liability-vault deposit;
|
|
1175
|
+
* otherwise those shares are preserved in the liability vault instead of being migrated to the source vault.
|
|
1176
|
+
* Cleanup also disables active collaterals on the repaid sub-account, transfers those collateral shares to the owner
|
|
1177
|
+
* account, and transfers any remaining source-vault shares from the source sub-account to the owner account.
|
|
1715
1178
|
*
|
|
1716
1179
|
* @param args - Repay-from-deposit plan arguments
|
|
1717
1180
|
* @param args.liabilityVault - Address of the liability vault (debt is repaid to this vault)
|
|
@@ -1720,10 +1183,11 @@ export class ExecutionService {
|
|
|
1720
1183
|
* @param args.fromVault - Vault to withdraw assets from (must be same underlying asset as liability for this plan)
|
|
1721
1184
|
* @param args.fromAccount - Sub-account that holds the deposit in `fromVault`
|
|
1722
1185
|
* @param args.account - Account entity; used for chainId, owner, and positions (to resolve assets and eligibility)
|
|
1723
|
-
* @
|
|
1186
|
+
* @param args.cleanupOnMax - Whether max repay should append repay-cushion, collateral, and source-share cleanup (default false)
|
|
1187
|
+
* @returns Array of transaction plan items (EVC batch only). Throws if asset differs between fromVault and liabilityVault; use planRepayWithSwap for cross-asset.
|
|
1724
1188
|
*/
|
|
1725
1189
|
planRepayFromDeposit(args) {
|
|
1726
|
-
const { liabilityVault, liabilityAmount, receiver, fromVault, fromAccount, account, } = args;
|
|
1190
|
+
const { liabilityVault, liabilityAmount, receiver, fromVault, fromAccount, account, cleanupOnMax = false, } = args;
|
|
1727
1191
|
const plan = [];
|
|
1728
1192
|
// Get positions
|
|
1729
1193
|
const liabilityPosition = account?.getPosition(receiver, liabilityVault);
|
|
@@ -1735,7 +1199,7 @@ export class ExecutionService {
|
|
|
1735
1199
|
}
|
|
1736
1200
|
const liabilityAsset = liabilityPosition.asset;
|
|
1737
1201
|
const fromAsset = fromPosition.asset;
|
|
1738
|
-
//
|
|
1202
|
+
// Cross-asset repay requires a swap path; this planner only builds same-asset batches.
|
|
1739
1203
|
if (fromAsset !== liabilityAsset) {
|
|
1740
1204
|
// This path requires a swap, which is handled by planRepayWithSwap
|
|
1741
1205
|
throw new Error("planRepayFromDeposit only supports same-asset paths. Use planRepayWithSwap for different assets.");
|
|
@@ -1744,6 +1208,7 @@ export class ExecutionService {
|
|
|
1744
1208
|
const amount = isMax && fromVault !== liabilityVault
|
|
1745
1209
|
? liabilityPosition.borrowed
|
|
1746
1210
|
: liabilityAmount;
|
|
1211
|
+
const hasPreExistingLiabilityDeposit = (liabilityPosition.assets ?? 0n) > 0n;
|
|
1747
1212
|
// Build EVC batch items
|
|
1748
1213
|
const batchItems = this.encodeRepayFromDeposit({
|
|
1749
1214
|
chainId: account.chainId,
|
|
@@ -1758,10 +1223,41 @@ export class ExecutionService {
|
|
|
1758
1223
|
isMax,
|
|
1759
1224
|
// Permit2 is handled separately in the plan
|
|
1760
1225
|
});
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1226
|
+
if (cleanupOnMax && isMax) {
|
|
1227
|
+
if (fromVault !== liabilityVault && !hasPreExistingLiabilityDeposit) {
|
|
1228
|
+
// Sweep only the repay cushion. If liability shares existed before this batch,
|
|
1229
|
+
// redeem(max) would also migrate that unrelated deposit into the source vault.
|
|
1230
|
+
batchItems.push({
|
|
1231
|
+
targetContract: liabilityVault,
|
|
1232
|
+
onBehalfOfAccount: receiver,
|
|
1233
|
+
value: 0n,
|
|
1234
|
+
data: encodeFunctionData({
|
|
1235
|
+
abi: eVaultAbi,
|
|
1236
|
+
functionName: "redeem",
|
|
1237
|
+
args: [maxUint256, fromVault, receiver],
|
|
1238
|
+
}),
|
|
1239
|
+
});
|
|
1240
|
+
batchItems.push({
|
|
1241
|
+
targetContract: fromVault,
|
|
1242
|
+
onBehalfOfAccount: fromAccount,
|
|
1243
|
+
value: 0n,
|
|
1244
|
+
data: encodeFunctionData({
|
|
1245
|
+
abi: eVaultAbi,
|
|
1246
|
+
functionName: "skim",
|
|
1247
|
+
args: [maxUint256, fromAccount],
|
|
1248
|
+
}),
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
this.appendMaxRepayCleanup({
|
|
1252
|
+
account,
|
|
1253
|
+
liabilityPosition,
|
|
1254
|
+
receiver,
|
|
1255
|
+
batchItems,
|
|
1256
|
+
sourceAccount: fromAccount,
|
|
1257
|
+
sourceVault: fromVault,
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "repayFromDeposit"));
|
|
1765
1261
|
return plan;
|
|
1766
1262
|
}
|
|
1767
1263
|
/**
|
|
@@ -1771,10 +1267,11 @@ export class ExecutionService {
|
|
|
1771
1267
|
* @param args - Repay-with-swap plan arguments
|
|
1772
1268
|
* @param args.swapQuote - Quote from swap service (e.g. fetchRepayQuotes); defines vaultIn, accountIn, accountOut, receiver, swap and verify steps
|
|
1773
1269
|
* @param args.account - Account entity; used for chainId and positions (to compute isMax and maxWithdraw)
|
|
1270
|
+
* @param args.cleanupOnMax - When true and the quote repays the full debt, the batch disables active collaterals on the repaid sub-account, transfers their shares to the owner account, and transfers remaining source-vault shares to the owner account (default false)
|
|
1774
1271
|
* @returns Array of transaction plan items (EVC batch: withdraw, swap, verify/repay). Throws if positions not found or liability is zero.
|
|
1775
1272
|
*/
|
|
1776
1273
|
planRepayWithSwap(args) {
|
|
1777
|
-
const { swapQuote, account } = args;
|
|
1274
|
+
const { swapQuote, account, cleanupOnMax = false } = args;
|
|
1778
1275
|
const plan = [];
|
|
1779
1276
|
const liabilityPosition = account?.getPosition(swapQuote.accountOut, swapQuote.receiver);
|
|
1780
1277
|
const fromPosition = account?.getPosition(swapQuote.accountIn, swapQuote.vaultIn);
|
|
@@ -1793,10 +1290,17 @@ export class ExecutionService {
|
|
|
1793
1290
|
isMax,
|
|
1794
1291
|
disableControllerOnMax: true,
|
|
1795
1292
|
});
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1293
|
+
if (cleanupOnMax && isMax) {
|
|
1294
|
+
this.appendMaxRepayCleanup({
|
|
1295
|
+
account,
|
|
1296
|
+
liabilityPosition,
|
|
1297
|
+
receiver: swapQuote.accountOut,
|
|
1298
|
+
batchItems,
|
|
1299
|
+
sourceAccount: swapQuote.accountIn,
|
|
1300
|
+
sourceVault: swapQuote.vaultIn,
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "repayWithSwap"));
|
|
1800
1304
|
return plan;
|
|
1801
1305
|
}
|
|
1802
1306
|
/**
|
|
@@ -1838,10 +1342,7 @@ export class ExecutionService {
|
|
|
1838
1342
|
sender: account.owner,
|
|
1839
1343
|
enableCollateral: shouldEnableCollateral,
|
|
1840
1344
|
});
|
|
1841
|
-
plan.push(
|
|
1842
|
-
type: "evcBatch",
|
|
1843
|
-
items: batchItems,
|
|
1844
|
-
});
|
|
1345
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "depositWithSwapFromWallet"));
|
|
1845
1346
|
return plan;
|
|
1846
1347
|
}
|
|
1847
1348
|
/**
|
|
@@ -1871,10 +1372,7 @@ export class ExecutionService {
|
|
|
1871
1372
|
amount,
|
|
1872
1373
|
sender: account.owner,
|
|
1873
1374
|
});
|
|
1874
|
-
plan.push(
|
|
1875
|
-
type: "evcBatch",
|
|
1876
|
-
items: batchItems,
|
|
1877
|
-
});
|
|
1375
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "swapFromWallet"));
|
|
1878
1376
|
return plan;
|
|
1879
1377
|
}
|
|
1880
1378
|
/**
|
|
@@ -1906,10 +1404,7 @@ export class ExecutionService {
|
|
|
1906
1404
|
disableCollateralOnMax: true,
|
|
1907
1405
|
isMax,
|
|
1908
1406
|
});
|
|
1909
|
-
plan.push(
|
|
1910
|
-
type: "evcBatch",
|
|
1911
|
-
items: batchItems,
|
|
1912
|
-
});
|
|
1407
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "swapCollateral"));
|
|
1913
1408
|
return plan;
|
|
1914
1409
|
}
|
|
1915
1410
|
/**
|
|
@@ -1937,10 +1432,103 @@ export class ExecutionService {
|
|
|
1937
1432
|
disableControllerOnMax: true,
|
|
1938
1433
|
isMax,
|
|
1939
1434
|
});
|
|
1940
|
-
plan.push(
|
|
1941
|
-
|
|
1942
|
-
|
|
1435
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "swapDebt"));
|
|
1436
|
+
return plan;
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* Builds a transaction plan for migrating a supplied/collateral position between two same-asset vaults.
|
|
1440
|
+
* This is the no-swap path for moving shares from one vault to another vault with the same underlying asset.
|
|
1441
|
+
*
|
|
1442
|
+
* @param args - Same-asset collateral migration plan arguments
|
|
1443
|
+
* @param args.fromVault - Source vault holding the supplied shares
|
|
1444
|
+
* @param args.toVault - Destination vault with the same underlying asset
|
|
1445
|
+
* @param args.amount - Asset amount to withdraw/redeem and skim into the destination vault
|
|
1446
|
+
* @param args.positionAccount - Sub-account that owns the source shares and receives destination shares
|
|
1447
|
+
* @param args.fromAsset - Optional source underlying asset; defaults to the account position asset
|
|
1448
|
+
* @param args.toAsset - Destination underlying asset, used to verify this is a same-asset migration
|
|
1449
|
+
* @param args.isMax - If true, redeems shares instead of withdrawing assets
|
|
1450
|
+
* @param args.maxShares - Optional exact share amount for max migration
|
|
1451
|
+
* @param args.enableCollateralTo - Optional override for enabling destination collateral
|
|
1452
|
+
* @param args.disableCollateralFrom - Optional override for disabling source collateral
|
|
1453
|
+
* @returns Array of transaction plan items (EVC batch; no token approvals)
|
|
1454
|
+
*/
|
|
1455
|
+
planMigrateSameAssetCollateral(args) {
|
|
1456
|
+
const { account, fromVault, toVault, amount, positionAccount, fromAsset, toAsset, isMax = false, maxShares, enableCollateralTo, disableCollateralFrom, } = args;
|
|
1457
|
+
const plan = [];
|
|
1458
|
+
const sourcePosition = account?.getPosition(positionAccount, fromVault);
|
|
1459
|
+
const resolvedFromAsset = fromAsset ?? sourcePosition?.asset;
|
|
1460
|
+
if (!resolvedFromAsset) {
|
|
1461
|
+
throw new Error(`Position not found. From vault: ${fromVault}, Account: ${positionAccount}. Source asset is required when position is not available.`);
|
|
1462
|
+
}
|
|
1463
|
+
if (getAddress(resolvedFromAsset) !== getAddress(toAsset)) {
|
|
1464
|
+
throw new Error("planMigrateSameAssetCollateral only supports same-asset vault migrations. Use planSwapCollateral for cross-asset migrations.");
|
|
1465
|
+
}
|
|
1466
|
+
const shouldEnableCollateralTo = enableCollateralTo ??
|
|
1467
|
+
!account.isCollateralEnabled(positionAccount, toVault);
|
|
1468
|
+
const shouldDisableCollateralFrom = disableCollateralFrom ??
|
|
1469
|
+
(isMax && account.isCollateralEnabled(positionAccount, fromVault));
|
|
1470
|
+
const batchItems = this.encodeMigrateSameAssetCollateral({
|
|
1471
|
+
chainId: account.chainId,
|
|
1472
|
+
fromVault,
|
|
1473
|
+
toVault,
|
|
1474
|
+
amount,
|
|
1475
|
+
account: positionAccount,
|
|
1476
|
+
isMax,
|
|
1477
|
+
maxShares: maxShares ?? (isMax ? sourcePosition?.shares : undefined),
|
|
1478
|
+
enableCollateralTo: shouldEnableCollateralTo,
|
|
1479
|
+
disableCollateralFrom: shouldDisableCollateralFrom,
|
|
1480
|
+
});
|
|
1481
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "migrateSameAssetCollateral"));
|
|
1482
|
+
return plan;
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Builds a transaction plan for migrating a full same-asset debt position from one liability vault to another.
|
|
1486
|
+
* This is the no-swap path for debt vault migration and repays the old vault with shares after borrowing
|
|
1487
|
+
* slightly more from the new vault to cover interest accrual.
|
|
1488
|
+
*
|
|
1489
|
+
* @param args - Same-asset debt migration plan arguments
|
|
1490
|
+
* @param args.oldLiabilityVault - Existing debt vault to fully repay and disable
|
|
1491
|
+
* @param args.newLiabilityVault - New same-asset debt vault to borrow from
|
|
1492
|
+
* @param args.liabilityAccount - Sub-account that owns the debt position
|
|
1493
|
+
* @param args.liabilityAmount - Current debt amount; defaults to the old-vault borrowed amount in account data
|
|
1494
|
+
* @param args.oldLiabilityAsset - Optional old liability asset; defaults to the old-vault account position asset
|
|
1495
|
+
* @param args.newLiabilityAsset - New liability underlying asset, used to verify this is a same-asset migration
|
|
1496
|
+
* @param args.sweepExcess - Whether to redeem and skim the migration cushion back into the new vault (default true)
|
|
1497
|
+
* @param args.transferRemainingSharesToOwner - Whether to transfer new-vault shares to the owner when liabilityAccount differs from owner (default true)
|
|
1498
|
+
* @returns Array of transaction plan items (EVC batch; no token approvals)
|
|
1499
|
+
*/
|
|
1500
|
+
planMigrateSameAssetDebt(args) {
|
|
1501
|
+
const { account, oldLiabilityVault, newLiabilityVault, liabilityAccount, liabilityAmount, oldLiabilityAsset, newLiabilityAsset, sweepExcess = true, transferRemainingSharesToOwner = true, } = args;
|
|
1502
|
+
const plan = [];
|
|
1503
|
+
const oldPosition = account?.getPosition(liabilityAccount, oldLiabilityVault);
|
|
1504
|
+
const resolvedOldAsset = oldLiabilityAsset ?? oldPosition?.asset;
|
|
1505
|
+
if (!resolvedOldAsset) {
|
|
1506
|
+
throw new Error(`Position not found. Old liability vault: ${oldLiabilityVault}, Account: ${liabilityAccount}. Old liability asset is required when position is not available.`);
|
|
1507
|
+
}
|
|
1508
|
+
if (getAddress(resolvedOldAsset) !== getAddress(newLiabilityAsset)) {
|
|
1509
|
+
throw new Error("planMigrateSameAssetDebt only supports same-asset debt migrations. Use planSwapDebt for cross-asset migrations.");
|
|
1510
|
+
}
|
|
1511
|
+
const amount = liabilityAmount ?? oldPosition?.borrowed;
|
|
1512
|
+
if (!amount || amount <= 0n) {
|
|
1513
|
+
throw new Error(`Debt position not found or liability is 0. Old liability vault: ${oldLiabilityVault}, Account: ${liabilityAccount}`);
|
|
1514
|
+
}
|
|
1515
|
+
const enableController = !account.isControllerEnabled(liabilityAccount, newLiabilityVault);
|
|
1516
|
+
const transferRemainingSharesTo = transferRemainingSharesToOwner &&
|
|
1517
|
+
getAddress(liabilityAccount) !== getAddress(account.owner)
|
|
1518
|
+
? account.owner
|
|
1519
|
+
: undefined;
|
|
1520
|
+
const batchItems = this.encodeMigrateSameAssetDebt({
|
|
1521
|
+
chainId: account.chainId,
|
|
1522
|
+
oldLiabilityVault,
|
|
1523
|
+
newLiabilityVault,
|
|
1524
|
+
amount,
|
|
1525
|
+
account: liabilityAccount,
|
|
1526
|
+
enableController,
|
|
1527
|
+
disableController: true,
|
|
1528
|
+
sweepExcess,
|
|
1529
|
+
transferRemainingSharesTo,
|
|
1943
1530
|
});
|
|
1531
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "migrateSameAssetDebt"));
|
|
1944
1532
|
return plan;
|
|
1945
1533
|
}
|
|
1946
1534
|
/**
|
|
@@ -1971,10 +1559,7 @@ export class ExecutionService {
|
|
|
1971
1559
|
disableCollateralFrom: disableCollateralFrom &&
|
|
1972
1560
|
(account?.isCollateralEnabled(from, vault) ?? false),
|
|
1973
1561
|
});
|
|
1974
|
-
plan.push(
|
|
1975
|
-
type: "evcBatch",
|
|
1976
|
-
items: batchItems,
|
|
1977
|
-
});
|
|
1562
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "transfer"));
|
|
1978
1563
|
return plan;
|
|
1979
1564
|
}
|
|
1980
1565
|
/**
|
|
@@ -2001,10 +1586,7 @@ export class ExecutionService {
|
|
|
2001
1586
|
to,
|
|
2002
1587
|
enableController,
|
|
2003
1588
|
});
|
|
2004
|
-
plan.push(
|
|
2005
|
-
type: "evcBatch",
|
|
2006
|
-
items: batchItems,
|
|
2007
|
-
});
|
|
1589
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "pullDebt"));
|
|
2008
1590
|
return plan;
|
|
2009
1591
|
}
|
|
2010
1592
|
/**
|
|
@@ -2020,10 +1602,10 @@ export class ExecutionService {
|
|
|
2020
1602
|
* @returns Array of transaction plan items (optional approval + EVC batch). Throws if swapQuote.accountIn !== swapQuote.accountOut.
|
|
2021
1603
|
*/
|
|
2022
1604
|
planMultiplyWithSwap(args) {
|
|
2023
|
-
const { collateralVault, collateralAmount, collateralAsset, account, swapQuote, } = args;
|
|
1605
|
+
const { collateralVault, collateralAmount, collateralAsset, collateralShareSource, account, swapQuote, } = args;
|
|
2024
1606
|
const plan = [];
|
|
2025
1607
|
// 1. Check if collateral approval is needed (only if depositing collateral)
|
|
2026
|
-
if (collateralAmount > 0n) {
|
|
1608
|
+
if (!collateralShareSource && collateralAmount > 0n) {
|
|
2027
1609
|
// Add approval requirement (will be resolved later with Wallet data)
|
|
2028
1610
|
plan.push({
|
|
2029
1611
|
type: "requiredApproval",
|
|
@@ -2041,8 +1623,19 @@ export class ExecutionService {
|
|
|
2041
1623
|
const longVault = swapQuote.receiver;
|
|
2042
1624
|
const liabilityAmount = BigInt(swapQuote.amountIn);
|
|
2043
1625
|
// 2. Determine if collateral needs to be enabled
|
|
2044
|
-
const
|
|
1626
|
+
const hasCollateralInput = collateralShareSource
|
|
1627
|
+
? collateralShareSource.shares > 0n
|
|
1628
|
+
: collateralAmount > 0n;
|
|
1629
|
+
const enableCollateral = hasCollateralInput &&
|
|
2045
1630
|
!(account?.isCollateralEnabled(receiver, collateralVault) ?? false);
|
|
1631
|
+
const resolvedCollateralShareSource = collateralShareSource
|
|
1632
|
+
? {
|
|
1633
|
+
...collateralShareSource,
|
|
1634
|
+
disableCollateralFrom: collateralShareSource.disableCollateralFrom &&
|
|
1635
|
+
(account?.isCollateralEnabled(collateralShareSource.from, collateralVault) ??
|
|
1636
|
+
false),
|
|
1637
|
+
}
|
|
1638
|
+
: undefined;
|
|
2046
1639
|
// 3. Determine if controller needs to be enabled
|
|
2047
1640
|
const enableController = !(account?.isControllerEnabled(receiver, liabilityVault) ?? false);
|
|
2048
1641
|
// 4. Get current controller (may need to disable if different)
|
|
@@ -2060,13 +1653,11 @@ export class ExecutionService {
|
|
|
2060
1653
|
enableCollateral,
|
|
2061
1654
|
currentController: currentController || undefined,
|
|
2062
1655
|
enableController,
|
|
1656
|
+
collateralShareSource: resolvedCollateralShareSource,
|
|
2063
1657
|
swapQuote,
|
|
2064
1658
|
// Permit2 is handled separately in the plan
|
|
2065
1659
|
});
|
|
2066
|
-
plan.push(
|
|
2067
|
-
type: "evcBatch",
|
|
2068
|
-
items: batchItems,
|
|
2069
|
-
});
|
|
1660
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "multiplyWithSwap"));
|
|
2070
1661
|
return plan;
|
|
2071
1662
|
}
|
|
2072
1663
|
/**
|
|
@@ -2085,10 +1676,10 @@ export class ExecutionService {
|
|
|
2085
1676
|
* @returns Array of transaction plan items (optional approval + EVC batch)
|
|
2086
1677
|
*/
|
|
2087
1678
|
planMultiplySameAsset(args) {
|
|
2088
|
-
const { collateralVault, collateralAmount, collateralAsset, liabilityVault, liabilityAmount, longVault, receiver, account, } = args;
|
|
1679
|
+
const { collateralVault, collateralAmount, collateralAsset, collateralShareSource, liabilityVault, liabilityAmount, longVault, receiver, account, } = args;
|
|
2089
1680
|
const plan = [];
|
|
2090
1681
|
// 1. Check if collateral approval is needed (only if depositing collateral)
|
|
2091
|
-
if (collateralAmount > 0n) {
|
|
1682
|
+
if (!collateralShareSource && collateralAmount > 0n) {
|
|
2092
1683
|
// Add approval requirement (will be resolved later with Wallet data)
|
|
2093
1684
|
plan.push({
|
|
2094
1685
|
type: "requiredApproval",
|
|
@@ -2099,8 +1690,19 @@ export class ExecutionService {
|
|
|
2099
1690
|
});
|
|
2100
1691
|
}
|
|
2101
1692
|
// 2. Determine if collateral needs to be enabled
|
|
2102
|
-
const
|
|
1693
|
+
const hasCollateralInput = collateralShareSource
|
|
1694
|
+
? collateralShareSource.shares > 0n
|
|
1695
|
+
: collateralAmount > 0n;
|
|
1696
|
+
const enableCollateral = hasCollateralInput &&
|
|
2103
1697
|
!(account?.isCollateralEnabled(receiver, collateralVault) ?? false);
|
|
1698
|
+
const resolvedCollateralShareSource = collateralShareSource
|
|
1699
|
+
? {
|
|
1700
|
+
...collateralShareSource,
|
|
1701
|
+
disableCollateralFrom: collateralShareSource.disableCollateralFrom &&
|
|
1702
|
+
(account?.isCollateralEnabled(collateralShareSource.from, collateralVault) ??
|
|
1703
|
+
false),
|
|
1704
|
+
}
|
|
1705
|
+
: undefined;
|
|
2104
1706
|
// 3. Determine if controller needs to be enabled
|
|
2105
1707
|
const enableController = !(account?.isControllerEnabled(receiver, liabilityVault) ?? false);
|
|
2106
1708
|
// 4. Get current controller (may need to disable if different)
|
|
@@ -2118,12 +1720,10 @@ export class ExecutionService {
|
|
|
2118
1720
|
enableCollateral,
|
|
2119
1721
|
currentController: currentController || undefined,
|
|
2120
1722
|
enableController,
|
|
1723
|
+
collateralShareSource: resolvedCollateralShareSource,
|
|
2121
1724
|
// Permit2 is handled separately in the plan
|
|
2122
1725
|
});
|
|
2123
|
-
plan.push(
|
|
2124
|
-
type: "evcBatch",
|
|
2125
|
-
items: batchItems,
|
|
2126
|
-
});
|
|
1726
|
+
plan.push(...this.convertBatchItemsToPlan(batchItems, "multiplySameAsset"));
|
|
2127
1727
|
return plan;
|
|
2128
1728
|
}
|
|
2129
1729
|
}
|