@eulerxyz/euler-v2-sdk 1.1.1 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/dist/src/index.d.ts +1 -0
  2. package/dist/src/index.d.ts.map +1 -1
  3. package/dist/src/index.js +1 -0
  4. package/dist/src/index.js.map +1 -1
  5. package/dist/src/sdk/buildSDK.d.ts +8 -0
  6. package/dist/src/sdk/buildSDK.d.ts.map +1 -1
  7. package/dist/src/sdk/buildSDK.js +20 -1
  8. package/dist/src/sdk/buildSDK.js.map +1 -1
  9. package/dist/src/sdk/sdk.d.ts +3 -0
  10. package/dist/src/sdk/sdk.d.ts.map +1 -1
  11. package/dist/src/sdk/sdk.js +2 -0
  12. package/dist/src/sdk/sdk.js.map +1 -1
  13. package/dist/src/services/executionService/abis/swapVerifierAbi.d.ts +130 -0
  14. package/dist/src/services/executionService/abis/swapVerifierAbi.d.ts.map +1 -1
  15. package/dist/src/services/executionService/abis/swapVerifierAbi.js +69 -0
  16. package/dist/src/services/executionService/abis/swapVerifierAbi.js.map +1 -1
  17. package/dist/src/services/executionService/executionService.d.ts +2 -1
  18. package/dist/src/services/executionService/executionService.d.ts.map +1 -1
  19. package/dist/src/services/executionService/executionService.js.map +1 -1
  20. package/dist/src/services/executionService/simulate.d.ts +2 -0
  21. package/dist/src/services/executionService/simulate.d.ts.map +1 -1
  22. package/dist/src/services/executionService/simulate.js +9 -1
  23. package/dist/src/services/executionService/simulate.js.map +1 -1
  24. package/dist/src/services/positionMigrationService/connectors/aave/aaveConnector.d.ts +81 -0
  25. package/dist/src/services/positionMigrationService/connectors/aave/aaveConnector.d.ts.map +1 -0
  26. package/dist/src/services/positionMigrationService/connectors/aave/aaveConnector.js +1291 -0
  27. package/dist/src/services/positionMigrationService/connectors/aave/aaveConnector.js.map +1 -0
  28. package/dist/src/services/positionMigrationService/connectors/aave/aaveConnectorTypes.d.ts +84 -0
  29. package/dist/src/services/positionMigrationService/connectors/aave/aaveConnectorTypes.d.ts.map +1 -0
  30. package/dist/src/services/positionMigrationService/connectors/aave/aaveConnectorTypes.js +2 -0
  31. package/dist/src/services/positionMigrationService/connectors/aave/aaveConnectorTypes.js.map +1 -0
  32. package/dist/src/services/positionMigrationService/connectors/aave/abis/aaveV3Abi.d.ts +370 -0
  33. package/dist/src/services/positionMigrationService/connectors/aave/abis/aaveV3Abi.d.ts.map +1 -0
  34. package/dist/src/services/positionMigrationService/connectors/aave/abis/aaveV3Abi.js +243 -0
  35. package/dist/src/services/positionMigrationService/connectors/aave/abis/aaveV3Abi.js.map +1 -0
  36. package/dist/src/services/positionMigrationService/connectors/aave/index.d.ts +4 -0
  37. package/dist/src/services/positionMigrationService/connectors/aave/index.d.ts.map +1 -0
  38. package/dist/src/services/positionMigrationService/connectors/aave/index.js +3 -0
  39. package/dist/src/services/positionMigrationService/connectors/aave/index.js.map +1 -0
  40. package/dist/src/services/positionMigrationService/connectors/index.d.ts +4 -0
  41. package/dist/src/services/positionMigrationService/connectors/index.d.ts.map +1 -0
  42. package/dist/src/services/positionMigrationService/connectors/index.js +4 -0
  43. package/dist/src/services/positionMigrationService/connectors/index.js.map +1 -0
  44. package/dist/src/services/positionMigrationService/connectors/metamorpho/abis/metamorphoAbi.d.ts +166 -0
  45. package/dist/src/services/positionMigrationService/connectors/metamorpho/abis/metamorphoAbi.d.ts.map +1 -0
  46. package/dist/src/services/positionMigrationService/connectors/metamorpho/abis/metamorphoAbi.js +108 -0
  47. package/dist/src/services/positionMigrationService/connectors/metamorpho/abis/metamorphoAbi.js.map +1 -0
  48. package/dist/src/services/positionMigrationService/connectors/metamorpho/index.d.ts +4 -0
  49. package/dist/src/services/positionMigrationService/connectors/metamorpho/index.d.ts.map +1 -0
  50. package/dist/src/services/positionMigrationService/connectors/metamorpho/index.js +3 -0
  51. package/dist/src/services/positionMigrationService/connectors/metamorpho/index.js.map +1 -0
  52. package/dist/src/services/positionMigrationService/connectors/metamorpho/metamorphoConnector.d.ts +56 -0
  53. package/dist/src/services/positionMigrationService/connectors/metamorpho/metamorphoConnector.d.ts.map +1 -0
  54. package/dist/src/services/positionMigrationService/connectors/metamorpho/metamorphoConnector.js +426 -0
  55. package/dist/src/services/positionMigrationService/connectors/metamorpho/metamorphoConnector.js.map +1 -0
  56. package/dist/src/services/positionMigrationService/connectors/metamorpho/metamorphoConnectorTypes.d.ts +46 -0
  57. package/dist/src/services/positionMigrationService/connectors/metamorpho/metamorphoConnectorTypes.d.ts.map +1 -0
  58. package/dist/src/services/positionMigrationService/connectors/metamorpho/metamorphoConnectorTypes.js +2 -0
  59. package/dist/src/services/positionMigrationService/connectors/metamorpho/metamorphoConnectorTypes.js.map +1 -0
  60. package/dist/src/services/positionMigrationService/connectors/morpho/abis/morphoBlueAbi.d.ts +269 -0
  61. package/dist/src/services/positionMigrationService/connectors/morpho/abis/morphoBlueAbi.d.ts.map +1 -0
  62. package/dist/src/services/positionMigrationService/connectors/morpho/abis/morphoBlueAbi.js +172 -0
  63. package/dist/src/services/positionMigrationService/connectors/morpho/abis/morphoBlueAbi.js.map +1 -0
  64. package/dist/src/services/positionMigrationService/connectors/morpho/index.d.ts +4 -0
  65. package/dist/src/services/positionMigrationService/connectors/morpho/index.d.ts.map +1 -0
  66. package/dist/src/services/positionMigrationService/connectors/morpho/index.js +3 -0
  67. package/dist/src/services/positionMigrationService/connectors/morpho/index.js.map +1 -0
  68. package/dist/src/services/positionMigrationService/connectors/morpho/morphoConnector.d.ts +57 -0
  69. package/dist/src/services/positionMigrationService/connectors/morpho/morphoConnector.d.ts.map +1 -0
  70. package/dist/src/services/positionMigrationService/connectors/morpho/morphoConnector.js +856 -0
  71. package/dist/src/services/positionMigrationService/connectors/morpho/morphoConnector.js.map +1 -0
  72. package/dist/src/services/positionMigrationService/connectors/morpho/morphoConnectorTypes.d.ts +61 -0
  73. package/dist/src/services/positionMigrationService/connectors/morpho/morphoConnectorTypes.d.ts.map +1 -0
  74. package/dist/src/services/positionMigrationService/connectors/morpho/morphoConnectorTypes.js +2 -0
  75. package/dist/src/services/positionMigrationService/connectors/morpho/morphoConnectorTypes.js.map +1 -0
  76. package/dist/src/services/positionMigrationService/connectors/shared.d.ts +83 -0
  77. package/dist/src/services/positionMigrationService/connectors/shared.d.ts.map +1 -0
  78. package/dist/src/services/positionMigrationService/connectors/shared.js +214 -0
  79. package/dist/src/services/positionMigrationService/connectors/shared.js.map +1 -0
  80. package/dist/src/services/positionMigrationService/index.d.ts +4 -0
  81. package/dist/src/services/positionMigrationService/index.d.ts.map +1 -0
  82. package/dist/src/services/positionMigrationService/index.js +3 -0
  83. package/dist/src/services/positionMigrationService/index.js.map +1 -0
  84. package/dist/src/services/positionMigrationService/positionMigrationService.d.ts +30 -0
  85. package/dist/src/services/positionMigrationService/positionMigrationService.d.ts.map +1 -0
  86. package/dist/src/services/positionMigrationService/positionMigrationService.js +420 -0
  87. package/dist/src/services/positionMigrationService/positionMigrationService.js.map +1 -0
  88. package/dist/src/services/positionMigrationService/positionMigrationServiceTypes.d.ts +235 -0
  89. package/dist/src/services/positionMigrationService/positionMigrationServiceTypes.d.ts.map +1 -0
  90. package/dist/src/services/positionMigrationService/positionMigrationServiceTypes.js +2 -0
  91. package/dist/src/services/positionMigrationService/positionMigrationServiceTypes.js.map +1 -0
  92. package/dist/src/services/swapService/swapVerification.d.ts +1 -1
  93. package/dist/src/services/swapService/swapVerification.d.ts.map +1 -1
  94. package/package.json +1 -1
