@gearbox-protocol/sdk 13.4.0 → 13.5.0-next.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 (90) hide show
  1. package/dist/cjs/abi/310/iSecuritizeDegenNFT.js +263 -0
  2. package/dist/cjs/abi/310/iSecuritizeKYCFactory.js +278 -0
  3. package/dist/cjs/{sdk/pools/PoolServiceV310.js → abi/iStateSerializer.js} +14 -8
  4. package/dist/cjs/dev/AccountOpener.js +45 -5
  5. package/dist/cjs/sdk/accounts/AbstractCreditAccountsService.js +375 -13
  6. package/dist/cjs/sdk/accounts/CreditAccountsServiceV310.js +16 -5
  7. package/dist/cjs/sdk/base/ChainContractsRegister.js +1 -1
  8. package/dist/cjs/sdk/base/TokensMeta.js +255 -32
  9. package/dist/cjs/sdk/base/index.js +2 -0
  10. package/dist/cjs/sdk/{constants/phantom-tokens.js → base/token-types.js} +9 -3
  11. package/dist/cjs/sdk/chain/chains.js +2 -1
  12. package/dist/cjs/sdk/constants/index.js +0 -2
  13. package/dist/cjs/sdk/market/MarketRegister.js +5 -2
  14. package/dist/cjs/sdk/market/MarketSuite.js +6 -0
  15. package/dist/cjs/{plugins/zappers/extraZappers.js → sdk/market/ZapperRegister.js} +110 -6
  16. package/dist/cjs/sdk/market/index.js +3 -1
  17. package/dist/cjs/sdk/market/pool/PoolSuite.js +3 -0
  18. package/dist/cjs/sdk/market/pool/PoolV310Contract.js +17 -2
  19. package/dist/cjs/sdk/market/pool/SecuritizeKYCFactory.js +97 -0
  20. package/dist/cjs/sdk/market/pool/index.js +4 -0
  21. package/dist/cjs/sdk/pools/PoolService.js +391 -0
  22. package/dist/cjs/sdk/pools/index.js +2 -4
  23. package/dist/esm/abi/310/iSecuritizeDegenNFT.js +239 -0
  24. package/dist/esm/abi/310/iSecuritizeKYCFactory.js +254 -0
  25. package/dist/esm/abi/iStateSerializer.js +12 -0
  26. package/dist/esm/dev/AccountOpener.js +47 -6
  27. package/dist/esm/sdk/accounts/AbstractCreditAccountsService.js +375 -13
  28. package/dist/esm/sdk/accounts/CreditAccountsServiceV310.js +16 -5
  29. package/dist/esm/sdk/base/ChainContractsRegister.js +1 -1
  30. package/dist/esm/sdk/base/TokensMeta.js +261 -32
  31. package/dist/esm/sdk/base/index.js +1 -0
  32. package/dist/esm/sdk/{constants/phantom-tokens.js → base/token-types.js} +4 -0
  33. package/dist/esm/sdk/chain/chains.js +2 -1
  34. package/dist/esm/sdk/constants/index.js +0 -1
  35. package/dist/esm/sdk/market/MarketRegister.js +5 -2
  36. package/dist/esm/sdk/market/MarketSuite.js +6 -0
  37. package/dist/esm/{plugins/zappers/extraZappers.js → sdk/market/ZapperRegister.js} +109 -2
  38. package/dist/esm/sdk/market/index.js +1 -0
  39. package/dist/esm/sdk/market/pool/PoolSuite.js +3 -0
  40. package/dist/esm/sdk/market/pool/PoolV310Contract.js +17 -2
  41. package/dist/esm/sdk/market/pool/SecuritizeKYCFactory.js +73 -0
  42. package/dist/esm/sdk/market/pool/index.js +2 -0
  43. package/dist/esm/sdk/pools/PoolService.js +371 -0
  44. package/dist/esm/sdk/pools/index.js +1 -2
  45. package/dist/types/abi/310/iSecuritizeDegenNFT.d.ts +324 -0
  46. package/dist/types/abi/310/iSecuritizeKYCFactory.d.ts +322 -0
  47. package/dist/types/abi/iStateSerializer.d.ts +11 -0
  48. package/dist/types/sdk/accounts/AbstractCreditAccountsService.d.ts +114 -3
  49. package/dist/types/sdk/accounts/CreditAccountsServiceV310.d.ts +1 -1
  50. package/dist/types/sdk/accounts/types.d.ts +96 -6
  51. package/dist/types/sdk/base/TokensMeta.d.ts +34 -21
  52. package/dist/types/sdk/base/index.d.ts +1 -0
  53. package/dist/types/sdk/base/token-types.d.ts +33 -0
  54. package/dist/types/sdk/base/types.d.ts +0 -7
  55. package/dist/types/sdk/chain/chains.d.ts +1 -1
  56. package/dist/types/sdk/constants/index.d.ts +0 -1
  57. package/dist/types/sdk/market/MarketRegister.d.ts +2 -2
  58. package/dist/types/sdk/market/MarketSuite.d.ts +3 -0
  59. package/dist/types/sdk/market/ZapperRegister.d.ts +17 -0
  60. package/dist/types/sdk/market/index.d.ts +1 -0
  61. package/dist/types/sdk/market/pool/PoolSuite.d.ts +2 -0
  62. package/dist/types/sdk/market/pool/PoolV310Contract.d.ts +6 -2
  63. package/dist/types/sdk/market/pool/SecuritizeKYCFactory.d.ts +345 -0
  64. package/dist/types/sdk/market/pool/index.d.ts +2 -0
  65. package/dist/types/sdk/market/types.d.ts +10 -0
  66. package/dist/types/sdk/pools/PoolService.d.ts +14 -0
  67. package/dist/types/sdk/pools/index.d.ts +1 -2
  68. package/dist/types/sdk/pools/types.d.ts +85 -111
  69. package/package.json +1 -1
  70. package/dist/cjs/plugins/zappers/ZappersPlugin.js +0 -144
  71. package/dist/cjs/plugins/zappers/index.js +0 -26
  72. package/dist/cjs/plugins/zappers/package.json +0 -1
  73. package/dist/cjs/sdk/pools/AbstractPoolService.js +0 -143
  74. package/dist/cjs/sdk/pools/createPoolService.js +0 -35
  75. package/dist/esm/plugins/zappers/ZappersPlugin.js +0 -126
  76. package/dist/esm/plugins/zappers/index.js +0 -3
  77. package/dist/esm/plugins/zappers/package.json +0 -1
  78. package/dist/esm/sdk/pools/AbstractPoolService.js +0 -119
  79. package/dist/esm/sdk/pools/PoolServiceV310.js +0 -6
  80. package/dist/esm/sdk/pools/createPoolService.js +0 -11
  81. package/dist/types/plugins/zappers/ZappersPlugin.d.ts +0 -18
  82. package/dist/types/plugins/zappers/extraZappers.d.ts +0 -6
  83. package/dist/types/plugins/zappers/index.d.ts +0 -3
  84. package/dist/types/plugins/zappers/types.d.ts +0 -12
  85. package/dist/types/sdk/constants/phantom-tokens.d.ts +0 -2
  86. package/dist/types/sdk/pools/AbstractPoolService.d.ts +0 -21
  87. package/dist/types/sdk/pools/PoolServiceV310.d.ts +0 -4
  88. package/dist/types/sdk/pools/createPoolService.d.ts +0 -3
  89. /package/dist/cjs/{plugins/zappers → sdk/market}/types.js +0 -0
  90. /package/dist/esm/{plugins/zappers → sdk/market}/types.js +0 -0
