@morpho-org/consumer-sdk 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +136 -10
- package/lib/actions/index.d.ts +1 -0
- package/lib/actions/index.js +1 -0
- package/lib/actions/marketV1/borrow.d.ts +34 -0
- package/lib/actions/marketV1/borrow.js +62 -0
- package/lib/actions/marketV1/buildReallocationActions.d.ts +17 -0
- package/lib/actions/marketV1/buildReallocationActions.js +36 -0
- package/lib/actions/marketV1/index.d.ts +6 -0
- package/lib/actions/marketV1/index.js +22 -0
- package/lib/actions/marketV1/repay.d.ts +44 -0
- package/lib/actions/marketV1/repay.js +93 -0
- package/lib/actions/marketV1/repayWithdrawCollateral.d.ts +51 -0
- package/lib/actions/marketV1/repayWithdrawCollateral.js +108 -0
- package/lib/actions/marketV1/supplyCollateral.d.ts +28 -0
- package/lib/actions/marketV1/supplyCollateral.js +85 -0
- package/lib/actions/marketV1/supplyCollateralBorrow.d.ts +37 -0
- package/lib/actions/marketV1/supplyCollateralBorrow.js +109 -0
- package/lib/actions/marketV1/withdrawCollateral.d.ts +28 -0
- package/lib/actions/marketV1/withdrawCollateral.js +51 -0
- package/lib/actions/requirements/encode/encodeErc20Permit.js +4 -1
- package/lib/actions/requirements/encode/encodeErc20Permit2.js +4 -1
- package/lib/actions/requirements/getMorphoAuthorizationRequirement.d.ts +21 -0
- package/lib/actions/requirements/getMorphoAuthorizationRequirement.js +55 -0
- package/lib/actions/requirements/index.d.ts +1 -0
- package/lib/actions/requirements/index.js +1 -0
- package/lib/actions/vaultV2/forceWithdraw.js +5 -1
- package/lib/client/morphoClient.d.ts +3 -1
- package/lib/client/morphoClient.js +3 -0
- package/lib/entities/index.d.ts +1 -0
- package/lib/entities/index.js +1 -0
- package/lib/entities/marketV1/index.d.ts +1 -0
- package/lib/entities/marketV1/index.js +17 -0
- package/lib/entities/marketV1/marketV1.d.ts +290 -0
- package/lib/entities/marketV1/marketV1.js +528 -0
- package/lib/entities/vaultV1/vaultV1.js +4 -0
- package/lib/entities/vaultV2/vaultV2.js +4 -0
- package/lib/helpers/computeReallocations.d.ts +23 -0
- package/lib/helpers/computeReallocations.js +98 -0
- package/lib/helpers/constant.d.ts +5 -0
- package/lib/helpers/constant.js +6 -1
- package/lib/helpers/index.d.ts +3 -0
- package/lib/helpers/index.js +18 -1
- package/lib/helpers/slippage.d.ts +46 -0
- package/lib/helpers/slippage.js +73 -0
- package/lib/helpers/validate.d.ts +150 -0
- package/lib/helpers/validate.js +279 -0
- package/lib/types/action.d.ts +75 -3
- package/lib/types/action.js +12 -1
- package/lib/types/client.d.ts +3 -1
- package/lib/types/error.d.ts +106 -1
- package/lib/types/error.js +165 -3
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/sharedLiquidity.d.ts +41 -0
- package/lib/types/sharedLiquidity.js +2 -0
- package/package.json +1 -1
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MorphoMarketV1 = void 0;
|
|
4
|
+
const blue_sdk_1 = require("@morpho-org/blue-sdk");
|
|
5
|
+
const blue_sdk_viem_1 = require("@morpho-org/blue-sdk-viem");
|
|
6
|
+
const simulation_sdk_1 = require("@morpho-org/simulation-sdk");
|
|
7
|
+
const actions_1 = require("../../actions");
|
|
8
|
+
const helpers_1 = require("../../helpers");
|
|
9
|
+
const types_1 = require("../../types");
|
|
10
|
+
class MorphoMarketV1 {
|
|
11
|
+
client;
|
|
12
|
+
marketParams;
|
|
13
|
+
chainId;
|
|
14
|
+
constructor(client, marketParams, chainId) {
|
|
15
|
+
this.client = client;
|
|
16
|
+
this.marketParams = marketParams;
|
|
17
|
+
this.chainId = chainId;
|
|
18
|
+
}
|
|
19
|
+
async getMarketData(parameters) {
|
|
20
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
21
|
+
return (0, blue_sdk_viem_1.fetchMarket)(this.marketParams.id, this.client.viemClient, {
|
|
22
|
+
...parameters,
|
|
23
|
+
chainId: this.chainId,
|
|
24
|
+
deployless: this.client.options.supportDeployless,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
async getPositionData(userAddress, parameters) {
|
|
28
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
29
|
+
return (0, blue_sdk_viem_1.fetchAccrualPosition)(userAddress, this.marketParams.id, this.client.viemClient, {
|
|
30
|
+
...parameters,
|
|
31
|
+
deployless: this.client.options.supportDeployless,
|
|
32
|
+
chainId: this.chainId,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
supplyCollateral({ amount = 0n, userAddress, nativeAmount, }) {
|
|
36
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
37
|
+
(0, helpers_1.validateUserAddress)(this.client.viemClient.account?.address, userAddress);
|
|
38
|
+
if (amount < 0n) {
|
|
39
|
+
throw new types_1.NonPositiveAssetAmountError(this.marketParams.collateralToken);
|
|
40
|
+
}
|
|
41
|
+
if (nativeAmount !== undefined && nativeAmount < 0n) {
|
|
42
|
+
throw new types_1.NegativeNativeAmountError(nativeAmount);
|
|
43
|
+
}
|
|
44
|
+
const totalCollateral = amount + (nativeAmount ?? 0n);
|
|
45
|
+
if (totalCollateral === 0n) {
|
|
46
|
+
throw new types_1.ZeroCollateralAmountError(this.marketParams.id);
|
|
47
|
+
}
|
|
48
|
+
if (nativeAmount !== undefined && nativeAmount > 0n) {
|
|
49
|
+
(0, helpers_1.validateNativeCollateral)(this.chainId, this.marketParams.collateralToken);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
getRequirements: (params) => (0, actions_1.getRequirements)(this.client.viemClient, {
|
|
53
|
+
address: this.marketParams.collateralToken,
|
|
54
|
+
chainId: this.chainId,
|
|
55
|
+
supportSignature: this.client.options.supportSignature,
|
|
56
|
+
supportDeployless: this.client.options.supportDeployless,
|
|
57
|
+
useSimplePermit: params?.useSimplePermit,
|
|
58
|
+
args: { amount, from: userAddress },
|
|
59
|
+
}),
|
|
60
|
+
buildTx: (requirementSignature) => (0, actions_1.marketV1SupplyCollateral)({
|
|
61
|
+
market: {
|
|
62
|
+
chainId: this.chainId,
|
|
63
|
+
marketParams: this.marketParams,
|
|
64
|
+
},
|
|
65
|
+
args: {
|
|
66
|
+
amount,
|
|
67
|
+
nativeAmount,
|
|
68
|
+
onBehalf: userAddress,
|
|
69
|
+
requirementSignature,
|
|
70
|
+
},
|
|
71
|
+
metadata: this.client.options.metadata,
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
borrow({ amount, userAddress, positionData, slippageTolerance = blue_sdk_1.DEFAULT_SLIPPAGE_TOLERANCE, reallocations, }) {
|
|
76
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
77
|
+
(0, helpers_1.validateUserAddress)(this.client.viemClient.account?.address, userAddress);
|
|
78
|
+
if (amount <= 0n) {
|
|
79
|
+
throw new types_1.NonPositiveBorrowAmountError(this.marketParams.id);
|
|
80
|
+
}
|
|
81
|
+
(0, helpers_1.validateSlippageTolerance)(slippageTolerance);
|
|
82
|
+
if (!positionData) {
|
|
83
|
+
throw new types_1.MissingAccrualPositionError(this.marketParams.id);
|
|
84
|
+
}
|
|
85
|
+
(0, helpers_1.validateAccrualPosition)({
|
|
86
|
+
positionData,
|
|
87
|
+
expectedMarketId: this.marketParams.id,
|
|
88
|
+
expectedUser: userAddress,
|
|
89
|
+
});
|
|
90
|
+
(0, helpers_1.validatePositionHealth)({
|
|
91
|
+
positionData,
|
|
92
|
+
additionalCollateral: 0n,
|
|
93
|
+
borrowAmount: amount,
|
|
94
|
+
marketId: this.marketParams.id,
|
|
95
|
+
lltv: this.marketParams.lltv,
|
|
96
|
+
});
|
|
97
|
+
const minSharePrice = (0, helpers_1.computeMinBorrowSharePrice)({
|
|
98
|
+
borrowAmount: amount,
|
|
99
|
+
market: positionData.market,
|
|
100
|
+
slippageTolerance,
|
|
101
|
+
});
|
|
102
|
+
return {
|
|
103
|
+
getRequirements: async () => {
|
|
104
|
+
const authTx = await (0, actions_1.getMorphoAuthorizationRequirement)({
|
|
105
|
+
viemClient: this.client.viemClient,
|
|
106
|
+
chainId: this.chainId,
|
|
107
|
+
userAddress,
|
|
108
|
+
});
|
|
109
|
+
return authTx ? [authTx] : [];
|
|
110
|
+
},
|
|
111
|
+
buildTx: () => (0, actions_1.marketV1Borrow)({
|
|
112
|
+
market: {
|
|
113
|
+
chainId: this.chainId,
|
|
114
|
+
marketParams: this.marketParams,
|
|
115
|
+
},
|
|
116
|
+
args: {
|
|
117
|
+
amount,
|
|
118
|
+
receiver: userAddress,
|
|
119
|
+
minSharePrice,
|
|
120
|
+
reallocations,
|
|
121
|
+
},
|
|
122
|
+
metadata: this.client.options.metadata,
|
|
123
|
+
}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
repay(params) {
|
|
127
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
128
|
+
(0, helpers_1.validateUserAddress)(this.client.viemClient.account?.address, params.userAddress);
|
|
129
|
+
const { userAddress, positionData, slippageTolerance = blue_sdk_1.DEFAULT_SLIPPAGE_TOLERANCE, } = params;
|
|
130
|
+
if ("assets" in params && "shares" in params) {
|
|
131
|
+
throw new types_1.MutuallyExclusiveRepayAmountsError(this.marketParams.id);
|
|
132
|
+
}
|
|
133
|
+
const isSharesMode = "shares" in params;
|
|
134
|
+
if (isSharesMode) {
|
|
135
|
+
if (params.shares <= 0n) {
|
|
136
|
+
throw new types_1.NonPositiveRepayAmountError(this.marketParams.id);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
if (params.assets <= 0n) {
|
|
141
|
+
throw new types_1.NonPositiveRepayAmountError(this.marketParams.id);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
(0, helpers_1.validateSlippageTolerance)(slippageTolerance);
|
|
145
|
+
if (!positionData) {
|
|
146
|
+
throw new types_1.MissingAccrualPositionError(this.marketParams.id);
|
|
147
|
+
}
|
|
148
|
+
(0, helpers_1.validateAccrualPosition)({
|
|
149
|
+
positionData,
|
|
150
|
+
expectedMarketId: this.marketParams.id,
|
|
151
|
+
expectedUser: userAddress,
|
|
152
|
+
});
|
|
153
|
+
let assets;
|
|
154
|
+
let shares;
|
|
155
|
+
let transferAmount;
|
|
156
|
+
if (isSharesMode) {
|
|
157
|
+
(0, helpers_1.validateRepayShares)({
|
|
158
|
+
positionData,
|
|
159
|
+
repayShares: params.shares,
|
|
160
|
+
marketId: this.marketParams.id,
|
|
161
|
+
});
|
|
162
|
+
assets = 0n;
|
|
163
|
+
shares = params.shares;
|
|
164
|
+
// Add slippage buffer to cover interest accrued between tx construction and execution.
|
|
165
|
+
// Without this, the on-chain repay amount may exceed the pre-transferred ERC20 amount.
|
|
166
|
+
const baseTransferAmount = positionData.market.toBorrowAssets(shares, "Up");
|
|
167
|
+
transferAmount = blue_sdk_1.MathLib.wMulUp(baseTransferAmount, blue_sdk_1.MathLib.WAD + slippageTolerance);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
(0, helpers_1.validateRepayAmount)({
|
|
171
|
+
positionData,
|
|
172
|
+
repayAssets: params.assets,
|
|
173
|
+
marketId: this.marketParams.id,
|
|
174
|
+
});
|
|
175
|
+
assets = params.assets;
|
|
176
|
+
shares = 0n;
|
|
177
|
+
transferAmount = params.assets;
|
|
178
|
+
}
|
|
179
|
+
const maxSharePrice = (0, helpers_1.computeMaxRepaySharePrice)({
|
|
180
|
+
repayAssets: assets,
|
|
181
|
+
repayShares: shares,
|
|
182
|
+
market: positionData.market,
|
|
183
|
+
slippageTolerance,
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
getRequirements: (reqParams) => (0, actions_1.getRequirements)(this.client.viemClient, {
|
|
187
|
+
address: this.marketParams.loanToken,
|
|
188
|
+
chainId: this.chainId,
|
|
189
|
+
supportSignature: this.client.options.supportSignature,
|
|
190
|
+
supportDeployless: this.client.options.supportDeployless,
|
|
191
|
+
useSimplePermit: reqParams?.useSimplePermit,
|
|
192
|
+
args: { amount: transferAmount, from: userAddress },
|
|
193
|
+
}),
|
|
194
|
+
buildTx: (requirementSignature) => (0, actions_1.marketV1Repay)({
|
|
195
|
+
market: {
|
|
196
|
+
chainId: this.chainId,
|
|
197
|
+
marketParams: this.marketParams,
|
|
198
|
+
},
|
|
199
|
+
args: {
|
|
200
|
+
assets,
|
|
201
|
+
shares,
|
|
202
|
+
transferAmount,
|
|
203
|
+
onBehalf: userAddress,
|
|
204
|
+
receiver: userAddress,
|
|
205
|
+
maxSharePrice,
|
|
206
|
+
requirementSignature,
|
|
207
|
+
},
|
|
208
|
+
metadata: this.client.options.metadata,
|
|
209
|
+
}),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
withdrawCollateral({ userAddress, amount, positionData, }) {
|
|
213
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
214
|
+
(0, helpers_1.validateUserAddress)(this.client.viemClient.account?.address, userAddress);
|
|
215
|
+
if (amount <= 0n) {
|
|
216
|
+
throw new types_1.NonPositiveWithdrawCollateralAmountError(this.marketParams.id);
|
|
217
|
+
}
|
|
218
|
+
if (!positionData) {
|
|
219
|
+
throw new types_1.MissingAccrualPositionError(this.marketParams.id);
|
|
220
|
+
}
|
|
221
|
+
(0, helpers_1.validateAccrualPosition)({
|
|
222
|
+
positionData,
|
|
223
|
+
expectedMarketId: this.marketParams.id,
|
|
224
|
+
expectedUser: userAddress,
|
|
225
|
+
});
|
|
226
|
+
if (amount > positionData.collateral) {
|
|
227
|
+
throw new types_1.WithdrawExceedsCollateralError({
|
|
228
|
+
withdrawAmount: amount,
|
|
229
|
+
available: positionData.collateral,
|
|
230
|
+
market: positionData.marketId,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
(0, helpers_1.validatePositionHealthAfterWithdraw)({
|
|
234
|
+
positionData,
|
|
235
|
+
withdrawAmount: amount,
|
|
236
|
+
lltv: this.marketParams.lltv,
|
|
237
|
+
marketId: this.marketParams.id,
|
|
238
|
+
});
|
|
239
|
+
return {
|
|
240
|
+
buildTx: () => (0, actions_1.marketV1WithdrawCollateral)({
|
|
241
|
+
market: {
|
|
242
|
+
chainId: this.chainId,
|
|
243
|
+
marketParams: this.marketParams,
|
|
244
|
+
},
|
|
245
|
+
args: {
|
|
246
|
+
amount,
|
|
247
|
+
onBehalf: userAddress,
|
|
248
|
+
receiver: userAddress,
|
|
249
|
+
},
|
|
250
|
+
metadata: this.client.options.metadata,
|
|
251
|
+
}),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
repayWithdrawCollateral(params) {
|
|
255
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
256
|
+
(0, helpers_1.validateUserAddress)(this.client.viemClient.account?.address, params.userAddress);
|
|
257
|
+
const { userAddress, withdrawAmount, positionData, slippageTolerance = blue_sdk_1.DEFAULT_SLIPPAGE_TOLERANCE, } = params;
|
|
258
|
+
if ("assets" in params && "shares" in params) {
|
|
259
|
+
throw new types_1.MutuallyExclusiveRepayAmountsError(this.marketParams.id);
|
|
260
|
+
}
|
|
261
|
+
const isSharesMode = "shares" in params;
|
|
262
|
+
if (isSharesMode) {
|
|
263
|
+
if (params.shares <= 0n) {
|
|
264
|
+
throw new types_1.NonPositiveRepayAmountError(this.marketParams.id);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
if (params.assets <= 0n) {
|
|
269
|
+
throw new types_1.NonPositiveRepayAmountError(this.marketParams.id);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (withdrawAmount <= 0n) {
|
|
273
|
+
throw new types_1.NonPositiveWithdrawCollateralAmountError(this.marketParams.id);
|
|
274
|
+
}
|
|
275
|
+
(0, helpers_1.validateSlippageTolerance)(slippageTolerance);
|
|
276
|
+
if (!positionData) {
|
|
277
|
+
throw new types_1.MissingAccrualPositionError(this.marketParams.id);
|
|
278
|
+
}
|
|
279
|
+
(0, helpers_1.validateAccrualPosition)({
|
|
280
|
+
positionData,
|
|
281
|
+
expectedMarketId: this.marketParams.id,
|
|
282
|
+
expectedUser: userAddress,
|
|
283
|
+
});
|
|
284
|
+
let assets;
|
|
285
|
+
let shares;
|
|
286
|
+
let transferAmount;
|
|
287
|
+
if (isSharesMode) {
|
|
288
|
+
(0, helpers_1.validateRepayShares)({
|
|
289
|
+
positionData,
|
|
290
|
+
repayShares: params.shares,
|
|
291
|
+
marketId: this.marketParams.id,
|
|
292
|
+
});
|
|
293
|
+
assets = 0n;
|
|
294
|
+
shares = params.shares;
|
|
295
|
+
const baseTransferAmount = positionData.market.toBorrowAssets(shares, "Up");
|
|
296
|
+
transferAmount = blue_sdk_1.MathLib.wMulUp(baseTransferAmount, blue_sdk_1.MathLib.WAD + slippageTolerance);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
(0, helpers_1.validateRepayAmount)({
|
|
300
|
+
positionData,
|
|
301
|
+
repayAssets: params.assets,
|
|
302
|
+
marketId: this.marketParams.id,
|
|
303
|
+
});
|
|
304
|
+
assets = params.assets;
|
|
305
|
+
shares = 0n;
|
|
306
|
+
transferAmount = params.assets;
|
|
307
|
+
}
|
|
308
|
+
if (withdrawAmount > positionData.collateral) {
|
|
309
|
+
throw new types_1.WithdrawExceedsCollateralError({
|
|
310
|
+
withdrawAmount,
|
|
311
|
+
available: positionData.collateral,
|
|
312
|
+
market: positionData.marketId,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
// Simulate repay to get post-repay position, then validate withdraw health
|
|
316
|
+
const { position: positionAfterRepay } = positionData.repay(assets, shares);
|
|
317
|
+
(0, helpers_1.validatePositionHealthAfterWithdraw)({
|
|
318
|
+
positionData: positionAfterRepay,
|
|
319
|
+
withdrawAmount,
|
|
320
|
+
lltv: this.marketParams.lltv,
|
|
321
|
+
marketId: this.marketParams.id,
|
|
322
|
+
});
|
|
323
|
+
const maxSharePrice = (0, helpers_1.computeMaxRepaySharePrice)({
|
|
324
|
+
repayAssets: assets,
|
|
325
|
+
repayShares: shares,
|
|
326
|
+
market: positionData.market,
|
|
327
|
+
slippageTolerance,
|
|
328
|
+
});
|
|
329
|
+
return {
|
|
330
|
+
getRequirements: async (reqParams) => {
|
|
331
|
+
const [erc20Requirements, authTx] = await Promise.all([
|
|
332
|
+
(0, actions_1.getRequirements)(this.client.viemClient, {
|
|
333
|
+
address: this.marketParams.loanToken,
|
|
334
|
+
chainId: this.chainId,
|
|
335
|
+
supportSignature: this.client.options.supportSignature,
|
|
336
|
+
supportDeployless: this.client.options.supportDeployless,
|
|
337
|
+
useSimplePermit: reqParams?.useSimplePermit,
|
|
338
|
+
args: { amount: transferAmount, from: userAddress },
|
|
339
|
+
}),
|
|
340
|
+
(0, actions_1.getMorphoAuthorizationRequirement)({
|
|
341
|
+
viemClient: this.client.viemClient,
|
|
342
|
+
chainId: this.chainId,
|
|
343
|
+
userAddress,
|
|
344
|
+
}),
|
|
345
|
+
]);
|
|
346
|
+
return [...erc20Requirements, ...(authTx ? [authTx] : [])];
|
|
347
|
+
},
|
|
348
|
+
buildTx: (requirementSignature) => (0, actions_1.marketV1RepayWithdrawCollateral)({
|
|
349
|
+
market: {
|
|
350
|
+
chainId: this.chainId,
|
|
351
|
+
marketParams: this.marketParams,
|
|
352
|
+
},
|
|
353
|
+
args: {
|
|
354
|
+
assets,
|
|
355
|
+
shares,
|
|
356
|
+
transferAmount,
|
|
357
|
+
withdrawAmount,
|
|
358
|
+
onBehalf: userAddress,
|
|
359
|
+
receiver: userAddress,
|
|
360
|
+
maxSharePrice,
|
|
361
|
+
requirementSignature,
|
|
362
|
+
},
|
|
363
|
+
metadata: this.client.options.metadata,
|
|
364
|
+
}),
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
supplyCollateralBorrow({ amount = 0n, userAddress, positionData, borrowAmount, nativeAmount, slippageTolerance = blue_sdk_1.DEFAULT_SLIPPAGE_TOLERANCE, reallocations, }) {
|
|
368
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
369
|
+
(0, helpers_1.validateUserAddress)(this.client.viemClient.account?.address, userAddress);
|
|
370
|
+
if (amount < 0n) {
|
|
371
|
+
throw new types_1.NonPositiveAssetAmountError(this.marketParams.collateralToken);
|
|
372
|
+
}
|
|
373
|
+
if (nativeAmount !== undefined && nativeAmount < 0n) {
|
|
374
|
+
throw new types_1.NegativeNativeAmountError(nativeAmount);
|
|
375
|
+
}
|
|
376
|
+
if (borrowAmount <= 0n) {
|
|
377
|
+
throw new types_1.NonPositiveBorrowAmountError(this.marketParams.id);
|
|
378
|
+
}
|
|
379
|
+
if (!positionData) {
|
|
380
|
+
throw new types_1.MissingAccrualPositionError(this.marketParams.id);
|
|
381
|
+
}
|
|
382
|
+
(0, helpers_1.validateAccrualPosition)({
|
|
383
|
+
positionData,
|
|
384
|
+
expectedMarketId: this.marketParams.id,
|
|
385
|
+
expectedUser: userAddress,
|
|
386
|
+
});
|
|
387
|
+
const totalCollateral = amount + (nativeAmount ?? 0n);
|
|
388
|
+
if (totalCollateral === 0n) {
|
|
389
|
+
throw new types_1.ZeroCollateralAmountError(this.marketParams.id);
|
|
390
|
+
}
|
|
391
|
+
(0, helpers_1.validateSlippageTolerance)(slippageTolerance);
|
|
392
|
+
if (nativeAmount !== undefined && nativeAmount > 0n) {
|
|
393
|
+
(0, helpers_1.validateNativeCollateral)(this.chainId, this.marketParams.collateralToken);
|
|
394
|
+
}
|
|
395
|
+
(0, helpers_1.validatePositionHealth)({
|
|
396
|
+
positionData,
|
|
397
|
+
additionalCollateral: totalCollateral,
|
|
398
|
+
borrowAmount,
|
|
399
|
+
marketId: this.marketParams.id,
|
|
400
|
+
lltv: this.marketParams.lltv,
|
|
401
|
+
});
|
|
402
|
+
const minSharePrice = (0, helpers_1.computeMinBorrowSharePrice)({
|
|
403
|
+
borrowAmount,
|
|
404
|
+
market: positionData.market,
|
|
405
|
+
slippageTolerance,
|
|
406
|
+
});
|
|
407
|
+
return {
|
|
408
|
+
getRequirements: async (params) => {
|
|
409
|
+
const [erc20Requirements, authTx] = await Promise.all([
|
|
410
|
+
(0, actions_1.getRequirements)(this.client.viemClient, {
|
|
411
|
+
address: this.marketParams.collateralToken,
|
|
412
|
+
chainId: this.chainId,
|
|
413
|
+
supportSignature: this.client.options.supportSignature,
|
|
414
|
+
supportDeployless: this.client.options.supportDeployless,
|
|
415
|
+
useSimplePermit: params?.useSimplePermit,
|
|
416
|
+
args: { amount, from: userAddress },
|
|
417
|
+
}),
|
|
418
|
+
(0, actions_1.getMorphoAuthorizationRequirement)({
|
|
419
|
+
viemClient: this.client.viemClient,
|
|
420
|
+
chainId: this.chainId,
|
|
421
|
+
userAddress,
|
|
422
|
+
}),
|
|
423
|
+
]);
|
|
424
|
+
return [...erc20Requirements, ...(authTx ? [authTx] : [])];
|
|
425
|
+
},
|
|
426
|
+
buildTx: (requirementSignature) => (0, actions_1.marketV1SupplyCollateralBorrow)({
|
|
427
|
+
market: {
|
|
428
|
+
chainId: this.chainId,
|
|
429
|
+
marketParams: this.marketParams,
|
|
430
|
+
},
|
|
431
|
+
args: {
|
|
432
|
+
amount,
|
|
433
|
+
nativeAmount,
|
|
434
|
+
borrowAmount,
|
|
435
|
+
onBehalf: userAddress,
|
|
436
|
+
receiver: userAddress,
|
|
437
|
+
minSharePrice,
|
|
438
|
+
requirementSignature,
|
|
439
|
+
reallocations,
|
|
440
|
+
},
|
|
441
|
+
metadata: this.client.options.metadata,
|
|
442
|
+
}),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
async getReallocationData({ vaultAddresses, market, block, }) {
|
|
446
|
+
(0, helpers_1.validateChainId)(this.client.viemClient.chain?.id, this.chainId);
|
|
447
|
+
if (market.id !== this.marketParams.id) {
|
|
448
|
+
throw new types_1.MarketIdMismatchError(market.id, this.marketParams.id);
|
|
449
|
+
}
|
|
450
|
+
const client = this.client.viemClient;
|
|
451
|
+
const fetchParams = {
|
|
452
|
+
blockNumber: block.number,
|
|
453
|
+
chainId: this.chainId,
|
|
454
|
+
deployless: this.client.options.supportDeployless,
|
|
455
|
+
};
|
|
456
|
+
// Phase 1: Fetch all vaults in parallel to get their withdrawQueues.
|
|
457
|
+
const vaults = await Promise.all(vaultAddresses.map((addr) => (0, blue_sdk_viem_1.fetchVault)(addr, client, fetchParams)));
|
|
458
|
+
// Collect unique market IDs from all vault withdrawQueues + target market.
|
|
459
|
+
const targetMarketId = this.marketParams.id;
|
|
460
|
+
const allMarketIds = new Set([targetMarketId]);
|
|
461
|
+
const vaultMarketPairs = [];
|
|
462
|
+
for (const vault of vaults) {
|
|
463
|
+
// Always include target market pair so its config/position is fetched
|
|
464
|
+
// even when the target market is only in the vault's supplyQueue.
|
|
465
|
+
vaultMarketPairs.push({ vault: vault.address, marketId: targetMarketId });
|
|
466
|
+
for (const mid of vault.withdrawQueue) {
|
|
467
|
+
allMarketIds.add(mid);
|
|
468
|
+
if (mid !== targetMarketId) {
|
|
469
|
+
vaultMarketPairs.push({ vault: vault.address, marketId: mid });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// Phase 2: Fetch all source markets, vault configs, and positions in parallel.
|
|
474
|
+
const sourceMarketIds = [...allMarketIds].filter((mid) => mid !== targetMarketId);
|
|
475
|
+
const loanToken = market.params.loanToken;
|
|
476
|
+
const [markets, configs, positions, holdings] = await Promise.all([
|
|
477
|
+
Promise.all(sourceMarketIds.map((mid) => (0, blue_sdk_viem_1.fetchMarket)(mid, client, fetchParams))),
|
|
478
|
+
Promise.all(vaultMarketPairs.map(({ vault, marketId: mid }) => (0, blue_sdk_viem_1.fetchVaultMarketConfig)(vault, mid, client, fetchParams).then((config) => ({ vault, mid, config })))),
|
|
479
|
+
Promise.all(vaultMarketPairs.map(({ vault, marketId: mid }) => (0, blue_sdk_viem_1.fetchPosition)(vault, mid, client, fetchParams).then((position) => ({
|
|
480
|
+
vault,
|
|
481
|
+
mid,
|
|
482
|
+
position,
|
|
483
|
+
})))),
|
|
484
|
+
Promise.all(vaultAddresses.map((addr) => (0, blue_sdk_viem_1.fetchHolding)(addr, loanToken, client, fetchParams))),
|
|
485
|
+
]);
|
|
486
|
+
// Assemble records for SimulationState.
|
|
487
|
+
const marketsRecord = {
|
|
488
|
+
[targetMarketId]: market,
|
|
489
|
+
};
|
|
490
|
+
for (const m of markets) {
|
|
491
|
+
marketsRecord[m.id] = m;
|
|
492
|
+
}
|
|
493
|
+
const vaultsRecord = {};
|
|
494
|
+
for (const v of vaults) {
|
|
495
|
+
vaultsRecord[v.address] = v;
|
|
496
|
+
}
|
|
497
|
+
const vaultMarketConfigsRecord = {};
|
|
498
|
+
for (const { vault, mid, config } of configs) {
|
|
499
|
+
(vaultMarketConfigsRecord[vault] ??= {})[mid] = config;
|
|
500
|
+
}
|
|
501
|
+
const positionsRecord = {};
|
|
502
|
+
for (const { vault, mid, position } of positions) {
|
|
503
|
+
(positionsRecord[vault] ??= {})[mid] = position;
|
|
504
|
+
}
|
|
505
|
+
const holdingsRecord = {};
|
|
506
|
+
for (const holding of holdings) {
|
|
507
|
+
(holdingsRecord[holding.user] ??= {})[holding.token] = holding;
|
|
508
|
+
}
|
|
509
|
+
return new simulation_sdk_1.SimulationState({
|
|
510
|
+
chainId: this.chainId,
|
|
511
|
+
block,
|
|
512
|
+
markets: marketsRecord,
|
|
513
|
+
vaults: vaultsRecord,
|
|
514
|
+
vaultMarketConfigs: vaultMarketConfigsRecord,
|
|
515
|
+
positions: positionsRecord,
|
|
516
|
+
holdings: holdingsRecord,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
getReallocations({ reallocationData, borrowAmount, options, }) {
|
|
520
|
+
return (0, helpers_1.computeReallocations)({
|
|
521
|
+
reallocationData,
|
|
522
|
+
marketId: this.marketParams.id,
|
|
523
|
+
borrowAmount,
|
|
524
|
+
options: { enabled: true, ...options },
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
exports.MorphoMarketV1 = MorphoMarketV1;
|
|
@@ -17,6 +17,10 @@ class MorphoVaultV1 {
|
|
|
17
17
|
this.chainId = chainId;
|
|
18
18
|
}
|
|
19
19
|
async getData(parameters) {
|
|
20
|
+
if (this.client.viemClient.chain?.id &&
|
|
21
|
+
this.client.viemClient.chain?.id !== this.chainId) {
|
|
22
|
+
throw new types_1.ChainIdMismatchError(this.client.viemClient.chain?.id, this.chainId);
|
|
23
|
+
}
|
|
20
24
|
return (0, blue_sdk_viem_1.fetchAccrualVault)(this.vault, this.client.viemClient, {
|
|
21
25
|
...parameters,
|
|
22
26
|
chainId: this.chainId,
|
|
@@ -17,6 +17,10 @@ class MorphoVaultV2 {
|
|
|
17
17
|
this.chainId = chainId;
|
|
18
18
|
}
|
|
19
19
|
async getData(parameters) {
|
|
20
|
+
if (this.client.viemClient.chain?.id &&
|
|
21
|
+
this.client.viemClient.chain?.id !== this.chainId) {
|
|
22
|
+
throw new types_1.ChainIdMismatchError(this.client.viemClient.chain?.id, this.chainId);
|
|
23
|
+
}
|
|
20
24
|
return (0, blue_sdk_viem_1.fetchAccrualVaultV2)(this.vault, this.client.viemClient, {
|
|
21
25
|
...parameters,
|
|
22
26
|
chainId: this.chainId,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type MarketId } from "@morpho-org/blue-sdk";
|
|
2
|
+
import type { SimulationState } from "@morpho-org/simulation-sdk";
|
|
3
|
+
import { type ReallocationComputeOptions, type VaultReallocation } from "../types";
|
|
4
|
+
/**
|
|
5
|
+
* Computes vault reallocations for a borrow operation on a target market.
|
|
6
|
+
*
|
|
7
|
+
* Replicates the shared liquidity algorithm from `populateSubBundle` in
|
|
8
|
+
* `@morpho-org/bundler-sdk-viem`. First attempts "friendly" reallocations
|
|
9
|
+
* respecting withdrawal utilization targets, then falls back to aggressive
|
|
10
|
+
* reallocations (100% withdrawal utilization) if liquidity is still insufficient.
|
|
11
|
+
*
|
|
12
|
+
* @param params.reallocationData - The simulation state containing market, vault, and position data.
|
|
13
|
+
* @param params.marketId - The target market to reallocate liquidity into.
|
|
14
|
+
* @param params.borrowAmount - The intended borrow amount (used to compute post-borrow utilization).
|
|
15
|
+
* @param params.options - Optional reallocation computation options.
|
|
16
|
+
* @returns Array of vault reallocations, sorted with withdrawals in ascending market id order.
|
|
17
|
+
*/
|
|
18
|
+
export declare const computeReallocations: ({ reallocationData: data, marketId, borrowAmount, options, }: {
|
|
19
|
+
readonly reallocationData: SimulationState;
|
|
20
|
+
readonly marketId: MarketId;
|
|
21
|
+
readonly borrowAmount: bigint;
|
|
22
|
+
readonly options?: ReallocationComputeOptions;
|
|
23
|
+
}) => readonly VaultReallocation[];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.computeReallocations = void 0;
|
|
4
|
+
const blue_sdk_1 = require("@morpho-org/blue-sdk");
|
|
5
|
+
const bundler_sdk_viem_1 = require("@morpho-org/bundler-sdk-viem");
|
|
6
|
+
const types_1 = require("../types");
|
|
7
|
+
/**
|
|
8
|
+
* Computes vault reallocations for a borrow operation on a target market.
|
|
9
|
+
*
|
|
10
|
+
* Replicates the shared liquidity algorithm from `populateSubBundle` in
|
|
11
|
+
* `@morpho-org/bundler-sdk-viem`. First attempts "friendly" reallocations
|
|
12
|
+
* respecting withdrawal utilization targets, then falls back to aggressive
|
|
13
|
+
* reallocations (100% withdrawal utilization) if liquidity is still insufficient.
|
|
14
|
+
*
|
|
15
|
+
* @param params.reallocationData - The simulation state containing market, vault, and position data.
|
|
16
|
+
* @param params.marketId - The target market to reallocate liquidity into.
|
|
17
|
+
* @param params.borrowAmount - The intended borrow amount (used to compute post-borrow utilization).
|
|
18
|
+
* @param params.options - Optional reallocation computation options.
|
|
19
|
+
* @returns Array of vault reallocations, sorted with withdrawals in ascending market id order.
|
|
20
|
+
*/
|
|
21
|
+
const computeReallocations = ({ reallocationData: data, marketId, borrowAmount, options, }) => {
|
|
22
|
+
if (options?.enabled === false)
|
|
23
|
+
return [];
|
|
24
|
+
const market = data.getMarket(marketId).accrueInterest(data.block.timestamp);
|
|
25
|
+
const newTotalBorrowAssets = market.totalBorrowAssets + borrowAmount;
|
|
26
|
+
const newTotalSupplyAssets = market.totalSupplyAssets;
|
|
27
|
+
const supplyTargetUtilization = options?.supplyTargetUtilization?.[market.params.id] ??
|
|
28
|
+
options?.defaultSupplyTargetUtilization ??
|
|
29
|
+
bundler_sdk_viem_1.DEFAULT_SUPPLY_TARGET_UTILIZATION;
|
|
30
|
+
if (blue_sdk_1.MarketUtils.getUtilization({
|
|
31
|
+
totalSupplyAssets: newTotalSupplyAssets,
|
|
32
|
+
totalBorrowAssets: newTotalBorrowAssets,
|
|
33
|
+
}) <= supplyTargetUtilization)
|
|
34
|
+
return [];
|
|
35
|
+
// Solve: newTotalBorrowAssets / (newTotalSupplyAssets + reallocatedAssets) = supplyTargetUtilization
|
|
36
|
+
let requiredAssets = supplyTargetUtilization === 0n
|
|
37
|
+
? blue_sdk_1.MathLib.MAX_UINT_160
|
|
38
|
+
: blue_sdk_1.MathLib.wDivDown(newTotalBorrowAssets, supplyTargetUtilization) -
|
|
39
|
+
newTotalSupplyAssets;
|
|
40
|
+
// Phase 1: "friendly" reallocations respecting withdrawal utilization targets.
|
|
41
|
+
const { withdrawals: friendlyWithdrawals, data: friendlyReallocationData } = data.getMarketPublicReallocations(market.id, options);
|
|
42
|
+
const withdrawals = [...friendlyWithdrawals];
|
|
43
|
+
const friendlyReallocationMarket = friendlyReallocationData.getMarket(market.id);
|
|
44
|
+
if (friendlyReallocationMarket.totalBorrowAssets + borrowAmount >
|
|
45
|
+
friendlyReallocationMarket.totalSupplyAssets) {
|
|
46
|
+
// Phase 2: "aggressive" — fully withdraw from every market (100% utilization).
|
|
47
|
+
requiredAssets = newTotalBorrowAssets - newTotalSupplyAssets;
|
|
48
|
+
withdrawals.push(...friendlyReallocationData.getMarketPublicReallocations(market.id, {
|
|
49
|
+
...options,
|
|
50
|
+
defaultMaxWithdrawalUtilization: blue_sdk_1.MathLib.WAD,
|
|
51
|
+
maxWithdrawalUtilization: {},
|
|
52
|
+
}).withdrawals);
|
|
53
|
+
}
|
|
54
|
+
if (requiredAssets <= 0n)
|
|
55
|
+
return [];
|
|
56
|
+
// Group withdrawals by vault, capping total at requiredAssets.
|
|
57
|
+
const reallocationsMap = {};
|
|
58
|
+
for (const { vault, ...withdrawal } of withdrawals) {
|
|
59
|
+
const vaultReallocations = (reallocationsMap[vault] ??= []);
|
|
60
|
+
const existing = vaultReallocations.find((item) => item.id === withdrawal.id);
|
|
61
|
+
const reallocatedAssets = blue_sdk_1.MathLib.min(withdrawal.assets, requiredAssets);
|
|
62
|
+
if (reallocatedAssets <= 0n)
|
|
63
|
+
continue;
|
|
64
|
+
if (existing != null) {
|
|
65
|
+
existing.assets += reallocatedAssets;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
vaultReallocations.push({
|
|
69
|
+
...withdrawal,
|
|
70
|
+
assets: reallocatedAssets,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
requiredAssets -= reallocatedAssets;
|
|
74
|
+
if (requiredAssets === 0n)
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
// Transform into VaultReallocation[] format.
|
|
78
|
+
return Object.entries(reallocationsMap)
|
|
79
|
+
.filter(([, vaultWithdrawals]) => vaultWithdrawals.length > 0)
|
|
80
|
+
.map(([vault, vaultWithdrawals]) => ({
|
|
81
|
+
vault: vault,
|
|
82
|
+
fee: (() => {
|
|
83
|
+
const config = data.getVault(vault).publicAllocatorConfig;
|
|
84
|
+
if (config == null) {
|
|
85
|
+
throw new types_1.MissingPublicAllocatorConfigError(vault);
|
|
86
|
+
}
|
|
87
|
+
return config.fee;
|
|
88
|
+
})(),
|
|
89
|
+
withdrawals: vaultWithdrawals
|
|
90
|
+
// Reallocation withdrawals must be sorted by market id in ascending order.
|
|
91
|
+
.sort(({ id: idA }, { id: idB }) => idA > idB ? 1 : idA < idB ? -1 : 0)
|
|
92
|
+
.map(({ id, assets }) => ({
|
|
93
|
+
marketParams: data.getMarket(id).params,
|
|
94
|
+
amount: assets,
|
|
95
|
+
})),
|
|
96
|
+
}));
|
|
97
|
+
};
|
|
98
|
+
exports.computeReallocations = computeReallocations;
|
|
@@ -1 +1,6 @@
|
|
|
1
|
+
/** Maximum slippage tolerance: 10% */
|
|
1
2
|
export declare const MAX_SLIPPAGE_TOLERANCE: bigint;
|
|
3
|
+
/** Default LLTV buffer: 0.5% below LLTV. Prevents instant liquidation on new positions. */
|
|
4
|
+
export declare const DEFAULT_LLTV_BUFFER: bigint;
|
|
5
|
+
/** Maximum absolute share price cap (100 RAY). Prevents absurd maxSharePrice values in repay. */
|
|
6
|
+
export declare const MAX_ABSOLUTE_SHARE_PRICE: bigint;
|