@gearbox-protocol/sdk 13.3.3 → 13.4.0-beta.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.
@@ -22,8 +22,13 @@ import {
22
22
  VERSION_RANGE_310
23
23
  } from "../constants/index.js";
24
24
  import { assetsMap } from "../router/index.js";
25
- import { AddressMap } from "../utils/index.js";
25
+ import { AddressMap, AddressSet } from "../utils/index.js";
26
26
  import { simulateWithPriceUpdates } from "../utils/viem/index.js";
27
+ import {
28
+ extractPriceUpdates,
29
+ extractQuotaTokens,
30
+ mergePriceUpdates
31
+ } from "./multicall-utils.js";
27
32
  const COMPRESSORS = {
28
33
  [chains.Mainnet.id]: "0x36F3d0Bb73CBC2E94fE24dF0f26a689409cF9023",
29
34
  [chains.Monad.id]: "0x36F3d0Bb73CBC2E94fE24dF0f26a689409cF9023"
@@ -323,12 +328,12 @@ class AbstractCreditAccountService extends SDKConstruct {
323
328
  keepAssets,
324
329
  debtOnly
325
330
  });
326
- const priceUpdates = await this.getPriceUpdatesForFacade({
327
- creditManager: account.creditManager,
328
- creditAccount: account,
329
- ignoreReservePrices
330
- });
331
- const calls = [...priceUpdates, ...routerCloseResult.calls];
331
+ const calls = await this.prependPriceUpdates(
332
+ account.creditManager,
333
+ routerCloseResult.calls,
334
+ account,
335
+ { ignoreReservePrices }
336
+ );
332
337
  let lossPolicyData;