@@ -1,3 +1,4 @@
1
+ import { ierc4626AdapterAbi } from "@gearbox-protocol/integrations-v3";
1
2
  import { encodeFunctionData, getContract } from "viem";
2
3
  import {
3
4
  iBotListV310Abi,
@@ -90,6 +91,23 @@ class AbstractCreditAccountService extends SDKConstruct {
90
91
  });
91
92
  return cad;
92
93
  }
94
+ /**
95
+ * Returns credit account data for a single account with the investor address resolved.
96
+ * Loads CA via getCreditAccountData; for KYC underlyings fetches the investor from the KYC factory's getInvestor(creditAccount), otherwise uses the account owner.
97
+ * @param account - Credit account address
98
+ * @param blockNumber - Optional block number for the read
99
+ * @returns CreditAccountDataWithInvestor (CA data + investor address), or undefined if the account is not found
100
+ */
101
+ async getCreditAccountDataWithInvestor(account, blockNumber) {
102
+ const ca = await this.getCreditAccountData(account, blockNumber);
103
+ if (!ca) return ca;
104
+ const marketSuite = this.sdk.marketRegister.findByCreditManager(
105
+ ca.creditManager
106
+ );
107
+ const factory = await marketSuite.getKYCFactory();
108
+ const investor = factory ? await factory.getInvestor(ca.creditAccount, true) : void 0;
109
+ return { ...ca, investor: investor ?? ca.owner };
110
+ }
93
111
  /**
94
112
  * {@inheritDoc ICreditAccountsService.getCreditAccounts}
95
113
  **/
