@gearbox-protocol/sdk 14.1.1 → 14.2.1
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/dist/cjs/dev/claimDSToken.js +208 -0
- package/dist/cjs/plugins/accounts/AccountsPlugin.js +1 -2
- package/dist/cjs/sdk/OnchainSDK.js +12 -0
- package/dist/cjs/sdk/accounts/CreditAccountsServiceV310.js +1448 -23
- package/dist/cjs/sdk/accounts/index.js +0 -4
- package/dist/cjs/sdk/market/credit/CreditFacadeV310Contract.js +6 -0
- package/dist/cjs/sdk/market/kyc/securitize/SecuritizeKYCFactory.js +19 -2
- package/dist/cjs/sdk/market/kyc/securitize/types.js +20 -0
- package/dist/cjs/sdk/market/oracle/PriceOracleV310Contract.js +26 -0
- package/dist/cjs/sdk/market/pricefeeds/PriceFeedsRegister.js +3 -0
- package/dist/esm/dev/claimDSToken.js +187 -0
- package/dist/esm/plugins/accounts/AccountsPlugin.js +2 -7
- package/dist/esm/sdk/OnchainSDK.js +14 -0
- package/dist/esm/sdk/accounts/CreditAccountsServiceV310.js +1466 -23
- package/dist/esm/sdk/accounts/index.js +0 -2
- package/dist/esm/sdk/accounts/multicall-utils.js +1 -5
- package/dist/esm/sdk/market/credit/CreditFacadeV310Contract.js +6 -0
- package/dist/esm/sdk/market/kyc/securitize/SecuritizeKYCFactory.js +21 -2
- package/dist/esm/sdk/market/kyc/securitize/types.js +12 -0
- package/dist/esm/sdk/market/oracle/PriceOracleV310Contract.js +26 -0
- package/dist/esm/sdk/market/pricefeeds/PriceFeedsRegister.js +3 -0
- package/dist/types/dev/claimDSToken.d.ts +34 -0
- package/dist/types/sdk/OnchainSDK.d.ts +10 -0
- package/dist/types/sdk/accounts/CreditAccountsServiceV310.d.ts +181 -3
- package/dist/types/sdk/accounts/index.d.ts +0 -2
- package/dist/types/sdk/accounts/types.d.ts +56 -0
- package/dist/types/sdk/market/credit/CreditFacadeV310Contract.d.ts +2 -0
- package/dist/types/sdk/market/kyc/securitize/SecuritizeKYCFactory.d.ts +1 -1
- package/dist/types/sdk/market/kyc/securitize/types.d.ts +37 -22
- package/dist/types/sdk/market/oracle/PriceOracleBaseContract.d.ts +5 -0
- package/dist/types/sdk/market/oracle/PriceOracleV310Contract.d.ts +4 -0
- package/dist/types/sdk/market/oracle/types.d.ts +9 -0
- package/dist/types/sdk/pools/types.d.ts +19 -0
- package/package.json +1 -1
- package/dist/cjs/sdk/accounts/AbstractCreditAccountsService.js +0 -1411
- package/dist/cjs/sdk/accounts/createCreditAccountService.js +0 -35
- package/dist/esm/sdk/accounts/AbstractCreditAccountsService.js +0 -1405
- package/dist/esm/sdk/accounts/createCreditAccountService.js +0 -11
- package/dist/types/sdk/accounts/AbstractCreditAccountsService.d.ts +0 -226
- package/dist/types/sdk/accounts/createCreditAccountService.d.ts +0 -9
|
@@ -18,14 +18,1107 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var CreditAccountsServiceV310_exports = {};
|
|
20
20
|
__export(CreditAccountsServiceV310_exports, {
|
|
21
|
-
|
|
21
|
+
CreditAccountsServiceV310: () => CreditAccountsServiceV310,
|
|
22
|
+
getWithdrawalCompressorAddress: () => getWithdrawalCompressorAddress
|
|
22
23
|
});
|
|
23
24
|
module.exports = __toCommonJS(CreditAccountsServiceV310_exports);
|
|
25
|
+
var import_integrations_v3 = require("@gearbox-protocol/integrations-v3");
|
|
24
26
|
var import_viem = require("viem");
|
|
25
27
|
var import_generated = require("../../abi/310/generated.js");
|
|
26
|
-
var
|
|
27
|
-
var
|
|
28
|
-
|
|
28
|
+
var import_creditAccountCompressor = require("../../abi/compressors/creditAccountCompressor.js");
|
|
29
|
+
var import_peripheryCompressor = require("../../abi/compressors/peripheryCompressor.js");
|
|
30
|
+
var import_rewardsCompressor = require("../../abi/compressors/rewardsCompressor.js");
|
|
31
|
+
var import_IWithdrawalCompressorV310 = require("../../abi/IWithdrawalCompressorV310.js");
|
|
32
|
+
var import_iBaseRewardPool = require("../../abi/iBaseRewardPool.js");
|
|
33
|
+
var import_iKYCFactory = require("../../abi/kyc/iKYCFactory.js");
|
|
34
|
+
var import_base = require("../base/index.js");
|
|
35
|
+
var import_chains = require("../chain/chains.js");
|
|
36
|
+
var import_constants = require("../constants/index.js");
|
|
37
|
+
var import_market = require("../market/index.js");
|
|
38
|
+
var import_router = require("../router/index.js");
|
|
39
|
+
var import_utils = require("../utils/index.js");
|
|
40
|
+
var import_viem2 = require("../utils/viem/index.js");
|
|
41
|
+
var import_multicall_utils = require("./multicall-utils.js");
|
|
42
|
+
const COMPRESSORS = {
|
|
43
|
+
[import_chains.chains.Mainnet.id]: "0x36F3d0Bb73CBC2E94fE24dF0f26a689409cF9023",
|
|
44
|
+
[import_chains.chains.Monad.id]: "0x36F3d0Bb73CBC2E94fE24dF0f26a689409cF9023"
|
|
45
|
+
};
|
|
46
|
+
function getWithdrawalCompressorAddress(chainId) {
|
|
47
|
+
return COMPRESSORS[chainId];
|
|
48
|
+
}
|
|
49
|
+
class CreditAccountsServiceV310 extends import_base.SDKConstruct {
|
|
50
|
+
#compressor;
|
|
51
|
+
#batchSize;
|
|
52
|
+
constructor(sdk, options) {
|
|
53
|
+
super(sdk);
|
|
54
|
+
this.#batchSize = options?.batchSize;
|
|
55
|
+
}
|
|
56
|
+
setBatchSize(batchSize) {
|
|
57
|
+
this.#batchSize = batchSize;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* {@inheritDoc ICreditAccountsService.getCreditAccountData}
|
|
61
|
+
**/
|
|
62
|
+
async getCreditAccountData(account, blockNumber) {
|
|
63
|
+
let raw;
|
|
64
|
+
try {
|
|
65
|
+
raw = await this.client.readContract({
|
|
66
|
+
abi: import_creditAccountCompressor.creditAccountCompressorAbi,
|
|
67
|
+
address: this.compressor,
|
|
68
|
+
functionName: "getCreditAccountData",
|
|
69
|
+
args: [account],
|
|
70
|
+
blockNumber,
|
|
71
|
+
// @ts-expect-error
|
|
72
|
+
gas: this.sdk.gasLimit
|
|
73
|
+
});
|
|
74
|
+
} catch (_e) {
|
|
75
|
+
return void 0;
|
|
76
|
+
}
|
|
77
|
+
const marketSuite = this.sdk.marketRegister.findByCreditManager(
|
|
78
|
+
raw.creditManager
|
|
79
|
+
);
|
|
80
|
+
const factory = marketSuite.kycFactory;
|
|
81
|
+
let ca;
|
|
82
|
+
let investor;
|
|
83
|
+
if (raw.success) {
|
|
84
|
+
ca = raw;
|
|
85
|
+
investor = await factory?.getInvestor(raw.creditAccount, false);
|
|
86
|
+
} else {
|
|
87
|
+
const { txs: priceUpdateTxs } = await this.#getUpdateForAccount(raw);
|
|
88
|
+
[ca, investor] = await (0, import_viem2.simulateWithPriceUpdates)(this.client, {
|
|
89
|
+
priceUpdates: priceUpdateTxs,
|
|
90
|
+
contracts: [
|
|
91
|
+
{
|
|
92
|
+
abi: import_creditAccountCompressor.creditAccountCompressorAbi,
|
|
93
|
+
address: this.compressor,
|
|
94
|
+
functionName: "getCreditAccountData",
|
|
95
|
+
args: [account]
|
|
96
|
+
},
|
|
97
|
+
...factory ? [
|
|
98
|
+
{
|
|
99
|
+
abi: import_iKYCFactory.iKYCFactoryAbi,
|
|
100
|
+
address: factory.address,
|
|
101
|
+
functionName: "getInvestor",
|
|
102
|
+
args: [raw.creditAccount]
|
|
103
|
+
}
|
|
104
|
+
] : []
|
|
105
|
+
],
|
|
106
|
+
blockNumber,
|
|
107
|
+
gas: this.sdk.gasLimit
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return { ...ca, investor };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* {@inheritDoc ICreditAccountsService.getCreditAccounts}
|
|
114
|
+
**/
|
|
115
|
+
async getCreditAccounts(options, blockNumber) {
|
|
116
|
+
const {
|
|
117
|
+
creditManager,
|
|
118
|
+
includeZeroDebt = false,
|
|
119
|
+
maxHealthFactor = import_constants.MAX_UINT256,
|
|
120
|
+
minHealthFactor = 0n,
|
|
121
|
+
owner = import_constants.ADDRESS_0X0,
|
|
122
|
+
ignoreReservePrices = false
|
|
123
|
+
} = options ?? {};
|
|
124
|
+
const arg0 = creditManager ?? {
|
|
125
|
+
configurators: this.marketConfigurators,
|
|
126
|
+
creditManagers: [],
|
|
127
|
+
pools: [],
|
|
128
|
+
underlying: import_constants.ADDRESS_0X0
|
|
129
|
+
};
|
|
130
|
+
const caFilter = {
|
|
131
|
+
owner,
|
|
132
|
+
includeZeroDebt,
|
|
133
|
+
minHealthFactor,
|
|
134
|
+
maxHealthFactor,
|
|
135
|
+
reverting: false
|
|
136
|
+
};
|
|
137
|
+
const { txs: priceUpdateTxs } = await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs(
|
|
138
|
+
ignoreReservePrices ? { main: true } : void 0
|
|
139
|
+
);
|
|
140
|
+
const allCAs = [];
|
|
141
|
+
let revertingOffset = 0;
|
|
142
|
+
for (const reverting of [false, true]) {
|
|
143
|
+
let offset = 0n;
|
|
144
|
+
revertingOffset = allCAs.length;
|
|
145
|
+
do {
|
|
146
|
+
const [accounts, newOffset] = await this.#getCreditAccounts(
|
|
147
|
+
this.#batchSize ? [
|
|
148
|
+
arg0,
|
|
149
|
+
{ ...caFilter, reverting },
|
|
150
|
+
offset,
|
|
151
|
+
BigInt(this.#batchSize)
|
|
152
|
+
// limit
|
|
153
|
+
] : [arg0, { ...caFilter, reverting }, offset],
|
|
154
|
+
priceUpdateTxs,
|
|
155
|
+
blockNumber
|
|
156
|
+
);
|
|
157
|
+
allCAs.push(...accounts);
|
|
158
|
+
offset = newOffset;
|
|
159
|
+
} while (offset !== 0n);
|
|
160
|
+
}
|
|
161
|
+
this.logger?.debug(
|
|
162
|
+
`loaded ${allCAs.length} credit accounts (${allCAs.length - revertingOffset} reverting)`
|
|
163
|
+
);
|
|
164
|
+
return allCAs.sort((a, b) => Number(a.healthFactor - b.healthFactor));
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* {@inheritDoc ICreditAccountsService.getBorrowerCreditAccounts}
|
|
168
|
+
**/
|
|
169
|
+
async getBorrowerCreditAccounts(borrower, options, blockNumber) {
|
|
170
|
+
const {
|
|
171
|
+
creditManager,
|
|
172
|
+
includeZeroDebt = false,
|
|
173
|
+
maxHealthFactor = import_constants.MAX_UINT256,
|
|
174
|
+
minHealthFactor = 0n,
|
|
175
|
+
ignoreReservePrices = false
|
|
176
|
+
} = options ?? {};
|
|
177
|
+
const { txs: priceUpdateTxs } = await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs(
|
|
178
|
+
ignoreReservePrices ? { main: true } : void 0
|
|
179
|
+
);
|
|
180
|
+
const investorDataList = await this.sdk.kyc.getInvestorData(borrower);
|
|
181
|
+
const kycAccountAddresses = investorDataList.flatMap(
|
|
182
|
+
(d) => d.creditAccounts.map((ca) => ca.creditAccount)
|
|
183
|
+
);
|
|
184
|
+
const cmFilter = creditManager ? {
|
|
185
|
+
configurators: [],
|
|
186
|
+
creditManagers: [creditManager],
|
|
187
|
+
pools: [],
|
|
188
|
+
underlying: import_constants.ADDRESS_0X0
|
|
189
|
+
} : {
|
|
190
|
+
configurators: this.marketConfigurators,
|
|
191
|
+
creditManagers: [],
|
|
192
|
+
pools: [],
|
|
193
|
+
underlying: import_constants.ADDRESS_0X0
|
|
194
|
+
};
|
|
195
|
+
const permissiveFilter = {
|
|
196
|
+
owner: borrower,
|
|
197
|
+
includeZeroDebt: true,
|
|
198
|
+
minHealthFactor: 0n,
|
|
199
|
+
maxHealthFactor: import_constants.MAX_UINT256,
|
|
200
|
+
reverting: false
|
|
201
|
+
};
|
|
202
|
+
const kycContracts = kycAccountAddresses.map(
|
|
203
|
+
(account) => ({
|
|
204
|
+
abi: import_creditAccountCompressor.creditAccountCompressorAbi,
|
|
205
|
+
address: this.compressor,
|
|
206
|
+
functionName: "getCreditAccountData",
|
|
207
|
+
args: [account]
|
|
208
|
+
})
|
|
209
|
+
);
|
|
210
|
+
const getCreditAccountsContracts = [false, true].map(
|
|
211
|
+
(reverting) => ({
|
|
212
|
+
abi: import_creditAccountCompressor.creditAccountCompressorAbi,
|
|
213
|
+
address: this.compressor,
|
|
214
|
+
functionName: "getCreditAccounts",
|
|
215
|
+
args: [cmFilter, { ...permissiveFilter, reverting }, 0n]
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
const allContracts = [...kycContracts, ...getCreditAccountsContracts];
|
|
219
|
+
const results = await (0, import_viem2.simulateWithPriceUpdates)(this.client, {
|
|
220
|
+
priceUpdates: priceUpdateTxs,
|
|
221
|
+
contracts: allContracts,
|
|
222
|
+
blockNumber,
|
|
223
|
+
gas: this.sdk.gasLimit
|
|
224
|
+
});
|
|
225
|
+
const kycResults = results.slice(
|
|
226
|
+
0,
|
|
227
|
+
kycAccountAddresses.length
|
|
228
|
+
);
|
|
229
|
+
const normalResults = results.slice(kycAccountAddresses.length);
|
|
230
|
+
const seen = new import_utils.AddressSet();
|
|
231
|
+
const allCAs = [];
|
|
232
|
+
for (const ca of kycResults) {
|
|
233
|
+
if (!seen.has(ca.creditAccount)) {
|
|
234
|
+
seen.add(ca.creditAccount);
|
|
235
|
+
allCAs.push({ ...ca, investor: borrower });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
for (const [accounts] of normalResults) {
|
|
239
|
+
for (const ca of accounts) {
|
|
240
|
+
if (!seen.has(ca.creditAccount)) {
|
|
241
|
+
seen.add(ca.creditAccount);
|
|
242
|
+
allCAs.push({ ...ca, investor: void 0 });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const filtered = allCAs.filter((ca) => {
|
|
247
|
+
if (!includeZeroDebt && ca.debt === 0n) return false;
|
|
248
|
+
if (ca.healthFactor < minHealthFactor) return false;
|
|
249
|
+
if (ca.healthFactor > maxHealthFactor) return false;
|
|
250
|
+
if (creditManager && !(0, import_utils.hexEq)(ca.creditManager, creditManager))
|
|
251
|
+
return false;
|
|
252
|
+
return true;
|
|
253
|
+
});
|
|
254
|
+
this.logger?.debug(
|
|
255
|
+
`loaded ${allCAs.length} borrower credit accounts (${kycResults.length} KYC, ${filtered.length} after filter)`
|
|
256
|
+
);
|
|
257
|
+
return filtered.sort((a, b) => Number(a.healthFactor - b.healthFactor));
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* {@inheritDoc ICreditAccountsService.getRewards}
|
|
261
|
+
**/
|
|
262
|
+
async getRewards(creditAccount) {
|
|
263
|
+
const rewards = await this.client.readContract({
|
|
264
|
+
abi: import_rewardsCompressor.rewardsCompressorAbi,
|
|
265
|
+
address: this.rewardCompressor,
|
|
266
|
+
functionName: "getRewards",
|
|
267
|
+
args: [creditAccount]
|
|
268
|
+
});
|
|
269
|
+
const callData = (0, import_viem.encodeFunctionData)({
|
|
270
|
+
abi: import_iBaseRewardPool.iBaseRewardPoolAbi,
|
|
271
|
+
functionName: "getReward",
|
|
272
|
+
args: []
|
|
273
|
+
});
|
|
274
|
+
const r = rewards.reduce((acc, r2) => {
|
|
275
|
+
const adapter = r2.adapter.toLowerCase();
|
|
276
|
+
const stakedPhantomToken = r2.stakedPhantomToken.toLowerCase();
|
|
277
|
+
const rewardToken = r2.rewardToken.toLowerCase();
|
|
278
|
+
const key = [adapter, stakedPhantomToken].join("-");
|
|
279
|
+
if (!acc[key]) {
|
|
280
|
+
acc[key] = {
|
|
281
|
+
adapter,
|
|
282
|
+
stakedPhantomToken,
|
|
283
|
+
calls: [
|
|
284
|
+
{
|
|
285
|
+
target: adapter,
|
|
286
|
+
callData
|
|
287
|
+
}
|
|
288
|
+
],
|
|
289
|
+
rewards: []
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
acc[key].rewards.push({
|
|
293
|
+
token: rewardToken,
|
|
294
|
+
balance: r2.amount
|
|
295
|
+
});
|
|
296
|
+
return acc;
|
|
297
|
+
}, {});
|
|
298
|
+
return Object.values(r);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* {@inheritDoc ICreditAccountsService.getConnectedBots}
|
|
302
|
+
**/
|
|
303
|
+
async getConnectedBots(accountsToCheck, legacyMigrationBot, additionalBots) {
|
|
304
|
+
const allResp = await this.client.multicall({
|
|
305
|
+
contracts: [
|
|
306
|
+
...accountsToCheck.map((o) => {
|
|
307
|
+
const pool = this.sdk.marketRegister.findByCreditManager(
|
|
308
|
+
o.creditManager
|
|
309
|
+
);
|
|
310
|
+
return {
|
|
311
|
+
abi: import_peripheryCompressor.peripheryCompressorAbi,
|
|
312
|
+
address: this.peripheryCompressor,
|
|
313
|
+
functionName: "getConnectedBots",
|
|
314
|
+
args: [pool.configurator.address, o.creditAccount]
|
|
315
|
+
};
|
|
316
|
+
}),
|
|
317
|
+
...legacyMigrationBot ? accountsToCheck.map((ca) => {
|
|
318
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
319
|
+
ca.creditManager
|
|
320
|
+
);
|
|
321
|
+
return {
|
|
322
|
+
abi: import_generated.iBotListV310Abi,
|
|
323
|
+
address: cm.creditFacade.botList,
|
|
324
|
+
functionName: "getBotStatus",
|
|
325
|
+
args: [legacyMigrationBot, ca.creditAccount]
|
|
326
|
+
};
|
|
327
|
+
}) : [],
|
|
328
|
+
...accountsToCheck.flatMap((ca) => {
|
|
329
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
330
|
+
ca.creditManager
|
|
331
|
+
);
|
|
332
|
+
return additionalBots.map((bot) => {
|
|
333
|
+
return {
|
|
334
|
+
abi: import_generated.iBotListV310Abi,
|
|
335
|
+
address: cm.creditFacade.botList,
|
|
336
|
+
functionName: "getBotStatus",
|
|
337
|
+
args: [bot, ca.creditAccount]
|
|
338
|
+
};
|
|
339
|
+
});
|
|
340
|
+
})
|
|
341
|
+
],
|
|
342
|
+
allowFailure: true,
|
|
343
|
+
batchSize: 0
|
|
344
|
+
});
|
|
345
|
+
const legacyStart = 0;
|
|
346
|
+
const legacyEnd = accountsToCheck.length;
|
|
347
|
+
const legacy = allResp.slice(
|
|
348
|
+
legacyStart,
|
|
349
|
+
legacyEnd
|
|
350
|
+
);
|
|
351
|
+
const migrationStart = legacyEnd;
|
|
352
|
+
const migrationEnd = legacyMigrationBot ? migrationStart + accountsToCheck.length : migrationStart;
|
|
353
|
+
const migrationResp = allResp.slice(
|
|
354
|
+
migrationStart,
|
|
355
|
+
migrationEnd
|
|
356
|
+
);
|
|
357
|
+
const additionalStart = migrationEnd;
|
|
358
|
+
const additionalResp = allResp.slice(
|
|
359
|
+
additionalStart
|
|
360
|
+
);
|
|
361
|
+
return {
|
|
362
|
+
legacy,
|
|
363
|
+
additionalBots: this.#getActiveBots(
|
|
364
|
+
accountsToCheck,
|
|
365
|
+
additionalBots,
|
|
366
|
+
additionalResp
|
|
367
|
+
),
|
|
368
|
+
legacyMigration: this.#getActiveMigrationBots(
|
|
369
|
+
accountsToCheck,
|
|
370
|
+
legacyMigrationBot,
|
|
371
|
+
migrationResp
|
|
372
|
+
)
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
#getActiveBots(accountsToCheck, bots, result) {
|
|
376
|
+
if (result.length !== bots.length * accountsToCheck.length) {
|
|
377
|
+
console.error(
|
|
378
|
+
"result length mismatch",
|
|
379
|
+
result.length,
|
|
380
|
+
bots.length * accountsToCheck.length
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
const botsByCAIndex = accountsToCheck.reduce((acc, _, index) => {
|
|
384
|
+
const r = result.slice(index * bots.length, (index + 1) * bots.length);
|
|
385
|
+
acc.push({
|
|
386
|
+
result: r
|
|
387
|
+
});
|
|
388
|
+
return acc;
|
|
389
|
+
}, []);
|
|
390
|
+
return botsByCAIndex;
|
|
391
|
+
}
|
|
392
|
+
#getActiveMigrationBots(accountsToCheck, bot, result) {
|
|
393
|
+
if (bot) {
|
|
394
|
+
if (result.length !== accountsToCheck.length) {
|
|
395
|
+
console.error(
|
|
396
|
+
"result length mismatch for migration bots",
|
|
397
|
+
result.length,
|
|
398
|
+
accountsToCheck.length
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
return { result, botAddress: bot };
|
|
402
|
+
}
|
|
403
|
+
return void 0;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* {@inheritDoc ICreditAccountsService.fullyLiquidate}
|
|
407
|
+
**/
|
|
408
|
+
async fullyLiquidate(props) {
|
|
409
|
+
const {
|
|
410
|
+
account,
|
|
411
|
+
to,
|
|
412
|
+
slippage = 50n,
|
|
413
|
+
keepAssets,
|
|
414
|
+
ignoreReservePrices,
|
|
415
|
+
applyLossPolicy,
|
|
416
|
+
debtOnly
|
|
417
|
+
} = props;
|
|
418
|
+
const cm = this.sdk.marketRegister.findCreditManager(account.creditManager);
|
|
419
|
+
const routerCloseResult = await this.sdk.routerFor(account).findBestClosePath({
|
|
420
|
+
creditAccount: account,
|
|
421
|
+
creditManager: cm.creditManager,
|
|
422
|
+
slippage,
|
|
423
|
+
keepAssets,
|
|
424
|
+
debtOnly
|
|
425
|
+
});
|
|
426
|
+
const calls = await this.#prependPriceUpdates(
|
|
427
|
+
account.creditManager,
|
|
428
|
+
routerCloseResult.calls,
|
|
429
|
+
account,
|
|
430
|
+
{ ignoreReservePrices }
|
|
431
|
+
);
|
|
432
|
+
let lossPolicyData;
|
|
433
|
+
if (applyLossPolicy) {
|
|
434
|
+
const market = this.sdk.marketRegister.findByCreditManager(
|
|
435
|
+
account.creditManager
|
|
436
|
+
);
|
|
437
|
+
lossPolicyData = await market.lossPolicy.getLiquidationData(
|
|
438
|
+
account.creditAccount
|
|
439
|
+
);
|
|
440
|
+
this.logger?.debug({ lossPolicyData }, "loss policy data");
|
|
441
|
+
}
|
|
442
|
+
const tx = cm.creditFacade.liquidateCreditAccount(
|
|
443
|
+
account.creditAccount,
|
|
444
|
+
to,
|
|
445
|
+
calls,
|
|
446
|
+
lossPolicyData
|
|
447
|
+
);
|
|
448
|
+
return {
|
|
449
|
+
tx,
|
|
450
|
+
calls,
|
|
451
|
+
routerCloseResult,
|
|
452
|
+
lossPolicyData,
|
|
453
|
+
creditFacade: cm.creditFacade
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* {@inheritDoc ICreditAccountsService.calcMinSeizedAmount}
|
|
458
|
+
*/
|
|
459
|
+
calcMinSeizedAmount(props) {
|
|
460
|
+
const { account, token, repaidAmount } = props;
|
|
461
|
+
const market = this.sdk.marketRegister.findByCreditManager(
|
|
462
|
+
account.creditManager
|
|
463
|
+
);
|
|
464
|
+
const suite = this.sdk.marketRegister.findCreditManager(
|
|
465
|
+
account.creditManager
|
|
466
|
+
);
|
|
467
|
+
const fee = suite.isExpired ? suite.creditManager.liquidationDiscountExpired : suite.creditManager.liquidationDiscount;
|
|
468
|
+
const tokenAmount = market.priceOracle.convert(
|
|
469
|
+
market.underlying,
|
|
470
|
+
token,
|
|
471
|
+
repaidAmount
|
|
472
|
+
);
|
|
473
|
+
return tokenAmount * 9990n / BigInt(fee);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* {@inheritDoc ICreditAccountsService.partiallyLiquidate}
|
|
477
|
+
*/
|
|
478
|
+
async partiallyLiquidate(props) {
|
|
479
|
+
const { account, token, repaidAmount, minSeizedAmount, to } = props;
|
|
480
|
+
const cm = this.sdk.marketRegister.findCreditManager(account.creditManager);
|
|
481
|
+
const updates = await this.getOnDemandPriceUpdates(account, true);
|
|
482
|
+
const tx = cm.creditFacade.partiallyLiquidateCreditAccount(
|
|
483
|
+
account.creditAccount,
|
|
484
|
+
token,
|
|
485
|
+
repaidAmount,
|
|
486
|
+
minSeizedAmount,
|
|
487
|
+
to,
|
|
488
|
+
updates
|
|
489
|
+
);
|
|
490
|
+
return tx;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* {@inheritDoc ICreditAccountsService.closeCreditAccount}
|
|
494
|
+
**/
|
|
495
|
+
async closeCreditAccount({
|
|
496
|
+
operation,
|
|
497
|
+
assetsToWithdraw,
|
|
498
|
+
creditAccount: ca,
|
|
499
|
+
to,
|
|
500
|
+
slippage = 50n,
|
|
501
|
+
closePath
|
|
502
|
+
}) {
|
|
503
|
+
const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
|
|
504
|
+
await this.sdk.tokensMeta.loadTokenData(cm.underlying);
|
|
505
|
+
const underlying = this.sdk.tokensMeta.mustGet(cm.underlying);
|
|
506
|
+
if (this.sdk.tokensMeta.isKYCUnderlying(underlying)) {
|
|
507
|
+
throw new Error(
|
|
508
|
+
"closeCreditAccount is not supported for KYC underlying credit accounts"
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
const routerCloseResult = closePath || await this.sdk.routerFor(ca).findBestClosePath({
|
|
512
|
+
creditAccount: ca,
|
|
513
|
+
creditManager: cm.creditManager,
|
|
514
|
+
slippage
|
|
515
|
+
});
|
|
516
|
+
const operationCalls = [
|
|
517
|
+
...routerCloseResult.calls,
|
|
518
|
+
...this.#prepareDisableQuotas(ca),
|
|
519
|
+
...this.#prepareDecreaseDebt(ca),
|
|
520
|
+
...assetsToWithdraw.map(
|
|
521
|
+
(t) => this.#prepareWithdrawToken(ca.creditFacade, t, import_constants.MAX_UINT256, to)
|
|
522
|
+
)
|
|
523
|
+
];
|
|
524
|
+
const calls = operation === "close" ? operationCalls : await this.#prependPriceUpdates(ca.creditManager, operationCalls, ca);
|
|
525
|
+
const tx = await this.#closeCreditAccountTx(
|
|
526
|
+
cm,
|
|
527
|
+
ca.creditAccount,
|
|
528
|
+
calls,
|
|
529
|
+
operation
|
|
530
|
+
);
|
|
531
|
+
return { tx, calls, routerCloseResult, creditFacade: cm.creditFacade };
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* {@inheritDoc ICreditAccountsService.updateQuotas}
|
|
535
|
+
**/
|
|
536
|
+
async updateQuotas({
|
|
537
|
+
minQuota,
|
|
538
|
+
averageQuota,
|
|
539
|
+
creditAccount
|
|
540
|
+
}) {
|
|
541
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
542
|
+
creditAccount.creditManager
|
|
543
|
+
);
|
|
544
|
+
const operationCalls = this.#prepareUpdateQuotas(
|
|
545
|
+
creditAccount.creditFacade,
|
|
546
|
+
{ minQuota, averageQuota }
|
|
547
|
+
);
|
|
548
|
+
const calls = await this.#prependPriceUpdates(
|
|
549
|
+
creditAccount.creditManager,
|
|
550
|
+
operationCalls,
|
|
551
|
+
creditAccount
|
|
552
|
+
);
|
|
553
|
+
const tx = await this.#multicallTx(cm, creditAccount.creditAccount, calls);
|
|
554
|
+
return { tx, calls, creditFacade: cm.creditFacade };
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* {@inheritDoc ICreditAccountsService.addCollateral}
|
|
558
|
+
**/
|
|
559
|
+
async addCollateral({
|
|
560
|
+
creditAccount,
|
|
561
|
+
asset,
|
|
562
|
+
permit,
|
|
563
|
+
ethAmount,
|
|
564
|
+
minQuota,
|
|
565
|
+
averageQuota
|
|
566
|
+
}) {
|
|
567
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
568
|
+
creditAccount.creditManager
|
|
569
|
+
);
|
|
570
|
+
const operationCalls = [
|
|
571
|
+
...this.#prepareAddCollateral(
|
|
572
|
+
creditAccount.creditFacade,
|
|
573
|
+
[asset],
|
|
574
|
+
permit ? { [asset.token]: permit } : {}
|
|
575
|
+
),
|
|
576
|
+
...this.#prepareUpdateQuotas(creditAccount.creditFacade, {
|
|
577
|
+
minQuota,
|
|
578
|
+
averageQuota
|
|
579
|
+
})
|
|
580
|
+
];
|
|
581
|
+
const calls = await this.#prependPriceUpdates(
|
|
582
|
+
creditAccount.creditManager,
|
|
583
|
+
operationCalls,
|
|
584
|
+
creditAccount
|
|
585
|
+
);
|
|
586
|
+
const tx = await this.#multicallTx(cm, creditAccount.creditAccount, calls);
|
|
587
|
+
tx.value = ethAmount.toString(10);
|
|
588
|
+
return { tx, calls, creditFacade: cm.creditFacade };
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* {@inheritDoc ICreditAccountsService.changeDebt}
|
|
592
|
+
**/
|
|
593
|
+
async changeDebt({
|
|
594
|
+
creditAccount,
|
|
595
|
+
amount,
|
|
596
|
+
collateral
|
|
597
|
+
}) {
|
|
598
|
+
if (amount === 0n) {
|
|
599
|
+
throw new Error("debt increase or decrease must be non-zero");
|
|
600
|
+
}
|
|
601
|
+
const isDecrease = amount < 0n;
|
|
602
|
+
const change = amount > 0n ? amount : -amount;
|
|
603
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
604
|
+
creditAccount.creditManager
|
|
605
|
+
);
|
|
606
|
+
const addCollateralCalls = collateral && isDecrease ? this.#prepareAddCollateral(
|
|
607
|
+
creditAccount.creditFacade,
|
|
608
|
+
[
|
|
609
|
+
{
|
|
610
|
+
token: collateral[0].token,
|
|
611
|
+
balance: collateral[0].balance
|
|
612
|
+
}
|
|
613
|
+
],
|
|
614
|
+
{}
|
|
615
|
+
) : [];
|
|
616
|
+
const unwrapCalls = collateral && isDecrease ? await this.getKYCUnwrapCalls(
|
|
617
|
+
collateral[0].balance,
|
|
618
|
+
creditAccount.creditManager
|
|
619
|
+
) || [] : [];
|
|
620
|
+
if (addCollateralCalls.length > 0 && unwrapCalls.length === 0 && collateral && collateral?.[0].token !== creditAccount.underlying) {
|
|
621
|
+
throw new Error(
|
|
622
|
+
"Can't use collateral other than underlying for non KYC market"
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
const operationCalls = [
|
|
626
|
+
...addCollateralCalls,
|
|
627
|
+
...unwrapCalls,
|
|
628
|
+
this.#prepareChangeDebt(creditAccount.creditFacade, change, isDecrease)
|
|
629
|
+
];
|
|
630
|
+
const calls = await this.#prependPriceUpdates(
|
|
631
|
+
creditAccount.creditManager,
|
|
632
|
+
operationCalls,
|
|
633
|
+
creditAccount
|
|
634
|
+
);
|
|
635
|
+
const tx = await this.#multicallTx(cm, creditAccount.creditAccount, calls);
|
|
636
|
+
return { tx, calls, creditFacade: cm.creditFacade };
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* {@inheritDoc ICreditAccountsService.executeSwap}
|
|
640
|
+
**/
|
|
641
|
+
async executeSwap({
|
|
642
|
+
creditAccount,
|
|
643
|
+
calls: swapCalls,
|
|
644
|
+
minQuota,
|
|
645
|
+
averageQuota
|
|
646
|
+
}) {
|
|
647
|
+
if (swapCalls.length === 0) throw new Error("No path to execute");
|
|
648
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
649
|
+
creditAccount.creditManager
|
|
650
|
+
);
|
|
651
|
+
const operationCalls = [
|
|
652
|
+
...swapCalls,
|
|
653
|
+
...this.#prepareUpdateQuotas(creditAccount.creditFacade, {
|
|
654
|
+
minQuota,
|
|
655
|
+
averageQuota
|
|
656
|
+
})
|
|
657
|
+
];
|
|
658
|
+
const calls = await this.#prependPriceUpdates(
|
|
659
|
+
creditAccount.creditManager,
|
|
660
|
+
operationCalls,
|
|
661
|
+
creditAccount
|
|
662
|
+
);
|
|
663
|
+
const tx = await this.#multicallTx(cm, creditAccount.creditAccount, calls);
|
|
664
|
+
return { tx, calls, creditFacade: cm.creditFacade };
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* {@inheritDoc ICreditAccountsService.previewDelayedWithdrawal}
|
|
668
|
+
**/
|
|
669
|
+
async previewDelayedWithdrawal({
|
|
670
|
+
creditAccount,
|
|
671
|
+
amount,
|
|
672
|
+
token
|
|
673
|
+
}) {
|
|
674
|
+
const compressor = getWithdrawalCompressorAddress(this.sdk.chainId);
|
|
675
|
+
if (!compressor)
|
|
676
|
+
throw new Error(
|
|
677
|
+
`No compressor for current chain ${this.sdk.networkType}`
|
|
678
|
+
);
|
|
679
|
+
const contract = (0, import_viem.getContract)({
|
|
680
|
+
address: compressor,
|
|
681
|
+
abi: import_IWithdrawalCompressorV310.iWithdrawalCompressorV310Abi,
|
|
682
|
+
client: this.client
|
|
683
|
+
});
|
|
684
|
+
const resp = await contract.read.getWithdrawalRequestResult([
|
|
685
|
+
creditAccount,
|
|
686
|
+
token,
|
|
687
|
+
amount
|
|
688
|
+
]);
|
|
689
|
+
return resp;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* {@inheritDoc ICreditAccountsService.getPendingWithdrawals}
|
|
693
|
+
**/
|
|
694
|
+
async getPendingWithdrawals({
|
|
695
|
+
creditAccount
|
|
696
|
+
}) {
|
|
697
|
+
const compressor = getWithdrawalCompressorAddress(this.sdk.chainId);
|
|
698
|
+
if (!compressor)
|
|
699
|
+
throw new Error(
|
|
700
|
+
`No compressor for current chain ${this.sdk.networkType}`
|
|
701
|
+
);
|
|
702
|
+
const contract = (0, import_viem.getContract)({
|
|
703
|
+
address: compressor,
|
|
704
|
+
abi: import_IWithdrawalCompressorV310.iWithdrawalCompressorV310Abi,
|
|
705
|
+
client: this.client
|
|
706
|
+
});
|
|
707
|
+
const resp = await contract.read.getCurrentWithdrawals([creditAccount]);
|
|
708
|
+
const claimableNow = resp?.[0] || [];
|
|
709
|
+
const pendingResult = [...resp?.[1] || []].sort(
|
|
710
|
+
(a, b) => a.claimableAt < b.claimableAt ? -1 : 1
|
|
711
|
+
);
|
|
712
|
+
const respResult = {
|
|
713
|
+
claimableNow: [...claimableNow],
|
|
714
|
+
pending: pendingResult
|
|
715
|
+
};
|
|
716
|
+
return respResult;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* {@inheritDoc ICreditAccountsService.startDelayedWithdrawal}
|
|
720
|
+
**/
|
|
721
|
+
async startDelayedWithdrawal({
|
|
722
|
+
creditAccount,
|
|
723
|
+
minQuota,
|
|
724
|
+
averageQuota,
|
|
725
|
+
preview
|
|
726
|
+
}) {
|
|
727
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
728
|
+
creditAccount.creditManager
|
|
729
|
+
);
|
|
730
|
+
const record = preview.outputs.reduce((acc, o) => {
|
|
731
|
+
const token = o.token.toLowerCase();
|
|
732
|
+
acc[token] = (acc[token] || 0n) + o.amount;
|
|
733
|
+
return acc;
|
|
734
|
+
}, {});
|
|
735
|
+
const balances = Object.entries(record).filter(([, a]) => a > 10n);
|
|
736
|
+
const storeExpectedBalances = {
|
|
737
|
+
target: cm.creditFacade.address,
|
|
738
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
739
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
740
|
+
functionName: "storeExpectedBalances",
|
|
741
|
+
args: [
|
|
742
|
+
balances.map(([token, amount]) => ({
|
|
743
|
+
token,
|
|
744
|
+
amount: amount > 10n ? amount - 10n : 0n
|
|
745
|
+
}))
|
|
746
|
+
]
|
|
747
|
+
})
|
|
748
|
+
};
|
|
749
|
+
const compareBalances = {
|
|
750
|
+
target: cm.creditFacade.address,
|
|
751
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
752
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
753
|
+
functionName: "compareBalances",
|
|
754
|
+
args: []
|
|
755
|
+
})
|
|
756
|
+
};
|
|
757
|
+
const operationCalls = [
|
|
758
|
+
storeExpectedBalances,
|
|
759
|
+
...preview.requestCalls,
|
|
760
|
+
compareBalances,
|
|
761
|
+
...this.#prepareUpdateQuotas(creditAccount.creditFacade, {
|
|
762
|
+
minQuota,
|
|
763
|
+
averageQuota
|
|
764
|
+
})
|
|
765
|
+
];
|
|
766
|
+
const calls = await this.#prependPriceUpdates(
|
|
767
|
+
creditAccount.creditManager,
|
|
768
|
+
operationCalls,
|
|
769
|
+
creditAccount
|
|
770
|
+
);
|
|
771
|
+
const tx = await this.#multicallTx(cm, creditAccount.creditAccount, calls);
|
|
772
|
+
return { tx, calls, creditFacade: cm.creditFacade };
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* {@inheritDoc ICreditAccountsService.claimDelayed}
|
|
776
|
+
**/
|
|
777
|
+
async claimDelayed({
|
|
778
|
+
creditAccount,
|
|
779
|
+
minQuota,
|
|
780
|
+
averageQuota,
|
|
781
|
+
claimableNow
|
|
782
|
+
}) {
|
|
783
|
+
const zeroDebt = creditAccount.debt === 0n;
|
|
784
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
785
|
+
creditAccount.creditManager
|
|
786
|
+
);
|
|
787
|
+
const record = claimableNow.outputs.reduce(
|
|
788
|
+
(acc, o) => {
|
|
789
|
+
const token = o.token.toLowerCase();
|
|
790
|
+
acc[token] = (acc[token] || 0n) + o.amount;
|
|
791
|
+
return acc;
|
|
792
|
+
},
|
|
793
|
+
{}
|
|
794
|
+
);
|
|
795
|
+
const balances = Object.entries(record).filter(([, a]) => a > 10n);
|
|
796
|
+
const storeExpectedBalances = {
|
|
797
|
+
target: cm.creditFacade.address,
|
|
798
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
799
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
800
|
+
functionName: "storeExpectedBalances",
|
|
801
|
+
args: [
|
|
802
|
+
balances.map(([token, amount]) => ({
|
|
803
|
+
token,
|
|
804
|
+
amount: amount > 10n ? amount - 10n : 0n
|
|
805
|
+
}))
|
|
806
|
+
]
|
|
807
|
+
})
|
|
808
|
+
};
|
|
809
|
+
const compareBalances = {
|
|
810
|
+
target: cm.creditFacade.address,
|
|
811
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
812
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
813
|
+
functionName: "compareBalances",
|
|
814
|
+
args: []
|
|
815
|
+
})
|
|
816
|
+
};
|
|
817
|
+
const quotaCalls = zeroDebt ? [] : this.#prepareUpdateQuotas(creditAccount.creditFacade, {
|
|
818
|
+
minQuota,
|
|
819
|
+
averageQuota
|
|
820
|
+
});
|
|
821
|
+
const operationCalls = [
|
|
822
|
+
storeExpectedBalances,
|
|
823
|
+
...claimableNow.claimCalls,
|
|
824
|
+
compareBalances,
|
|
825
|
+
...quotaCalls
|
|
826
|
+
];
|
|
827
|
+
const calls = zeroDebt ? operationCalls : await this.#prependPriceUpdates(
|
|
828
|
+
creditAccount.creditManager,
|
|
829
|
+
operationCalls,
|
|
830
|
+
creditAccount
|
|
831
|
+
);
|
|
832
|
+
const tx = await this.#multicallTx(cm, creditAccount.creditAccount, calls);
|
|
833
|
+
return { tx, calls, creditFacade: cm.creditFacade };
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* {@inheritDoc ICreditAccountsService.getApprovalAddress}
|
|
837
|
+
**/
|
|
838
|
+
async getApprovalAddress(options) {
|
|
839
|
+
const { creditManager } = options;
|
|
840
|
+
const suite = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
841
|
+
const marketSuite = this.sdk.marketRegister.findByPool(suite.pool);
|
|
842
|
+
const factory = marketSuite.kycFactory;
|
|
843
|
+
if (factory) {
|
|
844
|
+
return factory.getApprovalAddress(options);
|
|
845
|
+
}
|
|
846
|
+
return suite.creditManager.address;
|
|
847
|
+
}
|
|
848
|
+
/**
|
|
849
|
+
* {@inheritDoc ICreditAccountsService.getOpenAccountRequirements}
|
|
850
|
+
*/
|
|
851
|
+
async getOpenAccountRequirements(borrower, creditManager, props) {
|
|
852
|
+
const { kycFactory } = this.sdk.marketRegister.findByCreditManager(creditManager);
|
|
853
|
+
if (!kycFactory) {
|
|
854
|
+
return void 0;
|
|
855
|
+
}
|
|
856
|
+
return kycFactory.getOpenAccountRequirements(borrower, props);
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* {@inheritDoc ICreditAccountsService.openCA}
|
|
860
|
+
**/
|
|
861
|
+
async openCA(props) {
|
|
862
|
+
const {
|
|
863
|
+
ethAmount,
|
|
864
|
+
creditManager,
|
|
865
|
+
reopenCreditAccount,
|
|
866
|
+
collateral,
|
|
867
|
+
permits,
|
|
868
|
+
debt,
|
|
869
|
+
withdrawToken,
|
|
870
|
+
referralCode,
|
|
871
|
+
to,
|
|
872
|
+
calls: openPathCalls,
|
|
873
|
+
callsAfter,
|
|
874
|
+
minQuota,
|
|
875
|
+
averageQuota,
|
|
876
|
+
kycOptions
|
|
877
|
+
} = props;
|
|
878
|
+
const cmSuite = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
879
|
+
const cm = cmSuite.creditManager;
|
|
880
|
+
let tokenToWithdraw;
|
|
881
|
+
if (withdrawToken === true) {
|
|
882
|
+
tokenToWithdraw = cm.underlying;
|
|
883
|
+
} else if (typeof withdrawToken === "string") {
|
|
884
|
+
tokenToWithdraw = withdrawToken;
|
|
885
|
+
}
|
|
886
|
+
const operationCalls = [
|
|
887
|
+
this.#prepareIncreaseDebt(cm.creditFacade, debt),
|
|
888
|
+
...this.#prepareAddCollateral(cm.creditFacade, collateral, permits),
|
|
889
|
+
...openPathCalls,
|
|
890
|
+
// path from underlying to withdrawal token
|
|
891
|
+
...tokenToWithdraw ? [
|
|
892
|
+
this.#prepareWithdrawToken(
|
|
893
|
+
cm.creditFacade,
|
|
894
|
+
tokenToWithdraw,
|
|
895
|
+
import_constants.MAX_UINT256,
|
|
896
|
+
to
|
|
897
|
+
)
|
|
898
|
+
] : [],
|
|
899
|
+
...this.#prepareUpdateQuotas(cm.creditFacade, {
|
|
900
|
+
minQuota,
|
|
901
|
+
averageQuota
|
|
902
|
+
}),
|
|
903
|
+
...callsAfter ?? []
|
|
904
|
+
];
|
|
905
|
+
const calls = await this.#prependPriceUpdates(cm.address, operationCalls);
|
|
906
|
+
let tx;
|
|
907
|
+
if (reopenCreditAccount) {
|
|
908
|
+
tx = await this.#multicallTx(cmSuite, reopenCreditAccount, calls);
|
|
909
|
+
} else {
|
|
910
|
+
tx = await this.#openCreditAccountTx(
|
|
911
|
+
cmSuite,
|
|
912
|
+
to,
|
|
913
|
+
calls,
|
|
914
|
+
referralCode,
|
|
915
|
+
kycOptions
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
tx.value = ethAmount.toString(10);
|
|
919
|
+
return { calls, tx, creditFacade: cmSuite.creditFacade };
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* {@inheritDoc ICreditAccountsService.getBorrowRate}
|
|
923
|
+
**/
|
|
924
|
+
getBorrowRate(ca) {
|
|
925
|
+
const { creditManager } = this.sdk.marketRegister.findCreditManager(
|
|
926
|
+
ca.creditManager
|
|
927
|
+
);
|
|
928
|
+
const { pool } = this.sdk.marketRegister.findByCreditManager(
|
|
929
|
+
ca.creditManager
|
|
930
|
+
);
|
|
931
|
+
const { feeInterest } = creditManager;
|
|
932
|
+
const { baseInterestRate } = pool.pool;
|
|
933
|
+
const baseRateWithFee = baseInterestRate * (BigInt(feeInterest) + import_constants.PERCENTAGE_FACTOR);
|
|
934
|
+
const totalDebt = ca.debt + ca.accruedInterest + ca.accruedFees;
|
|
935
|
+
const r = ca.debt * baseRateWithFee / (totalDebt * import_constants.RAY);
|
|
936
|
+
const caTokens = new import_utils.AddressMap(ca.tokens.map((t) => [t.token, t]));
|
|
937
|
+
let qr = 0n;
|
|
938
|
+
for (const t of creditManager.collateralTokens) {
|
|
939
|
+
const b = caTokens.get(t);
|
|
940
|
+
if (b) {
|
|
941
|
+
qr += b.quota * BigInt(pool.pqk.quotas.get(t)?.rate ?? 0);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
qr = qr * (BigInt(feeInterest) + import_constants.PERCENTAGE_FACTOR) / import_constants.PERCENTAGE_FACTOR;
|
|
945
|
+
qr /= totalDebt;
|
|
946
|
+
return r + qr;
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* {@inheritDoc ICreditAccountsService.getOptimalHFForPartialLiquidation}
|
|
950
|
+
**/
|
|
951
|
+
getOptimalHFForPartialLiquidation(ca) {
|
|
952
|
+
const borrowRate = this.getBorrowRate(ca);
|
|
953
|
+
return import_constants.PERCENTAGE_FACTOR + (borrowRate < 100n ? borrowRate : 100n);
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Internal wrapper for CreditAccountCompressor.getCreditAccounts + price updates wrapped into multicall
|
|
957
|
+
* @param args
|
|
958
|
+
* @param priceUpdateTxs
|
|
959
|
+
* @param blockNumber
|
|
960
|
+
* @returns
|
|
961
|
+
*/
|
|
962
|
+
async #getCreditAccounts(args, priceUpdateTxs, blockNumber) {
|
|
963
|
+
let resp;
|
|
964
|
+
if (priceUpdateTxs?.length) {
|
|
965
|
+
[resp] = await (0, import_viem2.simulateWithPriceUpdates)(this.client, {
|
|
966
|
+
priceUpdates: priceUpdateTxs,
|
|
967
|
+
contracts: [
|
|
968
|
+
{
|
|
969
|
+
abi: import_creditAccountCompressor.creditAccountCompressorAbi,
|
|
970
|
+
address: this.compressor,
|
|
971
|
+
functionName: "getCreditAccounts",
|
|
972
|
+
args
|
|
973
|
+
}
|
|
974
|
+
],
|
|
975
|
+
blockNumber,
|
|
976
|
+
gas: this.sdk.gasLimit
|
|
977
|
+
});
|
|
978
|
+
} else {
|
|
979
|
+
resp = await this.client.readContract({
|
|
980
|
+
abi: import_creditAccountCompressor.creditAccountCompressorAbi,
|
|
981
|
+
address: this.compressor,
|
|
982
|
+
functionName: "getCreditAccounts",
|
|
983
|
+
args,
|
|
984
|
+
blockNumber,
|
|
985
|
+
// @ts-expect-error
|
|
986
|
+
gas: this.sdk.gasLimit
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
this.logger?.debug(
|
|
990
|
+
{
|
|
991
|
+
accounts: resp[0]?.length ?? 0,
|
|
992
|
+
nextOffset: Number(resp[1])
|
|
993
|
+
},
|
|
994
|
+
"got credit accounts"
|
|
995
|
+
);
|
|
996
|
+
return resp;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Returns multicall entries to redeem (unwrap) KYC ERC-4626 vault shares into underlying for the given credit manager.
|
|
1000
|
+
* Used when withdrawing debt from a KYC market: redeems adapter vault shares so the underlying can be withdrawn.
|
|
1001
|
+
* Only applies when the credit manager's underlying is KYC-gated and has an ERC-4626 adapter configured.
|
|
1002
|
+
* @param amount - Number of vault shares (adapter tokens) to redeem
|
|
1003
|
+
* @param creditManager - Credit manager address
|
|
1004
|
+
* @returns Array of MultiCall to pass to credit facade multicall, or undefined if underlying is not KYC or no adapter is configured
|
|
1005
|
+
*/
|
|
1006
|
+
async getKYCUnwrapCalls(amount, creditManager) {
|
|
1007
|
+
const suite = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
1008
|
+
const meta = this.sdk.tokensMeta.mustGet(suite.underlying);
|
|
1009
|
+
if (!this.sdk.tokensMeta.isKYCUnderlying(meta)) {
|
|
1010
|
+
return void 0;
|
|
1011
|
+
}
|
|
1012
|
+
const adapter = suite.creditManager.adapters.get(meta.addr);
|
|
1013
|
+
const adapterAddress = adapter?.address;
|
|
1014
|
+
if (!adapterAddress) {
|
|
1015
|
+
return void 0;
|
|
1016
|
+
}
|
|
1017
|
+
const mc = [
|
|
1018
|
+
{
|
|
1019
|
+
target: adapterAddress,
|
|
1020
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1021
|
+
abi: import_integrations_v3.ierc4626AdapterAbi,
|
|
1022
|
+
functionName: "redeem",
|
|
1023
|
+
args: [amount, import_constants.ADDRESS_0X0, import_constants.ADDRESS_0X0]
|
|
1024
|
+
})
|
|
1025
|
+
}
|
|
1026
|
+
];
|
|
1027
|
+
return mc;
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Returns multicall entries to deposit (wrap) underlying into KYC ERC-4626 vault shares for the given credit manager.
|
|
1031
|
+
* Used when adding debt on a KYC market: deposits underlying into the adapter vault so shares are minted on the account.
|
|
1032
|
+
* Only applies when the credit manager's underlying is KYC-gated and has an ERC-4626 adapter configured.
|
|
1033
|
+
* @param amount - Amount of underlying assets to deposit into the vault (in underlying decimals)
|
|
1034
|
+
* @param creditManager - Credit manager address
|
|
1035
|
+
* @returns Array of MultiCall to pass to credit facade multicall, or undefined if underlying is not KYC or no adapter is configured
|
|
1036
|
+
*/
|
|
1037
|
+
async getKYCWrapCalls(amount, creditManager) {
|
|
1038
|
+
const suite = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
1039
|
+
const meta = this.sdk.tokensMeta.mustGet(suite.underlying);
|
|
1040
|
+
if (!this.sdk.tokensMeta.isKYCUnderlying(meta)) {
|
|
1041
|
+
return void 0;
|
|
1042
|
+
}
|
|
1043
|
+
const adapter = suite.creditManager.adapters.get(meta.addr);
|
|
1044
|
+
const adapterAddress = adapter?.address;
|
|
1045
|
+
if (!adapterAddress) {
|
|
1046
|
+
return void 0;
|
|
1047
|
+
}
|
|
1048
|
+
const mc = [
|
|
1049
|
+
{
|
|
1050
|
+
target: adapterAddress,
|
|
1051
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1052
|
+
abi: import_integrations_v3.ierc4626AdapterAbi,
|
|
1053
|
+
functionName: "deposit",
|
|
1054
|
+
args: [amount, import_constants.ADDRESS_0X0]
|
|
1055
|
+
})
|
|
1056
|
+
}
|
|
1057
|
+
];
|
|
1058
|
+
return mc;
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Returns multicall entries to call redeemDiff on the KYC ERC-4626 adapter for the given credit manager.
|
|
1062
|
+
* Redeems the leftover vault shares (e.g. after repaying debt) so the account does not hold excess KYC vault tokens.
|
|
1063
|
+
* Only applies when the credit manager's underlying is KYC-gated and has an ERC-4626 adapter configured.
|
|
1064
|
+
* @param amount - Leftover vault share amount to redeem (in adapter/vault decimals)
|
|
1065
|
+
* @param creditManager - Credit manager address
|
|
1066
|
+
* @returns Array of MultiCall to pass to credit facade multicall, or undefined if underlying is not KYC or no adapter is configured
|
|
1067
|
+
*/
|
|
1068
|
+
async getRedeemDiffCalls(amount, creditManager) {
|
|
1069
|
+
const suite = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
1070
|
+
const meta = this.sdk.tokensMeta.mustGet(suite.underlying);
|
|
1071
|
+
if (!this.sdk.tokensMeta.isKYCUnderlying(meta)) {
|
|
1072
|
+
return void 0;
|
|
1073
|
+
}
|
|
1074
|
+
const adapter = suite.creditManager.adapters.get(meta.addr);
|
|
1075
|
+
const adapterAddress = adapter?.address;
|
|
1076
|
+
if (!adapterAddress) {
|
|
1077
|
+
return void 0;
|
|
1078
|
+
}
|
|
1079
|
+
const mc = [
|
|
1080
|
+
{
|
|
1081
|
+
target: adapterAddress,
|
|
1082
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1083
|
+
abi: import_integrations_v3.ierc4626AdapterAbi,
|
|
1084
|
+
functionName: "redeemDiff",
|
|
1085
|
+
args: [amount]
|
|
1086
|
+
})
|
|
1087
|
+
}
|
|
1088
|
+
];
|
|
1089
|
+
return mc;
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* Returns multicall entries to call depositDiff on the KYC ERC-4626 adapter for the given credit manager.
|
|
1093
|
+
* Deposits the leftover underlying (e.g. after decreasing debt) into the vault so the account does not hold excess underlying.
|
|
1094
|
+
* Only applies when the credit manager's underlying is KYC-gated and has an ERC-4626 adapter configured.
|
|
1095
|
+
* @param amount - Leftover underlying amount to deposit into the vault (in underlying decimals)
|
|
1096
|
+
* @param creditManager - Credit manager address
|
|
1097
|
+
* @returns Array of MultiCall to pass to credit facade multicall, or undefined if underlying is not KYC or no adapter is configured
|
|
1098
|
+
*/
|
|
1099
|
+
async getDepositDiffCalls(amount, creditManager) {
|
|
1100
|
+
const suite = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
1101
|
+
const meta = this.sdk.tokensMeta.mustGet(suite.underlying);
|
|
1102
|
+
if (!this.sdk.tokensMeta.isKYCUnderlying(meta)) {
|
|
1103
|
+
return void 0;
|
|
1104
|
+
}
|
|
1105
|
+
const adapter = suite.creditManager.adapters.get(meta.addr);
|
|
1106
|
+
const adapterAddress = adapter?.address;
|
|
1107
|
+
if (!adapterAddress) {
|
|
1108
|
+
return void 0;
|
|
1109
|
+
}
|
|
1110
|
+
const mc = [
|
|
1111
|
+
{
|
|
1112
|
+
target: adapterAddress,
|
|
1113
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1114
|
+
abi: import_integrations_v3.ierc4626AdapterAbi,
|
|
1115
|
+
functionName: "depositDiff",
|
|
1116
|
+
args: [amount]
|
|
1117
|
+
})
|
|
1118
|
+
}
|
|
1119
|
+
];
|
|
1120
|
+
return mc;
|
|
1121
|
+
}
|
|
29
1122
|
/**
|
|
30
1123
|
* {@inheritDoc ICreditAccountsService.setBot}
|
|
31
1124
|
*/
|
|
@@ -60,12 +1153,12 @@ class CreditAccountServiceV310 extends import_AbstractCreditAccountsService.Abst
|
|
|
60
1153
|
args: [botAddress, permissions]
|
|
61
1154
|
})
|
|
62
1155
|
};
|
|
63
|
-
const calls = targetContract.type === "creditAccount" ? await this
|
|
1156
|
+
const calls = targetContract.type === "creditAccount" ? await this.#prependPriceUpdates(
|
|
64
1157
|
targetContract.creditManager,
|
|
65
1158
|
[addBotCall],
|
|
66
1159
|
targetContract
|
|
67
1160
|
) : [addBotCall];
|
|
68
|
-
const tx = targetContract.type === "creditAccount" ? await this
|
|
1161
|
+
const tx = targetContract.type === "creditAccount" ? await this.#multicallTx(cm, targetContract.creditAccount, calls) : void 0;
|
|
69
1162
|
return { tx, calls, creditFacade: cm.creditFacade };
|
|
70
1163
|
}
|
|
71
1164
|
/**
|
|
@@ -83,24 +1176,24 @@ class CreditAccountServiceV310 extends import_AbstractCreditAccountsService.Abst
|
|
|
83
1176
|
);
|
|
84
1177
|
const operationCalls = [
|
|
85
1178
|
...assetsToWithdraw.map(
|
|
86
|
-
(a) => this
|
|
1179
|
+
(a) => this.#prepareWithdrawToken(
|
|
87
1180
|
creditAccount.creditFacade,
|
|
88
1181
|
a.token,
|
|
89
1182
|
a.balance,
|
|
90
1183
|
to
|
|
91
1184
|
)
|
|
92
1185
|
),
|
|
93
|
-
...this
|
|
1186
|
+
...this.#prepareUpdateQuotas(creditAccount.creditFacade, {
|
|
94
1187
|
minQuota,
|
|
95
1188
|
averageQuota
|
|
96
1189
|
})
|
|
97
1190
|
];
|
|
98
|
-
const calls = await this
|
|
1191
|
+
const calls = await this.#prependPriceUpdates(
|
|
99
1192
|
creditAccount.creditManager,
|
|
100
1193
|
operationCalls,
|
|
101
1194
|
creditAccount
|
|
102
1195
|
);
|
|
103
|
-
const tx = await this
|
|
1196
|
+
const tx = await this.#multicallTx(cm, creditAccount.creditAccount, calls);
|
|
104
1197
|
return { tx, calls, creditFacade: cm.creditFacade };
|
|
105
1198
|
}
|
|
106
1199
|
/**
|
|
@@ -125,18 +1218,18 @@ class CreditAccountServiceV310 extends import_AbstractCreditAccountsService.Abst
|
|
|
125
1218
|
creditAccount: ca
|
|
126
1219
|
});
|
|
127
1220
|
const operationCalls = [
|
|
128
|
-
...this
|
|
1221
|
+
...this.#prepareAddCollateral(ca.creditFacade, addCollateral, permits),
|
|
129
1222
|
...wrapCalls,
|
|
130
|
-
...this
|
|
131
|
-
...this
|
|
1223
|
+
...this.#prepareDisableQuotas(ca),
|
|
1224
|
+
...this.#prepareDecreaseDebt(ca),
|
|
132
1225
|
...unwrapCalls,
|
|
133
1226
|
...claimPath.calls,
|
|
134
1227
|
...assetsToWithdraw.map(
|
|
135
|
-
(t) => this
|
|
1228
|
+
(t) => this.#prepareWithdrawToken(ca.creditFacade, t.token, import_constants.MAX_UINT256, to)
|
|
136
1229
|
)
|
|
137
1230
|
];
|
|
138
|
-
const calls = operation === "close" ? operationCalls : await this
|
|
139
|
-
const tx = await this
|
|
1231
|
+
const calls = operation === "close" ? operationCalls : await this.#prependPriceUpdates(ca.creditManager, operationCalls, ca);
|
|
1232
|
+
const tx = await this.#closeCreditAccountTx(
|
|
140
1233
|
cm,
|
|
141
1234
|
ca.creditAccount,
|
|
142
1235
|
calls,
|
|
@@ -164,14 +1257,14 @@ class CreditAccountServiceV310 extends import_AbstractCreditAccountsService.Abst
|
|
|
164
1257
|
const wrapCalls = await this.getDepositDiffCalls(1n, ca.creditManager) ?? [];
|
|
165
1258
|
const addCollateral = collateralAssets.filter((a) => a.balance > 0);
|
|
166
1259
|
const operationCalls = [
|
|
167
|
-
...this
|
|
1260
|
+
...this.#prepareAddCollateral(ca.creditFacade, addCollateral, permits),
|
|
168
1261
|
...claimPath.calls,
|
|
169
1262
|
...wrapCalls,
|
|
170
1263
|
...assetsToWithdraw.map(
|
|
171
|
-
(t) => this
|
|
1264
|
+
(t) => this.#prepareWithdrawToken(ca.creditFacade, t.token, import_constants.MAX_UINT256, to)
|
|
172
1265
|
)
|
|
173
1266
|
];
|
|
174
|
-
const calls = await this
|
|
1267
|
+
const calls = await this.#prependPriceUpdates(
|
|
175
1268
|
ca.creditManager,
|
|
176
1269
|
operationCalls,
|
|
177
1270
|
ca
|
|
@@ -207,18 +1300,350 @@ class CreditAccountServiceV310 extends import_AbstractCreditAccountsService.Abst
|
|
|
207
1300
|
if (claimPath.calls.length === 0) throw new Error("No path to execute");
|
|
208
1301
|
const operationCalls = [
|
|
209
1302
|
...claimPath.calls,
|
|
210
|
-
...this
|
|
1303
|
+
...this.#prepareUpdateQuotas(ca.creditFacade, { minQuota, averageQuota })
|
|
211
1304
|
];
|
|
212
|
-
const calls = await this
|
|
1305
|
+
const calls = await this.#prependPriceUpdates(
|
|
213
1306
|
ca.creditManager,
|
|
214
1307
|
operationCalls,
|
|
215
1308
|
ca
|
|
216
1309
|
);
|
|
217
|
-
const tx = await this
|
|
1310
|
+
const tx = await this.#multicallTx(cm, ca.creditAccount, calls);
|
|
218
1311
|
return { tx, calls, creditFacade: cm.creditFacade };
|
|
219
1312
|
}
|
|
1313
|
+
/**
|
|
1314
|
+
* Returns raw txs that are needed to update all price feeds so that all credit accounts (possibly from different markets) compute
|
|
1315
|
+
* {@inheritDoc ICreditAccountsService.getOnDemandPriceUpdates}
|
|
1316
|
+
**/
|
|
1317
|
+
async getOnDemandPriceUpdates(account, ignoreReservePrices) {
|
|
1318
|
+
const { creditManager, creditAccount } = account;
|
|
1319
|
+
const cm = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
1320
|
+
const update = await this.#getUpdateForAccount(
|
|
1321
|
+
account,
|
|
1322
|
+
ignoreReservePrices
|
|
1323
|
+
);
|
|
1324
|
+
this.logger?.debug(
|
|
1325
|
+
{ account: creditAccount, manager: cm.name },
|
|
1326
|
+
`getting on demand price updates from ${update.txs.length} txs`
|
|
1327
|
+
);
|
|
1328
|
+
return (0, import_market.getRawPriceUpdates)(update);
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Analyzes a multicall array and prepends necessary on-demand price feed updates.
|
|
1332
|
+
*
|
|
1333
|
+
* Deduplicates existing `onDemandPriceUpdates` calls
|
|
1334
|
+
*
|
|
1335
|
+
* @param creditManager - Address of the credit manager
|
|
1336
|
+
* @param calls - The multicall array to prepend price updates to
|
|
1337
|
+
* @param ca - Credit account slice, undefined when opening a new account
|
|
1338
|
+
* @param options - Optional settings for price update generation
|
|
1339
|
+
* @returns A new array with a single consolidated price update call prepended,
|
|
1340
|
+
* followed by the non-price-update calls in their original order
|
|
1341
|
+
*/
|
|
1342
|
+
async #prependPriceUpdates(creditManager, calls, ca, options) {
|
|
1343
|
+
const market = this.sdk.marketRegister.findByCreditManager(creditManager);
|
|
1344
|
+
const cm = this.sdk.marketRegister.findCreditManager(creditManager).creditManager;
|
|
1345
|
+
const { priceUpdates: existingUpdates, remainingCalls } = (0, import_multicall_utils.extractPriceUpdates)(calls);
|
|
1346
|
+
const tokens = new import_utils.AddressSet([
|
|
1347
|
+
cm.underlying,
|
|
1348
|
+
// underlying - always included
|
|
1349
|
+
...(0, import_multicall_utils.extractQuotaTokens)(calls)
|
|
1350
|
+
// tokens from `updateQuota` calls
|
|
1351
|
+
]);
|
|
1352
|
+
if (ca) {
|
|
1353
|
+
for (const t of ca.tokens) {
|
|
1354
|
+
const isEnabled = (t.mask & ca.enabledTokensMask) !== 0n;
|
|
1355
|
+
if (t.balance > 10n && isEnabled) {
|
|
1356
|
+
tokens.add(t.token);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
const ignoreReservePrices = options?.ignoreReservePrices;
|
|
1361
|
+
const priceFeeds = market.priceOracle.priceFeedsForTokens(Array.from(tokens), {
|
|
1362
|
+
main: true,
|
|
1363
|
+
reserve: !ignoreReservePrices
|
|
1364
|
+
});
|
|
1365
|
+
const tStr = tokens.map((t) => this.labelAddress(t)).join(", ");
|
|
1366
|
+
const remark = ignoreReservePrices ? " main" : "";
|
|
1367
|
+
this.logger?.debug(
|
|
1368
|
+
{ account: ca?.creditAccount, manager: cm.name },
|
|
1369
|
+
`prependPriceUpdates for ${tStr} from ${priceFeeds.length}${remark} price feeds`
|
|
1370
|
+
);
|
|
1371
|
+
const generatedUpdates = await this.sdk.priceFeeds.generatePriceFeedsUpdates(priceFeeds);
|
|
1372
|
+
const merged = (0, import_multicall_utils.mergePriceUpdates)(existingUpdates, generatedUpdates);
|
|
1373
|
+
if (merged.length === 0) {
|
|
1374
|
+
return remainingCalls;
|
|
1375
|
+
}
|
|
1376
|
+
return [
|
|
1377
|
+
{
|
|
1378
|
+
target: cm.creditFacade,
|
|
1379
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1380
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1381
|
+
functionName: "onDemandPriceUpdates",
|
|
1382
|
+
args: [merged]
|
|
1383
|
+
})
|
|
1384
|
+
},
|
|
1385
|
+
...remainingCalls
|
|
1386
|
+
];
|
|
1387
|
+
}
|
|
1388
|
+
async #getUpdateForAccount(account, ignoreReservePrices) {
|
|
1389
|
+
const { creditManager, creditAccount, enabledTokensMask } = account;
|
|
1390
|
+
const market = this.sdk.marketRegister.findByCreditManager(creditManager);
|
|
1391
|
+
const cm = this.sdk.marketRegister.findCreditManager(creditManager).creditManager;
|
|
1392
|
+
const tokens = new import_utils.AddressSet([cm.underlying]);
|
|
1393
|
+
for (const t of account.tokens) {
|
|
1394
|
+
const isEnabled = (t.mask & enabledTokensMask) !== 0n;
|
|
1395
|
+
if (t.balance > 10n && isEnabled) {
|
|
1396
|
+
tokens.add(t.token);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
const priceFeeds = market.priceOracle.priceFeedsForTokens(Array.from(tokens), {
|
|
1400
|
+
main: true,
|
|
1401
|
+
reserve: !ignoreReservePrices
|
|
1402
|
+
});
|
|
1403
|
+
const tStr = tokens.map((t) => this.labelAddress(t)).join(", ");
|
|
1404
|
+
const remark = ignoreReservePrices ? " main" : "";
|
|
1405
|
+
this.logger?.debug(
|
|
1406
|
+
{ account: creditAccount, manager: cm.name },
|
|
1407
|
+
`generating price feed updates for ${tStr} from ${priceFeeds.length}${remark} price feeds`
|
|
1408
|
+
);
|
|
1409
|
+
return this.sdk.priceFeeds.generatePriceFeedsUpdateTxs(priceFeeds);
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* {@inheritDoc ICreditAccountsService.multicall}
|
|
1413
|
+
*/
|
|
1414
|
+
async multicall(creditAccount, calls, options) {
|
|
1415
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
1416
|
+
creditAccount.creditManager
|
|
1417
|
+
);
|
|
1418
|
+
const callsWithPrices = await this.#prependPriceUpdates(
|
|
1419
|
+
creditAccount.creditManager,
|
|
1420
|
+
calls,
|
|
1421
|
+
creditAccount,
|
|
1422
|
+
options
|
|
1423
|
+
);
|
|
1424
|
+
return cm.creditFacade.multicall(
|
|
1425
|
+
creditAccount.creditAccount,
|
|
1426
|
+
callsWithPrices
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1429
|
+
/**
|
|
1430
|
+
* {@inheritDoc ICreditAccountsService.botMulticall}
|
|
1431
|
+
*/
|
|
1432
|
+
async botMulticall(creditAccount, calls, options) {
|
|
1433
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
1434
|
+
creditAccount.creditManager
|
|
1435
|
+
);
|
|
1436
|
+
const callsWithPrices = await this.#prependPriceUpdates(
|
|
1437
|
+
creditAccount.creditManager,
|
|
1438
|
+
calls,
|
|
1439
|
+
creditAccount,
|
|
1440
|
+
options
|
|
1441
|
+
);
|
|
1442
|
+
return cm.creditFacade.botMulticall(
|
|
1443
|
+
creditAccount.creditAccount,
|
|
1444
|
+
callsWithPrices
|
|
1445
|
+
);
|
|
1446
|
+
}
|
|
1447
|
+
#prepareDisableQuotas(ca) {
|
|
1448
|
+
const calls = [];
|
|
1449
|
+
for (const { token, quota } of ca.tokens) {
|
|
1450
|
+
if (quota > 0n) {
|
|
1451
|
+
calls.push({
|
|
1452
|
+
target: ca.creditFacade,
|
|
1453
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1454
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1455
|
+
functionName: "updateQuota",
|
|
1456
|
+
args: [token, import_constants.MIN_INT96, 0n]
|
|
1457
|
+
})
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
return calls;
|
|
1462
|
+
}
|
|
1463
|
+
#prepareUpdateQuotas(creditFacade, { averageQuota, minQuota }) {
|
|
1464
|
+
const minRecord = (0, import_router.assetsMap)(minQuota);
|
|
1465
|
+
const calls = averageQuota.map((q) => {
|
|
1466
|
+
const minAsset = minRecord.get(q.token);
|
|
1467
|
+
const min = minAsset && minAsset?.balance > 0 ? minAsset.balance : 0n;
|
|
1468
|
+
return {
|
|
1469
|
+
target: creditFacade,
|
|
1470
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1471
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1472
|
+
functionName: "updateQuota",
|
|
1473
|
+
args: [q.token, q.balance, min]
|
|
1474
|
+
})
|
|
1475
|
+
};
|
|
1476
|
+
});
|
|
1477
|
+
return calls;
|
|
1478
|
+
}
|
|
1479
|
+
#prepareDecreaseDebt(ca) {
|
|
1480
|
+
if (ca.debt > 0n) {
|
|
1481
|
+
return [
|
|
1482
|
+
{
|
|
1483
|
+
target: ca.creditFacade,
|
|
1484
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1485
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1486
|
+
functionName: "decreaseDebt",
|
|
1487
|
+
args: [import_constants.MAX_UINT256]
|
|
1488
|
+
})
|
|
1489
|
+
}
|
|
1490
|
+
];
|
|
1491
|
+
}
|
|
1492
|
+
return [];
|
|
1493
|
+
}
|
|
1494
|
+
#prepareWithdrawToken(creditFacade, token, amount, to) {
|
|
1495
|
+
return {
|
|
1496
|
+
target: creditFacade,
|
|
1497
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1498
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1499
|
+
functionName: "withdrawCollateral",
|
|
1500
|
+
args: [token, amount, to]
|
|
1501
|
+
})
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
#prepareIncreaseDebt(creditFacade, debt) {
|
|
1505
|
+
return {
|
|
1506
|
+
target: creditFacade,
|
|
1507
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1508
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1509
|
+
functionName: "increaseDebt",
|
|
1510
|
+
args: [debt]
|
|
1511
|
+
})
|
|
1512
|
+
};
|
|
1513
|
+
}
|
|
1514
|
+
#prepareChangeDebt(creditFacade, change, isDecrease) {
|
|
1515
|
+
return {
|
|
1516
|
+
target: creditFacade,
|
|
1517
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1518
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1519
|
+
functionName: isDecrease ? "decreaseDebt" : "increaseDebt",
|
|
1520
|
+
args: [change]
|
|
1521
|
+
})
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
#prepareAddCollateral(creditFacade, assets, permits) {
|
|
1525
|
+
const calls = assets.map(({ token, balance }) => {
|
|
1526
|
+
const p = permits[token];
|
|
1527
|
+
if (p) {
|
|
1528
|
+
return {
|
|
1529
|
+
target: creditFacade,
|
|
1530
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1531
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1532
|
+
functionName: "addCollateralWithPermit",
|
|
1533
|
+
args: [token, balance, p.deadline, p.v, p.r, p.s]
|
|
1534
|
+
})
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
return {
|
|
1538
|
+
target: creditFacade,
|
|
1539
|
+
callData: (0, import_viem.encodeFunctionData)({
|
|
1540
|
+
abi: import_generated.iCreditFacadeMulticallV310Abi,
|
|
1541
|
+
functionName: "addCollateral",
|
|
1542
|
+
args: [token, balance]
|
|
1543
|
+
})
|
|
1544
|
+
};
|
|
1545
|
+
});
|
|
1546
|
+
return calls;
|
|
1547
|
+
}
|
|
1548
|
+
/**
|
|
1549
|
+
* Returns addresses of market configurators
|
|
1550
|
+
*/
|
|
1551
|
+
get marketConfigurators() {
|
|
1552
|
+
return this.sdk.marketRegister.marketConfigurators.map((mc) => mc.address);
|
|
1553
|
+
}
|
|
1554
|
+
get rewardCompressor() {
|
|
1555
|
+
return this.sdk.addressProvider.mustGetLatest(
|
|
1556
|
+
import_constants.AP_REWARDS_COMPRESSOR,
|
|
1557
|
+
import_constants.VERSION_RANGE_310
|
|
1558
|
+
)[0];
|
|
1559
|
+
}
|
|
1560
|
+
get peripheryCompressor() {
|
|
1561
|
+
return this.sdk.addressProvider.mustGetLatest(
|
|
1562
|
+
import_constants.AP_PERIPHERY_COMPRESSOR,
|
|
1563
|
+
import_constants.VERSION_RANGE_310
|
|
1564
|
+
)[0];
|
|
1565
|
+
}
|
|
1566
|
+
get compressor() {
|
|
1567
|
+
if (!this.#compressor) {
|
|
1568
|
+
[this.#compressor] = this.sdk.addressProvider.mustGetLatest(
|
|
1569
|
+
import_constants.AP_CREDIT_ACCOUNT_COMPRESSOR,
|
|
1570
|
+
import_constants.VERSION_RANGE_310
|
|
1571
|
+
);
|
|
1572
|
+
this.logger?.debug(
|
|
1573
|
+
`credit account compressor address: ${this.#compressor}`
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
return this.#compressor;
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Wrapper that selects between credit facade and KYC factory
|
|
1580
|
+
* @param suite
|
|
1581
|
+
* @param to
|
|
1582
|
+
* @param calls
|
|
1583
|
+
* @param referralCode
|
|
1584
|
+
* @param kycOptions
|
|
1585
|
+
* @returns
|
|
1586
|
+
*/
|
|
1587
|
+
async #openCreditAccountTx(suite, to, calls, referralCode, kycOptions) {
|
|
1588
|
+
const marketSuite = this.sdk.marketRegister.findByPool(suite.pool);
|
|
1589
|
+
const factory = marketSuite.kycFactory;
|
|
1590
|
+
if (factory) {
|
|
1591
|
+
return factory.openCreditAccount(
|
|
1592
|
+
suite.creditManager.address,
|
|
1593
|
+
calls,
|
|
1594
|
+
kycOptions
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
return suite.creditFacade.openCreditAccount(to, calls, referralCode ?? 0n);
|
|
1598
|
+
}
|
|
1599
|
+
/**
|
|
1600
|
+
* Wrapper that selects between credit facade and KYC factory
|
|
1601
|
+
* @param suite
|
|
1602
|
+
* @param creditAccount
|
|
1603
|
+
* @param calls
|
|
1604
|
+
* @param kycOptions
|
|
1605
|
+
* @returns
|
|
1606
|
+
*/
|
|
1607
|
+
async #multicallTx(suite, creditAccount, calls, kycOptions) {
|
|
1608
|
+
const marketSuite = this.sdk.marketRegister.findByCreditManager(
|
|
1609
|
+
suite.creditManager.address
|
|
1610
|
+
);
|
|
1611
|
+
const factory = marketSuite.kycFactory;
|
|
1612
|
+
if (factory) {
|
|
1613
|
+
return factory.multicall(creditAccount, calls, kycOptions);
|
|
1614
|
+
}
|
|
1615
|
+
return suite.creditFacade.multicall(creditAccount, calls);
|
|
1616
|
+
}
|
|
1617
|
+
/**
|
|
1618
|
+
* Wrapper that selects between credit facade and KYC factory
|
|
1619
|
+
* @param suite
|
|
1620
|
+
* @param creditAccount
|
|
1621
|
+
* @param calls
|
|
1622
|
+
* @param operation
|
|
1623
|
+
* @param kycOptions
|
|
1624
|
+
* @returns
|
|
1625
|
+
*/
|
|
1626
|
+
async #closeCreditAccountTx(suite, creditAccount, calls, operation, kycOptions) {
|
|
1627
|
+
const marketSuite = this.sdk.marketRegister.findByCreditManager(
|
|
1628
|
+
suite.creditManager.address
|
|
1629
|
+
);
|
|
1630
|
+
const factory = marketSuite.kycFactory;
|
|
1631
|
+
if (operation === "close") {
|
|
1632
|
+
if (factory) {
|
|
1633
|
+
throw new Error(
|
|
1634
|
+
"CloseOptions=close is not supported for KYC underlying credit accounts"
|
|
1635
|
+
);
|
|
1636
|
+
}
|
|
1637
|
+
return suite.creditFacade.closeCreditAccount(creditAccount, calls);
|
|
1638
|
+
}
|
|
1639
|
+
if (factory) {
|
|
1640
|
+
return factory.multicall(creditAccount, calls, kycOptions);
|
|
1641
|
+
}
|
|
1642
|
+
return suite.creditFacade.multicall(creditAccount, calls);
|
|
1643
|
+
}
|
|
220
1644
|
}
|
|
221
1645
|
// Annotate the CommonJS export names for ESM import in node:
|
|
222
1646
|
0 && (module.exports = {
|
|
223
|
-
|
|
1647
|
+
CreditAccountsServiceV310,
|
|
1648
|
+
getWithdrawalCompressorAddress
|
|
224
1649
|
});
|