333
338
  if (applyLossPolicy) {
334
339
  const market = this.sdk.marketRegister.findByCreditManager(
@@ -383,12 +388,7 @@ class AbstractCreditAccountService extends SDKConstruct {
383
388
  creditManager: cm.creditManager,
384
389
  slippage
385
390
  });
386
- const priceUpdates = await this.getPriceUpdatesForFacade({
387
- creditManager: ca.creditManager,
388
- creditAccount: ca
389
- });
390
- const calls = [
391
- ...operation === "close" ? [] : priceUpdates,
391
+ const operationCalls = [
392
392
  ...routerCloseResult.calls,
393
393
  ...this.prepareDisableQuotas(ca),
394
394
  ...this.prepareDecreaseDebt(ca),
@@ -396,6 +396,7 @@ class AbstractCreditAccountService extends SDKConstruct {
396
396
  (t) => this.prepareWithdrawToken(ca.creditFacade, t, MAX_UINT256, to)
397
397
  )
398
398
  ];
399
+ const calls = operation === "close" ? operationCalls : await this.prependPriceUpdates(ca.creditManager, operationCalls, ca);
399
400
  const tx = operation === "close" ? cm.creditFacade.closeCreditAccount(ca.creditAccount, calls) : cm.creditFacade.multicall(ca.creditAccount, calls);
400
401
  return { tx, calls, routerCloseResult, creditFacade: cm.creditFacade };
401
402
  }
@@ -415,17 +416,15 @@ class AbstractCreditAccountService extends SDKConstruct {
415
416
  const cm = this.sdk.marketRegister.findCreditManager(
416
417
  creditAccount.creditManager
417
418
  );
418
- const priceUpdates = await this.getPriceUpdatesForFacade({
419
- creditManager: creditAccount.creditManager,
419
+ const operationCalls = this.prepareUpdateQuotas(
420
+ creditAccount.creditFacade,
421
+ { minQuota, averageQuota }
422
+ );
423
+ const calls = await this.prependPriceUpdates(
424
+ creditAccount.creditManager,
425
+ operationCalls,
420
426
  creditAccount
421
- });
422
- const calls = [
423
- ...priceUpdates,
424
- ...this.prepareUpdateQuotas(creditAccount.creditFacade, {
425
- minQuota,
426
- averageQuota
427
- })
428
- ];
427
+ );
429
428
  const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
430
429
  return { tx, calls, creditFacade: cm.creditFacade };
431
430
  }
@@ -451,13 +450,7 @@ class AbstractCreditAccountService extends SDKConstruct {
451
450
  const cm = this.sdk.marketRegister.findCreditManager(
452
451
  creditAccount.creditManager
453
452
  );
454
- const priceUpdatesCalls = await this.getPriceUpdatesForFacade({
455
- creditManager: creditAccount.creditManager,
456
- creditAccount,
457
- desiredQuotas: averageQuota
458
- });
459
- const calls = [
460
- ...priceUpdatesCalls,
453
+ const operationCalls = [
461
454
  ...this.prepareAddCollateral(
462
455
  creditAccount.creditFacade,
463
456
  [asset],
@@ -468,6 +461,11 @@ class AbstractCreditAccountService extends SDKConstruct {
468
461
  averageQuota
469
462
  })
470
463
  ];
464
+ const calls = await this.prependPriceUpdates(
465
+ creditAccount.creditManager,
466
+ operationCalls,
467
+ creditAccount
468
+ );
471
469
  const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
472
470
  tx.value = ethAmount.toString(10);
473
471
  return { tx, calls, creditFacade: cm.creditFacade };
@@ -495,10 +493,6 @@ class AbstractCreditAccountService extends SDKConstruct {
495
493
  const cm = this.sdk.marketRegister.findCreditManager(
496
494
  creditAccount.creditManager
497
495
  );
498
- const priceUpdatesCalls = await this.getPriceUpdatesForFacade({
499
- creditManager: creditAccount.creditManager,
500
- creditAccount
501
- });
502
496
  const addCollateralCalls = addCollateral && isDecrease ? this.prepareAddCollateral(
503
497
  creditAccount.creditFacade,
504
498
  [
@@ -509,11 +503,15 @@ class AbstractCreditAccountService extends SDKConstruct {
509
503
  ],
510
504
  {}
511
505
  ) : [];
512
- const calls = [
513
- ...priceUpdatesCalls,
506
+ const operationCalls = [
514
507
  ...addCollateralCalls,
515
508
  this.#prepareChangeDebt(creditAccount.creditFacade, change, isDecrease)
516
509
  ];
510
+ const calls = await this.prependPriceUpdates(
511
+ creditAccount.creditManager,
512
+ operationCalls,
513
+ creditAccount
514
+ );
517
515
  const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
518
516
  return { tx, calls, creditFacade: cm.creditFacade };
519
517
  }
@@ -536,19 +534,18 @@ class AbstractCreditAccountService extends SDKConstruct {
536
534
  const cm = this.sdk.marketRegister.findCreditManager(
537
535
  creditAccount.creditManager
538
536
  );
539
- const priceUpdatesCalls = await this.getPriceUpdatesForFacade({
540
- creditManager: creditAccount.creditManager,
541
- creditAccount,
542
- desiredQuotas: averageQuota
543
- });
544
- const calls = [
545
- ...priceUpdatesCalls,
537
+ const operationCalls = [
546
538
  ...swapCalls,
547
539
  ...this.prepareUpdateQuotas(creditAccount.creditFacade, {
548
540
  minQuota,
549
541
  averageQuota
550
542
  })
551
543
  ];
544
+ const calls = await this.prependPriceUpdates(
545
+ creditAccount.creditManager,
546
+ operationCalls,
547
+ creditAccount
548
+ );
552
549
  const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
553
550
  return { tx, calls, creditFacade: cm.creditFacade };
554
551
  }
@@ -650,13 +647,7 @@ class AbstractCreditAccountService extends SDKConstruct {
650
647
  args: []
651
648
  })
652
649
  };
653
- const priceUpdatesCalls = await this.getPriceUpdatesForFacade({
654
- creditManager: creditAccount.creditManager,
655
- creditAccount,
656
- desiredQuotas: averageQuota
657
- });
658
- const calls = [
659
- ...priceUpdatesCalls,
650
+ const operationCalls = [
660
651
  storeExpectedBalances,
661
652
  ...preview.requestCalls,
662
653
  compareBalances,
@@ -665,6 +656,11 @@ class AbstractCreditAccountService extends SDKConstruct {
665
656
  averageQuota
666
657
  })
667
658
  ];
659
+ const calls = await this.prependPriceUpdates(
660
+ creditAccount.creditManager,
661
+ operationCalls,
662
+ creditAccount
663
+ );
668
664
  const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
669
665
  return { tx, calls, creditFacade: cm.creditFacade };
670
666
  }
@@ -714,22 +710,21 @@ class AbstractCreditAccountService extends SDKConstruct {
714
710
  args: []
715
711
  })
716
712
  };
717
- const priceUpdatesCalls = zeroDebt ? [] : await this.getPriceUpdatesForFacade({
718
- creditManager: creditAccount.creditManager,
719
- creditAccount,
720
- desiredQuotas: averageQuota
721
- });
722
713
  const quotaCalls = zeroDebt ? [] : this.prepareUpdateQuotas(creditAccount.creditFacade, {
723
714
  minQuota,
724
715
  averageQuota
725
716
  });
726
- const calls = [
727
- ...priceUpdatesCalls,
717
+ const operationCalls = [
728
718
  storeExpectedBalances,
729
719
  ...claimableNow.claimCalls,
730
720
  compareBalances,
731
721
  ...quotaCalls
732
722
  ];
723
+ const calls = zeroDebt ? operationCalls : await this.prependPriceUpdates(
724
+ creditAccount.creditManager,
725
+ operationCalls,
726
+ creditAccount
727
+ );
733
728
  const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
734
729
  return { tx, calls, creditFacade: cm.creditFacade };
735
730
  }
@@ -772,12 +767,7 @@ class AbstractCreditAccountService extends SDKConstruct {
772
767
  }) {
773
768
  const cmSuite = this.sdk.marketRegister.findCreditManager(creditManager);
774
769
  const cm = cmSuite.creditManager;
775
- const priceUpdatesCalls = await this.getPriceUpdatesForFacade({
776
- creditManager: cm.address,
777
- desiredQuotas: averageQuota
778
- });
779
- const calls = [
780
- ...priceUpdatesCalls,
770
+ const operationCalls = [
781
771
  this.#prepareIncreaseDebt(cm.creditFacade, debt),
782
772
  ...this.prepareAddCollateral(cm.creditFacade, collateral, permits),
783
773
  ...openPathCalls,
@@ -787,6 +777,7 @@ class AbstractCreditAccountService extends SDKConstruct {
787
777
  averageQuota
788
778
  })
789
779
  ];
780
+ const calls = await this.prependPriceUpdates(cm.address, operationCalls);
790
781
  const tx = cmSuite.creditFacade.openCreditAccount(to, calls, referralCode);
791
782
  tx.value = ethAmount.toString(10);
792
783
  return { calls, tx, creditFacade: cmSuite.creditFacade };
@@ -871,37 +862,6 @@ class AbstractCreditAccountService extends SDKConstruct {
871
862
  );
872
863
  return resp;
873
864
  }