@@ -144,6 +162,75 @@ class AbstractCreditAccountService extends SDKConstruct {
144
162
  );
145
163
  return allCAs.sort((a, b) => Number(a.healthFactor - b.healthFactor));
146
164
  }
165
+ /**
166
+ * Returns all credit accounts matching the filter, with investor set on each item.
167
+ * Delegates to getCreditAccounts; when options.owner is set, also loads KYC credit accounts for that owner and merges them into the list. Result is sorted by health factor ascending.
168
+ * @param options - Filter options (owner, creditManager, health factor, etc.)
169
+ * @param blockNumber - Optional block number for the read
170
+ * @returns Array of credit accounts (with investor field), sorted by health factor ascending
171
+ */
172
+ async getCreditAccountsWithInvestor(options, blockNumber) {
173
+ const { owner, ignoreReservePrices = false } = options ?? {};
174
+ const priceUpdate = await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs(
175
+ ignoreReservePrices ? { main: true } : void 0
176
+ );
177
+ const { txs: priceUpdateTxs } = priceUpdate;
178
+ const [common, kyc] = await Promise.all([
179
+ this.getCreditAccounts(options, blockNumber),
180
+ owner ? this.getKYCCreditAccountsOfOwner(owner, priceUpdateTxs, blockNumber) : void 0
181
+ ]);
182
+ const allCAs = common.map(
183
+ (ca) => ({
184
+ ...ca,
185
+ investor: owner || ca.owner
186
+ })
187
+ );
188
+ allCAs.push(...kyc || []);
189
+ return allCAs.sort((a, b) => Number(a.healthFactor - b.healthFactor));
190
+ }
191
+ async getKYCCreditAccountsOfOwner(owner, priceUpdateTxs, blockNumber) {
192
+ const suites = this.marketConfigurators.map((mc) => {
193
+ const suite = this.sdk.marketRegister.markets.find(
194
+ (m) => m.configurator.address === mc
195
+ );
196
+ return suite;
197
+ });
198
+ const kycCAAddresses = await this.getKYCCaOfInvestor(owner, suites);
199
+ const kycCAs = await this.loadSpecifiedAccounts(
200
+ kycCAAddresses,
201
+ priceUpdateTxs,
202
+ blockNumber
203
+ );
204
+ return kycCAs.map((ca) => ({
205
+ ...ca,
206
+ investor: owner
207
+ }));
208
+ }
209
+ /**
210
+ * Loads credit account data for the given addresses using simulateWithPriceUpdates.
211
+ * Applies the provided price update txs before reading, so returned data is consistent with up-to-date prices.
212
+ * @param accounts - Credit account addresses to load
213
+ * @param priceUpdateTxs - Price feed update txs to simulate before the read (e.g. from generatePriceFeedsUpdateTxs)
214
+ * @param blockNumber - Optional block number for the read
215
+ * @returns Array of CreditAccountData in the same order as accounts (throws if any getCreditAccountData call reverts)
216
+ */
217
+ async loadSpecifiedAccounts(accounts, priceUpdateTxs, blockNumber) {
218
+ if (accounts.length === 0) return [];
219
+ const list = await simulateWithPriceUpdates(this.client, {
220
+ priceUpdates: priceUpdateTxs,
221
+ contracts: accounts.map(
222
+ (account) => ({
223
+ abi: creditAccountCompressorAbi,
224
+ address: this.#compressor,
225
+ functionName: "getCreditAccountData",
226
+ args: [account]
227
+ })
228
+ ),
229
+ blockNumber,
230
+ gas: this.sdk.gasLimit
231
+ });
232
+ return list;
233
+ }
147
234
  /**
148
235
  * {@inheritDoc ICreditAccountsService.getRewards}
149
236
  **/
