@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.
Files changed (40) hide show
  1. package/dist/cjs/dev/claimDSToken.js +208 -0
  2. package/dist/cjs/plugins/accounts/AccountsPlugin.js +1 -2
  3. package/dist/cjs/sdk/OnchainSDK.js +12 -0
  4. package/dist/cjs/sdk/accounts/CreditAccountsServiceV310.js +1448 -23
  5. package/dist/cjs/sdk/accounts/index.js +0 -4
  6. package/dist/cjs/sdk/market/credit/CreditFacadeV310Contract.js +6 -0
  7. package/dist/cjs/sdk/market/kyc/securitize/SecuritizeKYCFactory.js +19 -2
  8. package/dist/cjs/sdk/market/kyc/securitize/types.js +20 -0
  9. package/dist/cjs/sdk/market/oracle/PriceOracleV310Contract.js +26 -0
  10. package/dist/cjs/sdk/market/pricefeeds/PriceFeedsRegister.js +3 -0
  11. package/dist/esm/dev/claimDSToken.js +187 -0
  12. package/dist/esm/plugins/accounts/AccountsPlugin.js +2 -7
  13. package/dist/esm/sdk/OnchainSDK.js +14 -0
  14. package/dist/esm/sdk/accounts/CreditAccountsServiceV310.js +1466 -23
  15. package/dist/esm/sdk/accounts/index.js +0 -2
  16. package/dist/esm/sdk/accounts/multicall-utils.js +1 -5
  17. package/dist/esm/sdk/market/credit/CreditFacadeV310Contract.js +6 -0
  18. package/dist/esm/sdk/market/kyc/securitize/SecuritizeKYCFactory.js +21 -2
  19. package/dist/esm/sdk/market/kyc/securitize/types.js +12 -0
  20. package/dist/esm/sdk/market/oracle/PriceOracleV310Contract.js +26 -0
  21. package/dist/esm/sdk/market/pricefeeds/PriceFeedsRegister.js +3 -0
  22. package/dist/types/dev/claimDSToken.d.ts +34 -0
  23. package/dist/types/sdk/OnchainSDK.d.ts +10 -0
  24. package/dist/types/sdk/accounts/CreditAccountsServiceV310.d.ts +181 -3
  25. package/dist/types/sdk/accounts/index.d.ts +0 -2
  26. package/dist/types/sdk/accounts/types.d.ts +56 -0
  27. package/dist/types/sdk/market/credit/CreditFacadeV310Contract.d.ts +2 -0
  28. package/dist/types/sdk/market/kyc/securitize/SecuritizeKYCFactory.d.ts +1 -1
  29. package/dist/types/sdk/market/kyc/securitize/types.d.ts +37 -22
  30. package/dist/types/sdk/market/oracle/PriceOracleBaseContract.d.ts +5 -0
  31. package/dist/types/sdk/market/oracle/PriceOracleV310Contract.d.ts +4 -0
  32. package/dist/types/sdk/market/oracle/types.d.ts +9 -0
  33. package/dist/types/sdk/pools/types.d.ts +19 -0
  34. package/package.json +1 -1
  35. package/dist/cjs/sdk/accounts/AbstractCreditAccountsService.js +0 -1411
  36. package/dist/cjs/sdk/accounts/createCreditAccountService.js +0 -35
  37. package/dist/esm/sdk/accounts/AbstractCreditAccountsService.js +0 -1405
  38. package/dist/esm/sdk/accounts/createCreditAccountService.js +0 -11
  39. package/dist/types/sdk/accounts/AbstractCreditAccountsService.d.ts +0 -226
  40. 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
- CreditAccountServiceV310: () => CreditAccountServiceV310
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 import_math = require("../constants/math.js");
27
- var import_AbstractCreditAccountsService = require("./AbstractCreditAccountsService.js");
28
- class CreditAccountServiceV310 extends import_AbstractCreditAccountsService.AbstractCreditAccountService {
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.prependPriceUpdates(
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.multicallTx(cm, targetContract.creditAccount, calls) : void 0;
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.prepareWithdrawToken(
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.prepareUpdateQuotas(creditAccount.creditFacade, {
1186
+ ...this.#prepareUpdateQuotas(creditAccount.creditFacade, {
94
1187
  minQuota,
95
1188
  averageQuota
96
1189
  })
97
1190
  ];
98
- const calls = await this.prependPriceUpdates(
1191
+ const calls = await this.#prependPriceUpdates(
99
1192
  creditAccount.creditManager,
100
1193
  operationCalls,
101
1194
  creditAccount
102
1195
  );
103
- const tx = await this.multicallTx(cm, creditAccount.creditAccount, calls);
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.prepareAddCollateral(ca.creditFacade, addCollateral, permits),
1221
+ ...this.#prepareAddCollateral(ca.creditFacade, addCollateral, permits),
129
1222
  ...wrapCalls,
130
- ...this.prepareDisableQuotas(ca),
131
- ...this.prepareDecreaseDebt(ca),
1223
+ ...this.#prepareDisableQuotas(ca),
1224
+ ...this.#prepareDecreaseDebt(ca),
132
1225
  ...unwrapCalls,
133
1226
  ...claimPath.calls,
134
1227
  ...assetsToWithdraw.map(
135
- (t) => this.prepareWithdrawToken(ca.creditFacade, t.token, import_math.MAX_UINT256, to)
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.prependPriceUpdates(ca.creditManager, operationCalls, ca);
139
- const tx = await this.closeCreditAccountTx(
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.prepareAddCollateral(ca.creditFacade, addCollateral, permits),
1260
+ ...this.#prepareAddCollateral(ca.creditFacade, addCollateral, permits),
168
1261
  ...claimPath.calls,
169
1262
  ...wrapCalls,
170
1263
  ...assetsToWithdraw.map(
171
- (t) => this.prepareWithdrawToken(ca.creditFacade, t.token, import_math.MAX_UINT256, to)
1264
+ (t) => this.#prepareWithdrawToken(ca.creditFacade, t.token, import_constants.MAX_UINT256, to)
172
1265
  )
173
1266
  ];
174
- const calls = await this.prependPriceUpdates(
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.prepareUpdateQuotas(ca.creditFacade, { minQuota, averageQuota })
1303
+ ...this.#prepareUpdateQuotas(ca.creditFacade, { minQuota, averageQuota })
211
1304
  ];
212
- const calls = await this.prependPriceUpdates(
1305
+ const calls = await this.#prependPriceUpdates(
213
1306
  ca.creditManager,
214
1307
  operationCalls,
215
1308
  ca
216
1309
  );
217
- const tx = await this.multicallTx(cm, ca.creditAccount, calls);
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
- CreditAccountServiceV310
1647
+ CreditAccountsServiceV310,
1648
+ getWithdrawalCompressorAddress
224
1649
  });