874
- /**
875
- * Returns raw txs that are needed to update all price feeds so that all credit accounts (possibly from different markets) compute
876
- *
877
- * This can be used by batch liquidator
878
- * @param accounts
879
- * @returns
880
- */
881
- async getUpdateForAccounts(accounts) {
882
- const tokensByPool = /* @__PURE__ */ new Map();
883
- const oracleByPool = /* @__PURE__ */ new Map();
884
- for (const acc of accounts) {
885
- const market = this.sdk.marketRegister.findByCreditManager(
886
- acc.creditManager
887
- );
888
- const pool = market.pool.pool.address;
889
- oracleByPool.set(pool, market.priceOracle);
890
- for (const t of acc.tokens) {
891
- if (t.balance > 10n) {
892
- const tokens = tokensByPool.get(pool) ?? /* @__PURE__ */ new Set();
893
- tokens.add(t.token);
894
- tokensByPool.set(pool, tokens);
895
- }
896
- }
897
- }
898
- const priceFeeds = [];
899
- for (const [pool, oracle] of oracleByPool.entries()) {
900
- const tokens = Array.from(tokensByPool.get(pool) ?? []);
901
- priceFeeds.push(...oracle.priceFeedsForTokens(tokens));
902
- }
903
- return this.sdk.priceFeeds.generatePriceFeedsUpdateTxs(priceFeeds);
904
- }
905
865
  async getUpdateForAccount(options) {
906
866
  const { creditManager, creditAccount, desiredQuotas, ignoreReservePrices } = options;
907
867
  const quotaRecord = desiredQuotas ? assetsMap(desiredQuotas) : desiredQuotas;
@@ -965,19 +925,113 @@ class AbstractCreditAccountService extends SDKConstruct {
965
925
  return market.priceOracle.onDemandPriceUpdates(
966
926
  cm.creditFacade.address,
967
927
  update
928
+ ).raw;
929
+ }
930
+ /**
931
+ * Analyzes a multicall array and prepends necessary on-demand price feed updates.
932
+ *
933
+ * Deduplicates existing `onDemandPriceUpdates` calls
934
+ *
935
+ * @param creditManager - Address of the credit manager
936
+ * @param calls - The multicall array to prepend price updates to
937
+ * @param ca - Credit account slice, undefined when opening a new account
938
+ * @param options - Optional settings for price update generation
939
+ * @returns A new array with a single consolidated price update call prepended,
940
+ * followed by the non-price-update calls in their original order
941
+ */
942
+ async prependPriceUpdates(creditManager, calls, ca, options) {
943
+ const market = this.sdk.marketRegister.findByCreditManager(creditManager);
944
+ const cm = this.sdk.marketRegister.findCreditManager(creditManager).creditManager;
945
+ const { priceUpdates: existingUpdates, remainingCalls } = extractPriceUpdates(calls);
946
+ const tokens = new AddressSet([
947
+ cm.underlying,
948
+ // underlying - always included
949
+ ...extractQuotaTokens(calls)
950
+ // tokens from `updateQuota` calls
951
+ ]);
952
+ if (ca) {
953
+ for (const t of ca.tokens) {
954
+ const isEnabled = (t.mask & ca.enabledTokensMask) !== 0n;
955
+ if (t.balance > 10n && isEnabled) {
956
+ tokens.add(t.token);
957
+ }
958
+ }
959
+ }
960
+ const ignoreReservePrices = options?.ignoreReservePrices;
961
+ const priceFeeds = market.priceOracle.priceFeedsForTokens(Array.from(tokens), {
962
+ main: true,
963
+ reserve: !ignoreReservePrices
964
+ });
965
+ const tStr = tokens.map((t) => this.labelAddress(t)).join(", ");
966
+ const remark = ignoreReservePrices ? " main" : "";
967
+ this.logger?.debug(
968
+ { account: ca?.creditAccount, manager: cm.name },
969
+ `prependPriceUpdates for ${tStr} from ${priceFeeds.length}${remark} price feeds`
968
970
  );
971
+ const generatedUpdates = await this.sdk.priceFeeds.generatePriceFeedsUpdates(priceFeeds);
972
+ const merged = mergePriceUpdates(existingUpdates, generatedUpdates);
973
+ if (merged.length === 0) {
974
+ return remainingCalls;
975
+ }
976
+ return [
977
+ {
978
+ target: cm.creditFacade,
979
+ callData: encodeFunctionData({
980
+ abi: iCreditFacadeMulticallV310Abi,
981
+ functionName: "onDemandPriceUpdates",
982
+ args: [merged]
983
+ })
984
+ },
985
+ ...remainingCalls
986
+ ];
969
987
  }
970
988
  /**
971
- * Returns price updates in format that is accepted by various credit facade methods (multicall, close/liquidate, etc...).
972
- * - If there are desiredQuotas and creditAccount update quotaBalance > 0 || (balance > 10n && isEnabled). Is used when account has both: balances and quota buys.
973
- * - If there is creditAccount update balance > 10n && isEnabled. Is used in credit account actions when quota is not being bought.
974
- * - If there is desiredQuotas update quotaBalance > 0. Is used on credit account opening, when quota is bought for the first time.
975
- * @param acc
976
- * @returns
989
+ * Executes a multicall on a credit account, automatically prepending
990
+ * necessary on-demand price feed updates.
991
+ *
992
+ * @param creditAccount - Credit account to execute multicall on
993
+ * @param calls - Array of multicall operations (price updates will be inferred)
994
+ * @param options - Optional settings for price update generation
995
+ * @returns Raw transaction ready to be signed and sent
977
996
  */
978
- async getPriceUpdatesForFacade(options) {
979
- const updates = await this.getOnDemandPriceUpdates(options);
980
- return updates.multicall;
997
+ async multicall(creditAccount, calls, options) {
998
+ const cm = this.sdk.marketRegister.findCreditManager(
999
+ creditAccount.creditManager
1000
+ );
1001
+ const callsWithPrices = await this.prependPriceUpdates(
1002
+ creditAccount.creditManager,
1003
+ calls,
1004
+ creditAccount,
1005
+ options
1006
+ );
1007
+ return cm.creditFacade.multicall(
1008
+ creditAccount.creditAccount,
1009
+ callsWithPrices
1010
+ );
1011
+ }
1012
+ /**
1013
+ * Executes a bot multicall on a credit account, automatically prepending
1014
+ * necessary on-demand price feed updates.
1015
+ *
1016
+ * @param creditAccount - Credit account to execute bot multicall on
1017
+ * @param calls - Array of multicall operations (price updates will be inferred)
1018
+ * @param options - Optional settings for price update generation
1019
+ * @returns Raw transaction ready to be signed and sent
1020
+ */
1021
+ async botMulticall(creditAccount, calls, options) {
1022
+ const cm = this.sdk.marketRegister.findCreditManager(
1023
+ creditAccount.creditManager
1024
+ );
1025
+ const callsWithPrices = await this.prependPriceUpdates(
1026
+ creditAccount.creditManager,
1027
+ calls,
1028
+ creditAccount,
1029
+ options
1030
+ );
1031
+ return cm.creditFacade.botMulticall(
1032
+ creditAccount.creditAccount,
1033
+ callsWithPrices
1034
+ );
981
1035
  }
982
1036
  prepareDisableQuotas(ca) {
983
1037
  const calls = [];
@@ -1099,79 +1153,6 @@ class AbstractCreditAccountService extends SDKConstruct {
1099
1153
  )[0];
1100
1154
  }
1101
1155
  }
1102
- const iMellowClaimerAdapterAbi = [
1103
- {
1104
- type: "function",
1105
- name: "getMultiVaultSubvaultIndices",
1106
- inputs: [{ name: "multiVault", type: "address", internalType: "address" }],
1107
- outputs: [
1108
- {
1109
- name: "subvaultIndices",
1110
- type: "uint256[]",
1111
- internalType: "uint256[]"
1112
- },
1113
- {
1114
- name: "withdrawalIndices",
1115
- type: "uint256[][]",
1116
- internalType: "uint256[][]"
1117
- }
1118
- ],
1119
- stateMutability: "view"
1120
- },
1121
- {
1122
- type: "function",
1123
- name: "getUserSubvaultIndices",
1124
- inputs: [
1125
- { name: "multiVault", type: "address", internalType: "address" },
1126
- { name: "user", type: "address", internalType: "address" }
1127
- ],
1128
- outputs: [
1129
- {
1130
- name: "subvaultIndices",
1131
- type: "uint256[]",
1132
- internalType: "uint256[]"
1133
- },
1134
- {
1135
- name: "withdrawalIndices",
1136
- type: "uint256[][]",
1137
- internalType: "uint256[][]"
1138
- }
1139
- ],
1140
- stateMutability: "view"
1141
- },
1142
- {
1143
- type: "function",
1144
- name: "multiAccept",
1145
- inputs: [
1146
- { name: "multiVault", type: "address", internalType: "address" },
1147
- {
1148
- name: "subvaultIndices",
1149
- type: "uint256[]",
1150
- internalType: "uint256[]"
1151
- },
1152
- { name: "indices", type: "uint256[][]", internalType: "uint256[][]" }
1153
- ],
1154
- outputs: [{ name: "", type: "bool", internalType: "bool" }],
1155
- stateMutability: "nonpayable"
1156
- },
1157
- {
1158
- type: "function",
1159
- name: "multiAcceptAndClaim",
1160
- inputs: [
1161
- { name: "multiVault", type: "address", internalType: "address" },
1162
- {
1163
- name: "subvaultIndices",
1164
- type: "uint256[]",
1165
- internalType: "uint256[]"
1166
- },
1167
- { name: "indices", type: "uint256[][]", internalType: "uint256[][]" },
1168
- { name: "", type: "address", internalType: "address" },
1169
- { name: "maxAssets", type: "uint256", internalType: "uint256" }
1170
- ],
1171
- outputs: [{ name: "", type: "bool", internalType: "bool" }],
1172
- stateMutability: "nonpayable"
1173
- }
1174
- ];
1175
1156
  export {
1176
1157
  AbstractCreditAccountService,
1177
1158
  getWithdrawalCompressorAddress
@@ -14,10 +14,6 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
14
14
  const cm = this.sdk.marketRegister.findCreditManager(
15
15
  targetContract.creditManager
16
16
  );
17
- const priceUpdatesCalls = targetContract.type === "creditAccount" ? await this.getPriceUpdatesForFacade({
18
- creditManager: targetContract.creditManager,
19
- creditAccount: targetContract
20
- }) : [];
21
17
  const permissions = defaultPermissions !== null ? defaultPermissions : await getContract({
22
18
  address: botAddress,
23
19
  client: this.sdk.client,
@@ -41,7 +37,11 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
41
37
  args: [botAddress, permissions]
42
38
  })
43
39
  };
44
- const calls = [...priceUpdatesCalls, addBotCall];
40
+ const calls = targetContract.type === "creditAccount" ? await this.prependPriceUpdates(
41
+ targetContract.creditManager,
42
+ [addBotCall],
43
+ targetContract
44
+ ) : [addBotCall];
45
45
  const tx = targetContract.type === "creditAccount" ? cm.creditFacade.multicall(targetContract.creditAccount, calls) : void 0;
46
46
  return { tx, calls, creditFacade: cm.creditFacade };
47
47
  }