@@ -353,6 +440,13 @@ class AbstractCreditAccountService extends SDKConstruct {
353
440
  closePath
354
441
  }) {
355
442
  const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
443
+ await this.sdk.tokensMeta.loadTokenData(cm.underlying);
444
+ const underlying = this.sdk.tokensMeta.mustGet(cm.underlying);
445
+ if (this.sdk.tokensMeta.isKYCUnderlying(underlying)) {
446
+ throw new Error(
447
+ "closeCreditAccount is not supported for KYC underlying credit accounts"
448
+ );
449
+ }
356
450
  const routerCloseResult = closePath || await this.sdk.routerFor(ca).findBestClosePath({
357
451
  creditAccount: ca,
358
452
  creditManager: cm.creditManager,
@@ -367,7 +461,12 @@ class AbstractCreditAccountService extends SDKConstruct {
367
461
  )
368
462
  ];
369
463
  const calls = operation === "close" ? operationCalls : await this.prependPriceUpdates(ca.creditManager, operationCalls, ca);
370
- const tx = operation === "close" ? cm.creditFacade.closeCreditAccount(ca.creditAccount, calls) : cm.creditFacade.multicall(ca.creditAccount, calls);
464
+ const tx = await this.closeCreditAccountTx(
465
+ cm,
466
+ ca.creditAccount,
467
+ calls,
468
+ operation
469
+ );
371
470
  return { tx, calls, routerCloseResult, creditFacade: cm.creditFacade };
372
471
  }
373
472
  /**
@@ -390,7 +489,7 @@ class AbstractCreditAccountService extends SDKConstruct {
390
489
  operationCalls,
391
490
  creditAccount
392
491
  );
393
- const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
492
+ const tx = await this.multicallTx(cm, creditAccount.creditAccount, calls);
394
493
  return { tx, calls, creditFacade: cm.creditFacade };
395
494
  }
396
495
  /**
@@ -423,7 +522,7 @@ class AbstractCreditAccountService extends SDKConstruct {
423
522
  operationCalls,
424
523
  creditAccount
425
524
  );
426
- const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
525
+ const tx = await this.multicallTx(cm, creditAccount.creditAccount, calls);
427
526
  tx.value = ethAmount.toString(10);
428
527
  return { tx, calls, creditFacade: cm.creditFacade };
429
528
  }
@@ -433,7 +532,7 @@ class AbstractCreditAccountService extends SDKConstruct {
433
532
  async changeDebt({
434
533
  creditAccount,
435
534
  amount,
436
- addCollateral
535
+ collateral
437
536
  }) {
438
537
  if (amount === 0n) {
439
538
  throw new Error("debt increase or decrease must be non-zero");
@@ -443,18 +542,28 @@ class AbstractCreditAccountService extends SDKConstruct {
443
542
  const cm = this.sdk.marketRegister.findCreditManager(
444
543
  creditAccount.creditManager
445
544
  );
446
- const addCollateralCalls = addCollateral && isDecrease ? this.prepareAddCollateral(
545
+ const addCollateralCalls = collateral && isDecrease ? this.prepareAddCollateral(
447
546
  creditAccount.creditFacade,
448
547
  [
449
548
  {
450
- token: creditAccount.underlying,
451
- balance: change
549
+ token: collateral[0].token,
550
+ balance: collateral[0].balance
452
551
  }
453
552
  ],
454
553
  {}
455
554
  ) : [];
555
+ const unwrapCalls = collateral && isDecrease ? await this.getKYCUnwrapCalls(
556
+ collateral[0].balance,
557
+ creditAccount.creditManager
558
+ ) || [] : [];
559
+ if (addCollateralCalls.length > 0 && unwrapCalls.length === 0 && collateral && collateral?.[0].token !== creditAccount.underlying) {
560
+ throw new Error(
561
+ "Can't use collateral other than underlying for non KYC market"
562
+ );
563
+ }
456
564
  const operationCalls = [
457
565
  ...addCollateralCalls,
566
+ ...unwrapCalls,
458
567
  this.#prepareChangeDebt(creditAccount.creditFacade, change, isDecrease)
459
568
  ];
460
569
  const calls = await this.prependPriceUpdates(
@@ -462,7 +571,7 @@ class AbstractCreditAccountService extends SDKConstruct {
462
571
  operationCalls,
463
572
  creditAccount
464
573
  );
465
- const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
574
+ const tx = await this.multicallTx(cm, creditAccount.creditAccount, calls);
466
575
  return { tx, calls, creditFacade: cm.creditFacade };
467
576
  }
468
577
  /**
@@ -490,7 +599,7 @@ class AbstractCreditAccountService extends SDKConstruct {
490
599
  operationCalls,
491
600
  creditAccount
492
601
  );
493
- const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
602
+ const tx = await this.multicallTx(cm, creditAccount.creditAccount, calls);
494
603
  return { tx, calls, creditFacade: cm.creditFacade };
495
604
  }
496
605
  /**
@@ -598,7 +707,7 @@ class AbstractCreditAccountService extends SDKConstruct {
598
707
  operationCalls,
599
708
  creditAccount
600
709
  );
601
- const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
710
+ const tx = await this.multicallTx(cm, creditAccount.creditAccount, calls);
602
711
  return { tx, calls, creditFacade: cm.creditFacade };
603
712
  }
604
713
  /**
@@ -659,9 +768,28 @@ class AbstractCreditAccountService extends SDKConstruct {
659
768
  operationCalls,
660
769
  creditAccount
661
770
  );
662
- const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
771
+ const tx = await this.multicallTx(cm, creditAccount.creditAccount, calls);
663
772
  return { tx, calls, creditFacade: cm.creditFacade };
664
773
  }
774
+ /**
775
+ * Returns address to which approval should be given on collateral token
776
+ * It's credit manager for classical markets and special wallet for KYC markets
777
+ * @param options - {@link GetApprovalAddressProps}
778
+ * @returns
779
+ **/
780
+ async getApprovalAddress(options) {
781
+ const { creditManager } = options;
782
+ const suite = this.sdk.marketRegister.findCreditManager(creditManager);
783
+ const marketSuite = this.sdk.marketRegister.findByPool(suite.pool);
784
+ const factory = await marketSuite.getKYCFactory();
785
+ if (factory) {
786
+ if ("creditAccount" in options) {
787
+ return factory.getWallet(options.creditAccount);
788
+ }
789
+ return factory.precomputeWalletAddress(creditManager, options.borrower);
790
+ }
791
+ return suite.creditManager.address;
792
+ }
665
793
  /**
666
794
  * {@inheritDoc ICreditAccountsService.openCA}
667
795
  **/
@@ -693,6 +821,7 @@ class AbstractCreditAccountService extends SDKConstruct {
693
821
  this.#prepareIncreaseDebt(cm.creditFacade, debt),
694
822
  ...this.prepareAddCollateral(cm.creditFacade, collateral, permits),
695
823
  ...openPathCalls,
824
+ // path from underlying to withdrawal token
696
825
  ...tokenToWithdraw ? [
697
826
  this.prepareWithdrawToken(
698
827
  cm.creditFacade,
@@ -710,9 +839,9 @@ class AbstractCreditAccountService extends SDKConstruct {
710
839
  const calls = await this.prependPriceUpdates(cm.address, operationCalls);
711
840
  let tx;
712
841
  if (reopenCreditAccount) {
713
- tx = await cmSuite.creditFacade.multicall(reopenCreditAccount, calls);
842
+ tx = await this.multicallTx(cmSuite, reopenCreditAccount, calls);
714
843
  } else {
715
- tx = cmSuite.creditFacade.openCreditAccount(to, calls, referralCode);
844
+ tx = await this.openCreditAccountTx(cmSuite, to, calls, referralCode);
716
845
  }
717
846
  tx.value = ethAmount.toString(10);
718
847
  return { calls, tx, creditFacade: cmSuite.creditFacade };
@@ -795,6 +924,131 @@ class AbstractCreditAccountService extends SDKConstruct {
795
924
  return resp;
796
925
  }
797
926
  /**
927
+ * Returns multicall entries to redeem (unwrap) KYC ERC-4626 vault shares into underlying for the given credit manager.
928
+ * Used when withdrawing debt from a KYC market: redeems adapter vault shares so the underlying can be withdrawn.
929
+ * Only applies when the credit manager's underlying is KYC-gated and has an ERC-4626 adapter configured.
930
+ * @param amount - Number of vault shares (adapter tokens) to redeem
931
+ * @param creditManager - Credit manager address
932
+ * @returns Array of MultiCall to pass to credit facade multicall, or undefined if underlying is not KYC or no adapter is configured
933
+ */
934
+ async getKYCUnwrapCalls(amount, creditManager) {
935
+ const suite = this.sdk.marketRegister.findCreditManager(creditManager);
936
+ const meta = this.sdk.tokensMeta.mustGet(suite.underlying);
937
+ if (!this.sdk.tokensMeta.isKYCUnderlying(meta)) {
938
+ return void 0;
939
+ }
940
+ const adapter = suite.creditManager.adapters.get(meta.addr);
941
+ const adapterAddress = adapter?.address;
942
+ if (!adapterAddress) {
943
+ return void 0;
944
+ }
945
+ const mc = [
946
+ {
947
+ target: adapterAddress,
948
+ callData: encodeFunctionData({
949
+ abi: ierc4626AdapterAbi,
950
+ functionName: "redeem",
951
+ args: [amount, ADDRESS_0X0, ADDRESS_0X0]
952
+ })
953
+ }
954
+ ];
955
+ return mc;
956
+ }
957
+ /**
958
+ * Returns multicall entries to deposit (wrap) underlying into KYC ERC-4626 vault shares for the given credit manager.
959
+ * Used when adding debt on a KYC market: deposits underlying into the adapter vault so shares are minted on the account.
960
+ * Only applies when the credit manager's underlying is KYC-gated and has an ERC-4626 adapter configured.
961
+ * @param amount - Amount of underlying assets to deposit into the vault (in underlying decimals)
962
+ * @param creditManager - Credit manager address
963
+ * @returns Array of MultiCall to pass to credit facade multicall, or undefined if underlying is not KYC or no adapter is configured
964
+ */
965
+ async getKYCWrapCalls(amount, creditManager) {
966
+ const suite = this.sdk.marketRegister.findCreditManager(creditManager);
967
+ const meta = this.sdk.tokensMeta.mustGet(suite.underlying);
968
+ if (!this.sdk.tokensMeta.isKYCUnderlying(meta)) {
969
+ return void 0;
970
+ }
971
+ const adapter = suite.creditManager.adapters.get(meta.addr);
972
+ const adapterAddress = adapter?.address;
973
+ if (!adapterAddress) {
974
+ return void 0;
975
+ }
976
+ const mc = [
977
+ {
978
+ target: adapterAddress,
979
+ callData: encodeFunctionData({
980
+ abi: ierc4626AdapterAbi,
981
+ functionName: "deposit",
982
+ args: [amount, ADDRESS_0X0]
983
+ })
984
+ }
985
+ ];
986
+ return mc;
987
+ }
988
+ /**
989
+ * Returns multicall entries to call redeemDiff on the KYC ERC-4626 adapter for the given credit manager.
990
+ * Redeems the leftover vault shares (e.g. after repaying debt) so the account does not hold excess KYC vault tokens.
991
+ * Only applies when the credit manager's underlying is KYC-gated and has an ERC-4626 adapter configured.
992
+ * @param amount - Leftover vault share amount to redeem (in adapter/vault decimals)
993
+ * @param creditManager - Credit manager address
994
+ * @returns Array of MultiCall to pass to credit facade multicall, or undefined if underlying is not KYC or no adapter is configured
995
+ */
996
+ async getRedeemDiffCalls(amount, creditManager) {
997
+ const suite = this.sdk.marketRegister.findCreditManager(creditManager);
998
+ const meta = this.sdk.tokensMeta.mustGet(suite.underlying);
999
+ if (!this.sdk.tokensMeta.isKYCUnderlying(meta)) {
1000
+ return void 0;
1001
+ }
1002
+ const adapter = suite.creditManager.adapters.get(meta.addr);
1003
+ const adapterAddress = adapter?.address;
1004
+ if (!adapterAddress) {
1005
+ return void 0;
1006
+ }
1007
+ const mc = [
1008
+ {
1009
+ target: adapterAddress,
1010
+ callData: encodeFunctionData({
1011
+ abi: ierc4626AdapterAbi,
1012
+ functionName: "redeemDiff",
1013
+ args: [amount]
1014
+ })
1015
+ }
1016
+ ];
1017
+ return mc;
1018
+ }
1019
+ /**
1020
+ * Returns multicall entries to call depositDiff on the KYC ERC-4626 adapter for the given credit manager.
1021
+ * Deposits the leftover underlying (e.g. after decreasing debt) into the vault so the account does not hold excess underlying.
1022
+ * Only applies when the credit manager's underlying is KYC-gated and has an ERC-4626 adapter configured.
1023
+ * @param amount - Leftover underlying amount to deposit into the vault (in underlying decimals)
1024
+ * @param creditManager - Credit manager address
1025
+ * @returns Array of MultiCall to pass to credit facade multicall, or undefined if underlying is not KYC or no adapter is configured
1026
+ */
1027
+ async getDepositDiffCalls(amount, creditManager) {
1028
+ const suite = this.sdk.marketRegister.findCreditManager(creditManager);
1029
+ const meta = this.sdk.tokensMeta.mustGet(suite.underlying);
1030
+ if (!this.sdk.tokensMeta.isKYCUnderlying(meta)) {
1031
+ return void 0;
1032
+ }
1033
+ const adapter = suite.creditManager.adapters.get(meta.addr);
1034
+ const adapterAddress = adapter?.address;
1035
+ if (!adapterAddress) {
1036
+ return void 0;
1037
+ }
1038
+ const mc = [
1039
+ {
1040
+ target: adapterAddress,
1041
+ callData: encodeFunctionData({
1042
+ abi: ierc4626AdapterAbi,
1043
+ functionName: "depositDiff",
1044
+ args: [amount]
1045
+ })
1046
+ }
1047
+ ];
1048
+ return mc;
1049
+ }
1050
+ /**
1051
+ * Returns raw txs that are needed to update all price feeds so that all credit accounts (possibly from different markets) compute
798
1052
  * {@inheritDoc ICreditAccountsService.getOnDemandPriceUpdates}
799
1053
  **/
800
1054
  async getOnDemandPriceUpdates(account, ignoreReservePrices) {
@@ -1055,6 +1309,114 @@ class AbstractCreditAccountService extends SDKConstruct {
1055
1309
  VERSION_RANGE_310
1056
1310
  )[0];
1057
1311
  }
1312
+ /**
1313
+ * Wrapper that selects between credit facade and KYC factory
1314
+ * @param suite
1315
+ * @param to
1316
+ * @param calls
1317
+ * @param referralCode
1318
+ * @returns
1319
+ */
1320
+ async openCreditAccountTx(suite, to, calls, referralCode) {
1321
+ const marketSuite = this.sdk.marketRegister.findByPool(suite.pool);
1322
+ const factory = await marketSuite.getKYCFactory();
1323
+ if (factory) {
1324
+ const tokensToRegister = await factory.getDSTokens();
1325
+ return factory.openCreditAccount(
1326
+ suite.creditManager.address,
1327
+ calls,
1328
+ tokensToRegister
1329
+ );
1330
+ }
1331
+ return suite.creditFacade.openCreditAccount(to, calls, referralCode ?? 0n);
1332
+ }
1333
+ /**
1334
+ * Wrapper that selects between credit facade and KYC factory
1335
+ * @param suite
1336
+ * @param creditAccount
1337
+ * @param calls
1338
+ * @returns
1339
+ */
1340
+ async multicallTx(suite, creditAccount, calls) {
1341
+ const marketSuite = this.sdk.marketRegister.findByCreditManager(
1342
+ suite.creditManager.address
1343
+ );
1344
+ const factory = await marketSuite.getKYCFactory();
1345
+ if (factory) {
1346
+ const tokensToRegister = [];
1347
+ return factory.multicall(creditAccount, calls, tokensToRegister);
1348
+ }
1349
+ return suite.creditFacade.multicall(creditAccount, calls);
1350
+ }
1351
+ /**
1352
+ * Wrapper that selects between credit facade and KYC factory
1353
+ * @param suite
1354
+ * @param creditAccount
1355
+ * @param calls
1356
+ * @param operation
1357
+ * @returns
1358
+ */
1359
+ async closeCreditAccountTx(suite, creditAccount, calls, operation) {
1360
+ const marketSuite = this.sdk.marketRegister.findByCreditManager(
1361
+ suite.creditManager.address
1362
+ );
1363
+ const factory = await marketSuite.getKYCFactory();
1364
+ if (operation === "close") {
1365
+ if (factory) {
1366
+ throw new Error(
1367
+ "CloseOptions=close is not supported for KYC underlying credit accounts"
1368
+ );
1369
+ }
1370
+ return suite.creditFacade.closeCreditAccount(creditAccount, calls);
1371
+ }
1372
+ if (factory) {
1373
+ const tokensToRegister = [];
1374
+ return factory.multicall(creditAccount, calls, tokensToRegister);
1375
+ }
1376
+ return suite.creditFacade.multicall(creditAccount, calls);
1377
+ }
1378
+ /**
1379
+ * Returns all KYC credit account addresses for an investor across the given market suites.
1380
+ * Resolves KYC factory per suite, then multicalls each factory's getCreditAccounts(investor).
1381
+ * @param investor - Owner address to query
1382
+ * @param suites - Market suites (KYC factories are resolved for each; undefined entries are skipped)
1383
+ * @returns Flat array of credit account addresses from all KYC markets
1384
+ */
1385
+ async getKYCCaOfInvestor(investor, suites) {
1386
+ if (suites.length === 0 || investor === ADDRESS_0X0) return [];
1387
+ const factories = await Promise.all(
1388
+ suites.map((suite) => suite ? suite.getKYCFactory() : void 0)
1389
+ );
1390
+ const safeFactories = factories.reduce(
1391
+ (acc, v) => {
1392
+ if (v) {
1393
+ acc.push(v);
1394
+ }
1395
+ return acc;
1396
+ },
1397
+ []
1398
+ );
1399
+ const allResp = await this.client.multicall({
1400
+ contracts: [
1401
+ ...safeFactories.map((factory) => {
1402
+ return {
1403
+ abi: factory.abi,
1404
+ address: factory.address,
1405
+ functionName: "getCreditAccounts",
1406
+ args: [investor]
1407
+ };
1408
+ })
1409
+ ],
1410
+ allowFailure: true,
1411
+ batchSize: 0
1412
+ });
1413
+ const caLists = safeFactories.reduce((acc, _, index) => {
1414
+ const response = allResp[index];
1415
+ acc.push(...response.result || []);
1416
+ return acc;
1417
+ }, []);
1418
+ return caLists;
1419
+ }
1058
1420
  }
1059
1421
  export {
1060
1422
  AbstractCreditAccountService,
@@ -42,7 +42,7 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
42
42
  [addBotCall],
43
43
  targetContract
44
44
  ) : [addBotCall];
45
- const tx = targetContract.type === "creditAccount" ? cm.creditFacade.multicall(targetContract.creditAccount, calls) : void 0;
45
+ const tx = targetContract.type === "creditAccount" ? await this.multicallTx(cm, targetContract.creditAccount, calls) : void 0;
46
46
  return { tx, calls, creditFacade: cm.creditFacade };
47
47
  }
48
48
  /**
@@ -77,7 +77,7 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
77
77
  operationCalls,
78
78
  creditAccount
79
79
  );
80
- const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
80
+ const tx = await this.multicallTx(cm, creditAccount.creditAccount, calls);
81
81
  return { tx, calls, creditFacade: cm.creditFacade };
82
82
  }
83
83
  /**
@@ -90,26 +90,35 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
90
90
  creditAccount: ca,
91
91
  permits,
92
92
  to,
93
- tokensToClaim
93
+ tokensToClaim,
94
+ calls: wrapCalls = []
94
95
  }) {
95
96
  const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
96
97
  const addCollateral = collateralAssets.filter((a) => a.balance > 0);
97
98
  const router = this.sdk.routerFor(ca);
99
+ const unwrapCalls = await this.getRedeemDiffCalls(1n, ca.creditManager) ?? [];
98
100
  const claimPath = await router.findClaimAllRewards({
99
101
  tokensToClaim,
100
102
  creditAccount: ca
101
103
  });
102
104
  const operationCalls = [
103
105
  ...this.prepareAddCollateral(ca.creditFacade, addCollateral, permits),
106
+ ...wrapCalls,
104
107
  ...this.prepareDisableQuotas(ca),
105
108
  ...this.prepareDecreaseDebt(ca),
109
+ ...unwrapCalls,
106
110
  ...claimPath.calls,
107
111
  ...assetsToWithdraw.map(
108
112
  (t) => this.prepareWithdrawToken(ca.creditFacade, t.token, MAX_UINT256, to)
109
113
  )
110
114
  ];
111
115
  const calls = operation === "close" ? operationCalls : await this.prependPriceUpdates(ca.creditManager, operationCalls, ca);
112
- const tx = operation === "close" ? cm.creditFacade.closeCreditAccount(ca.creditAccount, calls) : cm.creditFacade.multicall(ca.creditAccount, calls);
116
+ const tx = await this.closeCreditAccountTx(
117
+ cm,
118
+ ca.creditAccount,
119
+ calls,
120
+ operation
121
+ );
113
122
  return { tx, calls, creditFacade: cm.creditFacade };
114
123
  }
115
124
  /**
@@ -129,10 +138,12 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
129
138
  tokensToClaim,
130
139
  creditAccount: ca
131
140
  });
141
+ const wrapCalls = await this.getDepositDiffCalls(1n, ca.creditManager) ?? [];
132
142
  const addCollateral = collateralAssets.filter((a) => a.balance > 0);
133
143
  const operationCalls = [
134
144
  ...this.prepareAddCollateral(ca.creditFacade, addCollateral, permits),
135
145
  ...claimPath.calls,
146
+ ...wrapCalls,
136
147
  ...assetsToWithdraw.map(
137
148
  (t) => this.prepareWithdrawToken(ca.creditFacade, t.token, MAX_UINT256, to)
138
149
  )
@@ -180,7 +191,7 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
180
191
  operationCalls,
181
192
  ca
182
193
  );
183
- const tx = cm.creditFacade.multicall(ca.creditAccount, calls);
194
+ const tx = await this.multicallTx(cm, ca.creditAccount, calls);
184
195
  return { tx, calls, creditFacade: cm.creditFacade };
185
196
  }
186
197
  async previewWithdrawLlamathenaProportionally(_) {
@@ -15,7 +15,7 @@ class ChainContractsRegister {
15
15
  logger;
16
16
  constructor(client, logger) {
17
17
  this.client = client;
18
- this.tokensMeta = new TokensMeta(client);
18
+ this.tokensMeta = new TokensMeta(client, logger);
19
19
  this.logger = logger;
20
20
  }
21
21
  /**