@@ -0,0 +1,856 @@
1
+ import { encodeAbiParameters, encodeFunctionData, getAddress, keccak256, maxUint256, } from "viem";
2
+ import { eVaultAbi, swapperAbi, swapVerifierAbi, } from "../../../executionService/index.js";
3
+ import { applyBuffer, assertEulerSource, assertEulerTarget, assertSameAddress, assertSwapQuoteUsesSwapper, encodeGenericSwap, encodeSwapQuoteVerificationItem, encodeVerifyAmountMinAndDepositItem, encodeVerifyDebtMaxItem, getSwapperAddress, getSwapQuoteInputAmount, getSwapQuoteSwapCalls, getSwapVerifierAddress, resolveEulerSourceAmounts, splitPermitSignature, } from "../shared.js";
4
+ import { morphoBlueAbi } from "./abis/morphoBlueAbi.js";
5
+ export const MORPHO_CONNECTOR_ID = "morpho";
6
+ export const MORPHO_PROTOCOL = "Morpho";
7
+ const DEFAULT_MORPHO_ADDRESSES = {
8
+ 1: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
9
+ 10: "0xce95AfbB8EA029495c66020883F87aaE8864AF92",
10
+ 56: "0x01b0Bd309AA75547f7a37Ad7B1219A898E67a83a",
11
+ 130: "0x8f5ae9CddB9f68de460C77730b018Ae7E04a140A",
12
+ 143: "0xD5D960E8C380B724a48AC59E2DfF1b2CB4a1eAee",
13
+ 146: "0xd6c916eB7542D0Ad3f18AEd0FCBD50C582cfa95f",
14
+ 239: "0x918B9F2E4B44E20c6423105BB6cCEB71473aD35c",
15
+ 480: "0xE741BC7c34758b4caE05062794E8Ae24978AF432",
16
+ 999: "0x68e37dE8d93d3496ae143F2E900490f6280C57cD",
17
+ 8453: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb",
18
+ 42161: "0x6c247b1F6182318877311737BaC0844bAa518F5e",
19
+ 57073: "0x857f3EefE8cbda3Bc49367C996cd664A880d3042",
20
+ };
21
+ const DEFAULT_MORPHO_GRAPHQL_URL = "https://api.morpho.org/graphql";
22
+ const DEFAULT_INTEREST_BUFFER_BPS = 100n;
23
+ const DEFAULT_MIN_LIQUIDITY = 0n;
24
+ const DEFAULT_OUTBOUND_INTEREST_BUFFER_BPS = 1n;
25
+ const DEFAULT_MORPHO_GRAPHQL_TIMEOUT_MS = 10_000;
26
+ const ZERO_BYTES = "0x";
27
+ const MORPHO_MARKET_PARAMS_ABI = [
28
+ {
29
+ type: "tuple",
30
+ components: [
31
+ { name: "loanToken", type: "address" },
32
+ { name: "collateralToken", type: "address" },
33
+ { name: "oracle", type: "address" },
34
+ { name: "irm", type: "address" },
35
+ { name: "lltv", type: "uint256" },
36
+ ],
37
+ },
38
+ ];
39
+ const MORPHO_MARKETS_QUERY = `#graphql
40
+ query EulerMigrationMorphoMarkets($chainIds: [Int!], $loanAssets: [String!], $collateralAssets: [String!]) {
41
+ markets(
42
+ first: 100
43
+ where: {
44
+ chainId_in: $chainIds
45
+ loanAssetAddress_in: $loanAssets
46
+ collateralAssetAddress_in: $collateralAssets
47
+ listed: true
48
+ }
49
+ ) {
50
+ items {
51
+ marketId
52
+ listed
53
+ lltv
54
+ irmAddress
55
+ oracle { address }
56
+ loanAsset { address symbol decimals }
57
+ collateralAsset { address symbol decimals }
58
+ state {
59
+ borrowApy
60
+ netBorrowApy
61
+ supplyApy
62
+ liquidityAssets
63
+ liquidityAssetsUsd
64
+ }
65
+ }
66
+ }
67
+ }
68
+ `;
69
+ export const MORPHO_AUTHORIZATION_TYPES = {
70
+ Authorization: [
71
+ { name: "authorizer", type: "address" },
72
+ { name: "authorized", type: "address" },
73
+ { name: "isAuthorized", type: "bool" },
74
+ { name: "nonce", type: "uint256" },
75
+ { name: "deadline", type: "uint256" },
76
+ ],
77
+ };
78
+ export function getMorphoMarketId(marketParams) {
79
+ return keccak256(encodeAbiParameters(MORPHO_MARKET_PARAMS_ABI, [
80
+ normalizeMarketParams(marketParams),
81
+ ]));
82
+ }
83
+ export function splitMorphoAuthorizationSignature(signature) {
84
+ if (signature.length !== 132) {
85
+ throw new Error("Morpho authorization signature must be 65 bytes");
86
+ }
87
+ return splitPermitSignature(signature);
88
+ }
89
+ export class MorphoPositionMigrationConnector {
90
+ deploymentService;
91
+ providerService;
92
+ executionService;
93
+ id = MORPHO_CONNECTOR_ID;
94
+ protocol = MORPHO_PROTOCOL;
95
+ name = "Morpho";
96
+ morphoAddresses;
97
+ defaultInterestBufferBps;
98
+ morphoGraphqlUrl;
99
+ morphoGraphqlTimeoutMs;
100
+ fetchFn;
101
+ defaultMinLiquidity;
102
+ constructor(deploymentService, providerService, executionService, config = {}) {
103
+ this.deploymentService = deploymentService;
104
+ this.providerService = providerService;
105
+ this.executionService = executionService;
106
+ this.morphoAddresses = {
107
+ ...DEFAULT_MORPHO_ADDRESSES,
108
+ ...(config.morphoAddresses ?? {}),
109
+ };
110
+ this.defaultInterestBufferBps =
111
+ config.defaultInterestBufferBps ?? DEFAULT_INTEREST_BUFFER_BPS;
112
+ this.morphoGraphqlUrl =
113
+ config.morphoGraphqlUrl ?? DEFAULT_MORPHO_GRAPHQL_URL;
114
+ this.morphoGraphqlTimeoutMs =
115
+ config.morphoGraphqlTimeoutMs ?? DEFAULT_MORPHO_GRAPHQL_TIMEOUT_MS;
116
+ const configuredFetch = config.fetchFn ?? globalThis.fetch;
117
+ this.fetchFn = ((input, init) => configuredFetch.call(globalThis, input, init));
118
+ this.defaultMinLiquidity =
119
+ config.defaultMinLiquidity ?? DEFAULT_MIN_LIQUIDITY;
120
+ }
121
+ listPositions(args) {
122
+ return Promise.all((args.positionRefs ?? []).map((positionRef) => this.getPosition({
123
+ connectorId: MORPHO_CONNECTOR_ID,
124
+ chainId: args.chainId,
125
+ owner: args.owner,
126
+ positionRef,
127
+ }))).then((positions) => positions.filter((position) => position.debt.amount > 0n || position.collateral.amount > 0n));
128
+ }
129
+ async listTargets(args) {
130
+ if ((args.direction ?? "euler-to-external") !== "euler-to-external") {
131
+ return [];
132
+ }
133
+ const debtAsset = getAddress(args.debtAsset);
134
+ const collateralAsset = getAddress(args.collateralAsset);
135
+ const controller = new AbortController();
136
+ const timeout = setTimeout(() => controller.abort(), this.morphoGraphqlTimeoutMs);
137
+ let res;
138
+ try {
139
+ res = await this.fetchFn(this.morphoGraphqlUrl, {
140
+ method: "POST",
141
+ headers: {
142
+ accept: "application/json",
143
+ "content-type": "application/json",
144
+ },
145
+ body: JSON.stringify({
146
+ query: MORPHO_MARKETS_QUERY,
147
+ variables: {
148
+ chainIds: [args.chainId],
149
+ loanAssets: [debtAsset],
150
+ collateralAssets: [collateralAsset],
151
+ },
152
+ }),
153
+ signal: controller.signal,
154
+ });
155
+ }
156
+ finally {
157
+ clearTimeout(timeout);
158
+ }
159
+ if (!res.ok) {
160
+ throw new Error(`Morpho markets request failed: ${res.status}`);
161
+ }
162
+ const body = (await res.json());
163
+ if (body.errors?.length) {
164
+ throw new Error(body.errors[0]?.message ?? "Morpho markets request failed");
165
+ }
166
+ const minLiquidity = args.minLiquidity ?? this.defaultMinLiquidity;
167
+ return (body.data?.markets?.items ?? [])
168
+ .map((market) => toMorphoMigrationTarget(args.chainId, market))
169
+ .filter((target) => !!target &&
170
+ target.raw?.listed !== false &&
171
+ (target.liquidity?.amount ?? 0n) >= minLiquidity &&
172
+ (target.raw?.lltv ?? 0) > 0)
173
+ .sort((a, b) => {
174
+ const aLiquidity = a.liquidity?.amount ?? 0n;
175
+ const bLiquidity = b.liquidity?.amount ?? 0n;
176
+ if (aLiquidity === bLiquidity)
177
+ return 0;
178
+ return aLiquidity > bLiquidity ? -1 : 1;
179
+ });
180
+ }
181
+ async getPosition(args) {
182
+ const marketParams = normalizeMarketParams(args.positionRef);
183
+ const marketId = getMorphoMarketId(marketParams);
184
+ const owner = getAddress(args.owner);
185
+ const morpho = this.getMorphoAddress(args.chainId);
186
+ const provider = this.providerService.getProvider(args.chainId);
187
+ const [positionResult, marketResult] = (await provider.multicall({
188
+ contracts: [
189
+ {
190
+ address: morpho,
191
+ abi: morphoBlueAbi,
192
+ functionName: "position",
193
+ args: [marketId, owner],
194
+ },
195
+ {
196
+ address: morpho,
197
+ abi: morphoBlueAbi,
198
+ functionName: "market",
199
+ args: [marketId],
200
+ },
201
+ ],
202
+ allowFailure: false,
203
+ }));
204
+ const market = toMorphoMarketState(marketResult);
205
+ const raw = {
206
+ marketId,
207
+ owner,
208
+ supplyShares: positionResult[0],
209
+ borrowShares: positionResult[1],
210
+ collateral: positionResult[2],
211
+ borrowAssets: toAssetsUp(positionResult[1], market.totalBorrowAssets, market.totalBorrowShares),
212
+ market,
213
+ marketParams,
214
+ };
215
+ return {
216
+ connectorId: MORPHO_CONNECTOR_ID,
217
+ protocol: MORPHO_PROTOCOL,
218
+ id: marketId,
219
+ chainId: args.chainId,
220
+ owner,
221
+ ref: marketParams,
222
+ debt: {
223
+ asset: marketParams.loanToken,
224
+ amount: raw.borrowAssets,
225
+ shares: raw.borrowShares,
226
+ },
227
+ collateral: {
228
+ asset: marketParams.collateralToken,
229
+ amount: raw.collateral,
230
+ },
231
+ raw,
232
+ };
233
+ }
234
+ async getAuthorization(args) {
235
+ if (args.direction !== "external-to-euler" &&
236
+ args.direction !== "euler-to-external") {
237
+ throw new Error(`Unsupported migration direction: ${args.direction}`);
238
+ }
239
+ const owner = getAddress(args.owner);
240
+ // The standing Morpho authorization is granted to the SwapVerifier (which
241
+ // runs the privileged borrow / withdrawCollateral legs as top-level EVC
242
+ // items), not the Swapper. Pinning onBehalf to the caller prevents replay
243
+ // abuse by a front-runner.
244
+ const swapVerifier = getSwapVerifierAddress(this.deploymentService, args.chainId);
245
+ const alreadyAuthorized = await this.isAuthorized(args.chainId, owner, swapVerifier);
246
+ if (alreadyAuthorized && !args.removeAuthorizationAfterMigration)
247
+ return undefined;
248
+ const morpho = this.getMorphoAddress(args.chainId);
249
+ const provider = this.providerService.getProvider(args.chainId);
250
+ const nonce = (await provider.readContract({
251
+ address: morpho,
252
+ abi: morphoBlueAbi,
253
+ functionName: "nonce",
254
+ args: [owner],
255
+ }));
256
+ const deadline = args.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 60 * 60);
257
+ const enableRequest = alreadyAuthorized
258
+ ? undefined
259
+ : this.buildAuthorizationRequest({
260
+ chainId: args.chainId,
261
+ owner,
262
+ morpho,
263
+ swapVerifier,
264
+ nonce,
265
+ deadline,
266
+ isAuthorized: true,
267
+ positionId: args.position?.id,
268
+ });
269
+ const disableRequest = args.removeAuthorizationAfterMigration
270
+ ? this.buildAuthorizationRequest({
271
+ chainId: args.chainId,
272
+ owner,
273
+ morpho,
274
+ swapVerifier,
275
+ nonce: nonce + (enableRequest ? 1n : 0n),
276
+ deadline,
277
+ isAuthorized: false,
278
+ positionId: args.position?.id,
279
+ })
280
+ : undefined;
281
+ if (enableRequest && disableRequest) {
282
+ enableRequest.postMigrationAuthorization = disableRequest;
283
+ }
284
+ return enableRequest ?? disableRequest;
285
+ }
286
+ async buildMigrationBatch(args) {
287
+ if (args.position.connectorId !== MORPHO_CONNECTOR_ID) {
288
+ throw new Error(`Morpho connector cannot build migration for ${args.position.connectorId}`);
289
+ }
290
+ if (args.direction === "external-to-euler") {
291
+ return this.buildExternalToEulerBatch(args);
292
+ }
293
+ if (args.direction === "euler-to-external") {
294
+ return this.buildEulerToMorphoBatch(args);
295
+ }
296
+ throw new Error(`Unsupported migration direction: ${args.direction}`);
297
+ }
298
+ async buildExternalToEulerBatch(args) {
299
+ const target = assertEulerTarget(args.target);
300
+ const owner = getAddress(args.owner);
301
+ const eulerAccount = getAddress(target.eulerAccount);
302
+ const targetBorrowVault = target.borrowVault
303
+ ? getAddress(target.borrowVault)
304
+ : undefined;
305
+ const targetCollateralVault = getAddress(target.collateralVault);
306
+ const marketParams = normalizeMarketParams(args.position.raw.marketParams);
307
+ const morpho = this.getMorphoAddress(args.chainId);
308
+ const swapper = getSwapperAddress(this.deploymentService, args.chainId, target.swapper);
309
+ const swapVerifier = getSwapVerifierAddress(this.deploymentService, args.chainId);
310
+ const borrowShares = args.position.raw.borrowShares;
311
+ const hasDebt = borrowShares > 0n;
312
+ const collateralAmount = args.position.raw.collateral;
313
+ const collateralWithdrawAmount = collateralAmount;
314
+ const collateralMinAmount = target.minCollateralAssets ?? collateralAmount;
315
+ const verifierDeadline = args.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 60 * 60);
316
+ const collateralSwapQuote = args.collateralSwapQuote;
317
+ const debtSwapQuote = args.debtSwapQuote;
318
+ const baseDebtRepayAmount = applyBuffer(args.position.raw.borrowAssets, target.interestBufferBps ?? this.defaultInterestBufferBps);
319
+ const borrowAmount = target.borrowAmount ??
320
+ (debtSwapQuote
321
+ ? getSwapQuoteInputAmount(debtSwapQuote)
322
+ : baseDebtRepayAmount);
323
+ if (collateralAmount <= 0n) {
324
+ throw new Error("Morpho source position has no collateral to migrate");
325
+ }
326
+ if (hasDebt && !targetBorrowVault) {
327
+ throw new Error("Target Euler borrow vault is required");
328
+ }
329
+ if (!hasDebt && debtSwapQuote) {
330
+ throw new Error("Morpho debt swap quote requires source debt");
331
+ }
332
+ if (hasDebt && borrowAmount <= 0n) {
333
+ throw new Error("Euler borrow amount must be greater than zero");
334
+ }
335
+ if (debtSwapQuote) {
336
+ assertSwapQuoteUsesSwapper(debtSwapQuote, swapper, "Morpho debt migration");
337
+ }
338
+ if (collateralSwapQuote) {
339
+ assertSwapQuoteUsesSwapper(collateralSwapQuote, swapper, "Morpho collateral migration");
340
+ }
341
+ const items = [];
342
+ const alreadyAuthorized = args.skipAuthorizationCheck && args.authorization
343
+ ? false
344
+ : await this.isAuthorized(args.chainId, owner, swapVerifier);
345
+ if (!alreadyAuthorized) {
346
+ if (!args.authorization) {
347
+ throw new Error("Morpho authorization for the Euler SwapVerifier is required");
348
+ }
349
+ items.push(this.encodeSetAuthorizationWithSigItem({
350
+ chainId: args.chainId,
351
+ owner,
352
+ swapVerifier,
353
+ authorization: args.authorization,
354
+ isAuthorized: true,
355
+ }));
356
+ }
357
+ if (hasDebt && targetBorrowVault && (target.enableController ?? true)) {
358
+ items.push(this.executionService.encodeEnableController(args.chainId, eulerAccount, targetBorrowVault));
359
+ }
360
+ if (hasDebt && (target.enableCollateral ?? true)) {
361
+ items.push(this.executionService.encodeEnableCollateral(args.chainId, eulerAccount, targetCollateralVault));
362
+ }
363
+ if (hasDebt && targetBorrowVault) {
364
+ items.push({
365
+ targetContract: targetBorrowVault,
366
+ onBehalfOfAccount: eulerAccount,
367
+ value: 0n,
368
+ data: encodeFunctionData({
369
+ abi: eVaultAbi,
370
+ functionName: "borrow",
371
+ args: [borrowAmount, swapper],
372
+ }),
373
+ });
374
+ }
375
+ // Multicall #1: repay the Morpho debt (and run the debt swap if present)
376
+ // before withdrawing collateral. Only emitted when there is source debt.
377
+ if (hasDebt) {
378
+ const preWithdrawCalls = [];
379
+ if (debtSwapQuote) {
380
+ preWithdrawCalls.push(...getSwapQuoteSwapCalls(debtSwapQuote, "Morpho debt migration"));
381
+ }
382
+ preWithdrawCalls.push(encodeGenericSwap({
383
+ target: morpho,
384
+ tokenIn: marketParams.loanToken,
385
+ tokenOut: marketParams.loanToken,
386
+ payload: encodeFunctionData({
387
+ abi: morphoBlueAbi,
388
+ functionName: "repay",
389
+ args: [marketParams, 0n, borrowShares, owner, ZERO_BYTES],
390
+ }),
391
+ }));
392
+ if (debtSwapQuote) {
393
+ preWithdrawCalls.push(encodeFunctionData({
394
+ abi: swapperAbi,
395
+ functionName: "sweep",
396
+ args: [marketParams.loanToken, 0n, owner],
397
+ }));
398
+ }
399
+ items.push({
400
+ targetContract: swapper,
401
+ onBehalfOfAccount: owner,
402
+ value: 0n,
403
+ data: encodeFunctionData({
404
+ abi: swapperAbi,
405
+ functionName: "multicall",
406
+ args: [preWithdrawCalls],
407
+ }),
408
+ });
409
+ }
410
+ // Top-level EVC item: withdraw Morpho collateral on behalf of the owner
411
+ // through the SwapVerifier (the standing authorization is granted to it).
412
+ // No-swap migrations forward the collateral to the verifier so the
413
+ // verified deposit helper can perform the final Euler deposit.
414
+ items.push({
415
+ targetContract: swapVerifier,
416
+ onBehalfOfAccount: owner,
417
+ value: 0n,
418
+ data: encodeFunctionData({
419
+ abi: swapVerifierAbi,
420
+ functionName: "morphoWithdrawCollateralForSender",
421
+ args: [
422
+ morpho,
423
+ marketParams,
424
+ collateralWithdrawAmount,
425
+ collateralSwapQuote ? swapper : swapVerifier,
426
+ ],
427
+ }),
428
+ });
429
+ // Multicall #2: run the collateral swap, if present, and repay any excess
430
+ // debt. No-swap collateral deposits are handled by SwapVerifier below.
431
+ const postDepositRepayCall = hasDebt && targetBorrowVault
432
+ ? encodeFunctionData({
433
+ abi: swapperAbi,
434
+ functionName: "repay",
435
+ args: [
436
+ debtSwapQuote
437
+ ? getAddress(debtSwapQuote.tokenIn.address)
438
+ : marketParams.loanToken,
439
+ targetBorrowVault,
440
+ borrowAmount,
441
+ eulerAccount,
442
+ ],
443
+ })
444
+ : undefined;
445
+ const postWithdrawCalls = [];
446
+ if (collateralSwapQuote) {
447
+ postWithdrawCalls.push(collateralSwapQuote.swap.swapperData);
448
+ if (postDepositRepayCall)
449
+ postWithdrawCalls.push(postDepositRepayCall);
450
+ }
451
+ if (postWithdrawCalls.length > 0) {
452
+ items.push({
453
+ targetContract: swapper,
454
+ onBehalfOfAccount: owner,
455
+ value: 0n,
456
+ data: encodeFunctionData({
457
+ abi: swapperAbi,
458
+ functionName: "multicall",
459
+ args: [postWithdrawCalls],
460
+ }),
461
+ });
462
+ }
463
+ if (collateralSwapQuote) {
464
+ items.push(encodeSwapQuoteVerificationItem({
465
+ quote: collateralSwapQuote,
466
+ swapVerifier,
467
+ vault: targetCollateralVault,
468
+ account: eulerAccount,
469
+ deadline: args.deadline ?? BigInt(collateralSwapQuote.verify.deadline ?? 0),
470
+ label: "Morpho collateral migration",
471
+ }));
472
+ }
473
+ else {
474
+ items.push(encodeVerifyAmountMinAndDepositItem({
475
+ swapVerifier,
476
+ onBehalfOfAccount: owner,
477
+ vault: targetCollateralVault,
478
+ receiver: eulerAccount,
479
+ amountMin: collateralMinAmount,
480
+ deadline: verifierDeadline,
481
+ }));
482
+ if (postDepositRepayCall) {
483
+ items.push({
484
+ targetContract: swapper,
485
+ onBehalfOfAccount: owner,
486
+ value: 0n,
487
+ data: postDepositRepayCall,
488
+ });
489
+ }
490
+ }
491
+ if (args.removeAuthorizationAfterMigration) {
492
+ items.push(this.encodeRemoveAuthorizationItem({
493
+ chainId: args.chainId,
494
+ owner,
495
+ swapVerifier,
496
+ authorization: args.authorization,
497
+ }));
498
+ }
499
+ return items;
500
+ }
501
+ async buildEulerToMorphoBatch(args) {
502
+ if (args.collateralSwapQuote || args.debtSwapQuote) {
503
+ throw new Error("Morpho Euler to Morpho migration does not support swaps");
504
+ }
505
+ const source = assertEulerSource(args.source);
506
+ const owner = getAddress(args.owner);
507
+ const eulerAccount = getAddress(source.eulerAccount);
508
+ const sourceBorrowVault = getAddress(source.borrowVault);
509
+ const sourceCollateralVault = getAddress(source.collateralVault);
510
+ const marketParams = normalizeMarketParams(args.position.raw.marketParams);
511
+ const morpho = this.getMorphoAddress(args.chainId);
512
+ const swapper = getSwapperAddress(this.deploymentService, args.chainId, source.swapper);
513
+ const swapVerifier = getSwapVerifierAddress(this.deploymentService, args.chainId);
514
+ const verifierDeadline = args.deadline ?? BigInt(Math.floor(Date.now() / 1000) + 60 * 60);
515
+ const sourceAmounts = await resolveEulerSourceAmounts(source, args.account);
516
+ const externalTarget = args.externalTarget ?? {};
517
+ const collateralAmount = externalTarget.collateralAmount ?? sourceAmounts.collateralAmount;
518
+ const sourceCollateralAmount = collateralAmount;
519
+ const redeemCollateralShares = externalTarget.collateralAmount === undefined
520
+ ? sourceAmounts.collateralShares
521
+ : undefined;
522
+ const morphoBorrowAmount = externalTarget.borrowAmount ??
523
+ applyBuffer(sourceAmounts.debtAmount, externalTarget.interestBufferBps ??
524
+ DEFAULT_OUTBOUND_INTEREST_BUFFER_BPS);
525
+ const eulerRepayAmount = externalTarget.repayAmount ?? maxUint256;
526
+ if (sourceAmounts.debtAmount <= 0n) {
527
+ throw new Error("Euler source position has no debt to migrate");
528
+ }
529
+ if (sourceCollateralAmount <= 0n || collateralAmount <= 0n) {
530
+ throw new Error("Euler source position has no collateral to migrate");
531
+ }
532
+ if (morphoBorrowAmount <= 0n) {
533
+ throw new Error("Morpho borrow amount must be greater than zero");
534
+ }
535
+ const items = [];
536
+ const alreadyAuthorized = await this.isAuthorized(args.chainId, owner, swapVerifier);
537
+ if (!alreadyAuthorized) {
538
+ if (!args.authorization) {
539
+ throw new Error("Morpho authorization for the Euler SwapVerifier is required");
540
+ }
541
+ items.push(this.encodeSetAuthorizationWithSigItem({
542
+ chainId: args.chainId,
543
+ owner,
544
+ swapVerifier,
545
+ authorization: args.authorization,
546
+ isAuthorized: true,
547
+ }));
548
+ }
549
+ items.push({
550
+ targetContract: sourceCollateralVault,
551
+ onBehalfOfAccount: eulerAccount,
552
+ value: 0n,
553
+ data: encodeFunctionData({
554
+ abi: eVaultAbi,
555
+ functionName: redeemCollateralShares ? "redeem" : "withdraw",
556
+ args: redeemCollateralShares
557
+ ? [redeemCollateralShares, swapper, eulerAccount]
558
+ : [sourceCollateralAmount, swapper, eulerAccount],
559
+ }),
560
+ });
561
+ // Multicall #1: supply exact Morpho collateral before borrowing.
562
+ const preBorrowCalls = [
563
+ encodeGenericSwap({
564
+ target: morpho,
565
+ tokenIn: marketParams.collateralToken,
566
+ tokenOut: marketParams.collateralToken,
567
+ payload: encodeFunctionData({
568
+ abi: morphoBlueAbi,
569
+ functionName: "supplyCollateral",
570
+ args: [marketParams, collateralAmount, owner, ZERO_BYTES],
571
+ }),
572
+ }),
573
+ ];
574
+ items.push({
575
+ targetContract: swapper,
576
+ onBehalfOfAccount: owner,
577
+ value: 0n,
578
+ data: encodeFunctionData({
579
+ abi: swapperAbi,
580
+ functionName: "multicall",
581
+ args: [preBorrowCalls],
582
+ }),
583
+ });
584
+ // Top-level EVC item: borrow from Morpho on behalf of the owner through the
585
+ // SwapVerifier (the standing authorization is granted to it), forwarding the
586
+ // borrowed loan token to the Swapper.
587
+ items.push({
588
+ targetContract: swapVerifier,
589
+ onBehalfOfAccount: owner,
590
+ value: 0n,
591
+ data: encodeFunctionData({
592
+ abi: swapVerifierAbi,
593
+ functionName: "morphoBorrowForSender",
594
+ args: [morpho, marketParams, morphoBorrowAmount, swapper],
595
+ }),
596
+ });
597
+ // Multicall #2: repay the Euler source borrow vault and sweep any
598
+ // remainders back to the owner.
599
+ const postBorrowCalls = [
600
+ encodeFunctionData({
601
+ abi: swapperAbi,
602
+ functionName: "repay",
603
+ args: [
604
+ marketParams.loanToken,
605
+ sourceBorrowVault,
606
+ eulerRepayAmount,
607
+ eulerAccount,
608
+ ],
609
+ }),
610
+ ];
611
+ postBorrowCalls.push(encodeFunctionData({
612
+ abi: swapperAbi,
613
+ functionName: "sweep",
614
+ args: [marketParams.loanToken, 0n, owner],
615
+ }));
616
+ // Return collateral remainders from yield-bearing source-vault share drift
617
+ // to the owner rather than stranding them.
618
+ postBorrowCalls.push(encodeFunctionData({
619
+ abi: swapperAbi,
620
+ functionName: "sweep",
621
+ args: [marketParams.collateralToken, 0n, owner],
622
+ }));
623
+ items.push({
624
+ targetContract: swapper,
625
+ onBehalfOfAccount: owner,
626
+ value: 0n,
627
+ data: encodeFunctionData({
628
+ abi: swapperAbi,
629
+ functionName: "multicall",
630
+ args: [postBorrowCalls],
631
+ }),
632
+ });
633
+ items.push(encodeVerifyDebtMaxItem({
634
+ swapVerifier,
635
+ onBehalfOfAccount: owner,
636
+ vault: sourceBorrowVault,
637
+ account: eulerAccount,
638
+ amountMax: 0n,
639
+ deadline: verifierDeadline,
640
+ }));
641
+ if (args.removeAuthorizationAfterMigration) {
642
+ items.push(this.encodeRemoveAuthorizationItem({
643
+ chainId: args.chainId,
644
+ owner,
645
+ swapVerifier,
646
+ authorization: args.authorization,
647
+ }));
648
+ }
649
+ return items;
650
+ }
651
+ getProtocolAddress(chainId) {
652
+ const morpho = this.morphoAddresses[chainId];
653
+ return morpho ? getAddress(morpho) : undefined;
654
+ }
655
+ getMorphoAddress(chainId) {
656
+ const morpho = this.getProtocolAddress(chainId);
657
+ if (!morpho) {
658
+ throw new Error(`Morpho connector is not configured for chainId ${chainId}`);
659
+ }
660
+ return morpho;
661
+ }
662
+ async isAuthorized(chainId, authorizer, authorized) {
663
+ const provider = this.providerService.getProvider(chainId);
664
+ return provider.readContract({
665
+ address: this.getMorphoAddress(chainId),
666
+ abi: morphoBlueAbi,
667
+ functionName: "isAuthorized",
668
+ args: [getAddress(authorizer), getAddress(authorized)],
669
+ });
670
+ }
671
+ encodeSetAuthorizationWithSigItem(args) {
672
+ const request = assertMorphoAuthorizationRequest(args.authorization.request);
673
+ if (!args.authorization.signature) {
674
+ throw new Error("Morpho authorization signature is required");
675
+ }
676
+ const authorization = toMorphoAuthorization(request.typedData.message);
677
+ assertSameAddress(authorization.authorizer, args.owner, "Morpho authorization authorizer must match migration owner");
678
+ assertSameAddress(authorization.authorized, args.swapVerifier, "Morpho authorization target must be the Euler SwapVerifier");
679
+ if (authorization.isAuthorized !== args.isAuthorized) {
680
+ throw new Error(`Morpho authorization must set isAuthorized=${args.isAuthorized}`);
681
+ }
682
+ return {
683
+ targetContract: this.getMorphoAddress(args.chainId),
684
+ onBehalfOfAccount: args.owner,
685
+ value: 0n,
686
+ data: encodeFunctionData({
687
+ abi: morphoBlueAbi,
688
+ functionName: "setAuthorizationWithSig",
689
+ args: [
690
+ authorization,
691
+ splitMorphoAuthorizationSignature(args.authorization.signature),
692
+ ],
693
+ }),
694
+ };
695
+ }
696
+ encodeRemoveAuthorizationItem(args) {
697
+ const authorization = args.authorization?.postMigrationAuthorization ?? args.authorization;
698
+ if (!authorization) {
699
+ throw new Error("Morpho authorization removal signature is required");
700
+ }
701
+ return this.encodeSetAuthorizationWithSigItem({
702
+ chainId: args.chainId,
703
+ owner: args.owner,
704
+ swapVerifier: args.swapVerifier,
705
+ authorization,
706
+ isAuthorized: false,
707
+ });
708
+ }
709
+ buildAuthorizationRequest(args) {
710
+ const message = {
711
+ authorizer: args.owner,
712
+ authorized: args.swapVerifier,
713
+ isAuthorized: args.isAuthorized,
714
+ nonce: args.nonce,
715
+ deadline: args.deadline,
716
+ };
717
+ return {
718
+ kind: "typedData",
719
+ connectorId: MORPHO_CONNECTOR_ID,
720
+ protocol: MORPHO_PROTOCOL,
721
+ chainId: args.chainId,
722
+ owner: args.owner,
723
+ positionId: args.positionId,
724
+ typedData: {
725
+ domain: {
726
+ chainId: args.chainId,
727
+ verifyingContract: args.morpho,
728
+ },
729
+ types: MORPHO_AUTHORIZATION_TYPES,
730
+ primaryType: "Authorization",
731
+ message,
732
+ },
733
+ };
734
+ }
735
+ }
736
+ function assertMorphoAuthorizationRequest(request) {
737
+ if (request.kind !== "typedData" ||
738
+ request.connectorId !== MORPHO_CONNECTOR_ID) {
739
+ throw new Error("Expected a Morpho typed-data authorization request");
740
+ }
741
+ return request;
742
+ }
743
+ function toMorphoAuthorization(message) {
744
+ return {
745
+ authorizer: getAddress(message.authorizer),
746
+ authorized: getAddress(message.authorized),
747
+ isAuthorized: message.isAuthorized,
748
+ nonce: BigInt(message.nonce),
749
+ deadline: BigInt(message.deadline),
750
+ };
751
+ }
752
+ function toMorphoMigrationTarget(chainId, market) {
753
+ const loanAsset = parseMorphoApiAsset(market.loanAsset);
754
+ const collateralAsset = parseMorphoApiAsset(market.collateralAsset);
755
+ if (!market.marketId ||
756
+ !loanAsset ||
757
+ !collateralAsset ||
758
+ !market.oracle?.address ||
759
+ !market.irmAddress) {
760
+ return null;
761
+ }
762
+ const lltvRaw = parseBigIntAmount(market.lltv);
763
+ const liquidityAssets = parseBigIntAmount(market.state?.liquidityAssets);
764
+ const ref = {
765
+ loanToken: loanAsset.asset,
766
+ collateralToken: collateralAsset.asset,
767
+ oracle: getAddress(market.oracle.address),
768
+ irm: getAddress(market.irmAddress),
769
+ lltv: lltvRaw,
770
+ };
771
+ const marketId = getMorphoMarketId(ref);
772
+ return {
773
+ connectorId: MORPHO_CONNECTOR_ID,
774
+ protocol: MORPHO_PROTOCOL,
775
+ id: marketId,
776
+ chainId,
777
+ ref,
778
+ debt: loanAsset,
779
+ collateral: collateralAsset,
780
+ liquidity: {
781
+ asset: loanAsset.asset,
782
+ amount: liquidityAssets,
783
+ },
784
+ raw: {
785
+ marketId,
786
+ listed: market.listed !== false,
787
+ borrowApy: parseNumberOrNull(market.state?.borrowApy),
788
+ netBorrowApy: parseNumberOrNull(market.state?.netBorrowApy),
789
+ supplyApy: parseNumberOrNull(market.state?.supplyApy),
790
+ lltv: lltvRaw > 0n ? Number(lltvRaw) / 1e16 : null,
791
+ liquidityAssets,
792
+ liquidityAssetsUsd: parseNumberOrNull(market.state?.liquidityAssetsUsd),
793
+ },
794
+ };
795
+ }
796
+ function parseMorphoApiAsset(asset) {
797
+ if (!asset?.address)
798
+ return null;
799
+ const parsed = Number(asset.decimals);
800
+ return {
801
+ asset: getAddress(asset.address),
802
+ ...(asset.symbol ? { symbol: asset.symbol } : {}),
803
+ ...(Number.isInteger(parsed) && parsed >= 0 ? { decimals: parsed } : {}),
804
+ };
805
+ }
806
+ function parseBigIntAmount(value) {
807
+ if (typeof value === "bigint")
808
+ return value;
809
+ if (typeof value === "number" && Number.isFinite(value)) {
810
+ return BigInt(Math.trunc(value));
811
+ }
812
+ if (typeof value !== "string")
813
+ return 0n;
814
+ const trimmed = value.trim();
815
+ if (!trimmed)
816
+ return 0n;
817
+ try {
818
+ return BigInt(trimmed);
819
+ }
820
+ catch {
821
+ return 0n;
822
+ }
823
+ }
824
+ function parseNumberOrNull(value) {
825
+ if (typeof value === "number")
826
+ return Number.isFinite(value) ? value : null;
827
+ if (typeof value !== "string")
828
+ return null;
829
+ const parsed = Number(value);
830
+ return Number.isFinite(parsed) ? parsed : null;
831
+ }
832
+ function normalizeMarketParams(marketParams) {
833
+ return {
834
+ loanToken: getAddress(marketParams.loanToken),
835
+ collateralToken: getAddress(marketParams.collateralToken),
836
+ oracle: getAddress(marketParams.oracle),
837
+ irm: getAddress(marketParams.irm),
838
+ lltv: BigInt(marketParams.lltv),
839
+ };
840
+ }
841
+ function toMorphoMarketState(market) {
842
+ return {
843
+ totalSupplyAssets: market[0],
844
+ totalSupplyShares: market[1],
845
+ totalBorrowAssets: market[2],
846
+ totalBorrowShares: market[3],
847
+ lastUpdate: market[4],
848
+ fee: market[5],
849
+ };
850
+ }
851
+ function toAssetsUp(shares, totalAssets, totalShares) {
852
+ if (shares === 0n || totalAssets === 0n || totalShares === 0n)
853
+ return 0n;
854
+ return (shares * totalAssets + totalShares - 1n) / totalShares;
855
+ }
856
+ //# sourceMappingURL=morphoConnector.js.map