@@ -58,12 +58,7 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
58
58
  const cm = this.sdk.marketRegister.findCreditManager(
59
59
  creditAccount.creditManager
60
60
  );
61
- const priceUpdatesCalls = await this.getPriceUpdatesForFacade({
62
- creditManager: creditAccount.creditManager,
63
- creditAccount
64
- });
65
- const calls = [
66
- ...priceUpdatesCalls,
61
+ const operationCalls = [
67
62
  ...assetsToWithdraw.map(
68
63
  (a) => this.prepareWithdrawToken(
69
64
  creditAccount.creditFacade,
@@ -77,6 +72,11 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
77
72
  averageQuota
78
73
  })
79
74
  ];
75
+ const calls = await this.prependPriceUpdates(
76
+ creditAccount.creditManager,
77
+ operationCalls,
78
+ creditAccount
79
+ );
80
80
  const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
81
81
  return { tx, calls, creditFacade: cm.creditFacade };
82
82
  }
@@ -99,12 +99,7 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
99
99
  tokensToClaim,
100
100
  creditAccount: ca
101
101
  });
102
- const priceUpdates = await this.getPriceUpdatesForFacade({
103
- creditManager: ca.creditManager,
104
- creditAccount: ca
105
- });
106
- const calls = [
107
- ...operation === "close" ? [] : priceUpdates,
102
+ const operationCalls = [
108
103
  ...this.prepareAddCollateral(ca.creditFacade, addCollateral, permits),
109
104
  ...this.prepareDisableQuotas(ca),
110
105
  ...this.prepareDecreaseDebt(ca),
@@ -113,6 +108,7 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
113
108
  (t) => this.prepareWithdrawToken(ca.creditFacade, t.token, MAX_UINT256, to)
114
109
  )
115
110
  ];
111
+ const calls = operation === "close" ? operationCalls : await this.prependPriceUpdates(ca.creditManager, operationCalls, ca);
116
112
  const tx = operation === "close" ? cm.creditFacade.closeCreditAccount(ca.creditAccount, calls) : cm.creditFacade.multicall(ca.creditAccount, calls);
117
113
  return { tx, calls, creditFacade: cm.creditFacade };
118
114
  }
@@ -133,19 +129,19 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
133
129
  tokensToClaim,
134
130
  creditAccount: ca
135
131
  });
136
- const priceUpdates = await this.getPriceUpdatesForFacade({
137
- creditManager: ca.creditManager,
138
- creditAccount: ca
139
- });
140
132
  const addCollateral = collateralAssets.filter((a) => a.balance > 0);
141
- const calls = [
142
- ...priceUpdates,
133
+ const operationCalls = [
143
134
  ...this.prepareAddCollateral(ca.creditFacade, addCollateral, permits),
144
135
  ...claimPath.calls,
145
136
  ...assetsToWithdraw.map(
146
137
  (t) => this.prepareWithdrawToken(ca.creditFacade, t.token, MAX_UINT256, to)
147
138
  )
148
139
  ];
140
+ const calls = await this.prependPriceUpdates(
141
+ ca.creditManager,
142
+ operationCalls,
143
+ ca
144
+ );
149
145
  const tx = cm.creditFacade.liquidateCreditAccount(
150
146
  ca.creditAccount,
151
147
  to,
@@ -175,16 +171,15 @@ class CreditAccountServiceV310 extends AbstractCreditAccountService {
175
171
  });
176
172
  }
177
173
  if (claimPath.calls.length === 0) throw new Error("No path to execute");
178
- const priceUpdatesCalls = await this.getPriceUpdatesForFacade({
179
- creditManager: ca.creditManager,
180
- creditAccount: ca,
181
- desiredQuotas: averageQuota
182
- });
183
- const calls = [
184
- ...priceUpdatesCalls,
174
+ const operationCalls = [
185
175
  ...claimPath.calls,
186
176
  ...this.prepareUpdateQuotas(ca.creditFacade, { minQuota, averageQuota })
187
177
  ];
178
+ const calls = await this.prependPriceUpdates(
179
+ ca.creditManager,
180
+ operationCalls,
181
+ ca
182
+ );
188
183
  const tx = cm.creditFacade.multicall(ca.creditAccount, calls);
189
184
  return { tx, calls, creditFacade: cm.creditFacade };
190
185
  }
@@ -0,0 +1,69 @@
1
+ import {
2
+ decodeFunctionData,
3
+ getAbiItem,
4
+ toFunctionSelector
5
+ } from "viem";
6
+ import { iCreditFacadeMulticallV310Abi } from "../../abi/310/generated.js";
7
+ import { AddressMap } from "../utils/AddressMap.js";
8
+ import { AddressSet } from "../utils/AddressSet.js";
9
+ const ON_DEMAND_SELECTOR = toFunctionSelector(
10
+ getAbiItem({
11
+ abi: iCreditFacadeMulticallV310Abi,
12
+ name: "onDemandPriceUpdates"
13
+ })
14
+ );
15
+ const UPDATE_QUOTA_SELECTOR = toFunctionSelector(
16
+ getAbiItem({
17
+ abi: iCreditFacadeMulticallV310Abi,
18
+ name: "updateQuota"
19
+ })
20
+ );
21
+ function extractPriceUpdates(calls) {
22
+ const priceUpdates = [];
23
+ const remainingCalls = [];
24
+ for (const call of calls) {
25
+ if (isOnDemandPriceUpdateCall(call)) {
26
+ const decoded = decodeFunctionData({
27
+ abi: iCreditFacadeMulticallV310Abi,
28
+ data: call.callData
29
+ });
30
+ const updates = decoded.args[0];
31
+ priceUpdates.push(...updates);
32
+ } else {
33
+ remainingCalls.push(call);
34
+ }
35
+ }
36
+ return { priceUpdates, remainingCalls };
37
+ }
38
+ function extractQuotaTokens(calls) {
39
+ const tokens = new AddressSet();
40
+ for (const { callData } of calls) {
41
+ if (callData.slice(0, 10) !== UPDATE_QUOTA_SELECTOR) {
42
+ continue;
43
+ }
44
+ const decoded = decodeFunctionData({
45
+ abi: iCreditFacadeMulticallV310Abi,
46
+ data: callData
47
+ });
48
+ const [token, quotaChange] = decoded.args;
49
+ if (quotaChange > 0n) {
50
+ tokens.add(token);
51
+ }
52
+ }
53
+ return tokens;
54
+ }
55
+ function mergePriceUpdates(existing, generated) {
56
+ const seen = new AddressMap();
57
+ for (const u of [...generated, ...existing]) {
58
+ seen.upsert(u.priceFeed, u);
59
+ }
60
+ return seen.values();
61
+ }
62
+ function isOnDemandPriceUpdateCall(call) {
63
+ return call.callData.slice(0, 10) === ON_DEMAND_SELECTOR;
64
+ }
65
+ export {
66
+ extractPriceUpdates,
67
+ extractQuotaTokens,
68
+ mergePriceUpdates
69
+ };
@@ -72,6 +72,12 @@ class CreditFacadeV310Contract extends CreditFacadeV310BaseContract {
72
72
  args: [ca, calls]
73
73
  });
74
74
  }
75
+ botMulticall(ca, calls) {
76
+ return this.createRawTx({
77
+ functionName: "botMulticall",
78
+ args: [ca, calls]
79
+ });
80
+ }
75
81
  openCreditAccount(to, calls, referralCode) {
76
82
  return this.createRawTx({
77
83
  functionName: "openCreditAccount",