@gearbox-protocol/sdk 3.0.0-vfour.90 → 3.0.0-vfour.92

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.
@@ -22320,986 +22320,1081 @@ var MarketRegister = class extends SDKConstruct {
22320
22320
  return this.#curators;
22321
22321
  }
22322
22322
  };
22323
-
22324
- // src/sdk/accounts/CreditAccountsService.ts
22325
- var CreditAccountsService = class extends SDKConstruct {
22326
- #compressor;
22327
- constructor(sdk) {
22328
- super(sdk);
22329
- this.#compressor = sdk.addressProvider.getLatestVersion(
22330
- AP_CREDIT_ACCOUNT_COMPRESSOR
22323
+ var PathOptionFactory = class _PathOptionFactory {
22324
+ // TODO: get rid of token data from SDK
22325
+ static generatePathOptions(balances, network, loopsInTx) {
22326
+ const curvePools = _PathOptionFactory.getCurvePools(balances);
22327
+ const balancerPools = _PathOptionFactory.getBalancerPools(balances);
22328
+ const curveInitPO = curvePools.map((symbol) => {
22329
+ return {
22330
+ target: sdkGov.tokenDataByNetwork[network][symbol],
22331
+ option: 0,
22332
+ totalOptions: sdkGov.contractParams[sdkGov.curveTokens[symbol].pool].tokens.length
22333
+ };
22334
+ });
22335
+ const balancerInitPO = balancerPools.map((symbol) => {
22336
+ return {
22337
+ target: sdkGov.tokenDataByNetwork[network][symbol],
22338
+ option: 0,
22339
+ totalOptions: sdkGov.balancerLpTokens[symbol].underlying.length
22340
+ };
22341
+ });
22342
+ const initPO = [...curveInitPO, ...balancerInitPO];
22343
+ const totalLoops = initPO.reduce(
22344
+ (acc, item) => acc * item.totalOptions,
22345
+ 1
22331
22346
  );
22332
- }
22333
- /**
22334
- * Returns single credit account data, or undefined if it's not found
22335
- * Performs all necessary price feed updates under the hood
22336
- * @param account
22337
- * @param options
22338
- * @returns
22339
- */
22340
- async getCreditAccountData(account, options) {
22341
- const blockNumber = options?.blockNumber;
22342
- let raw;
22343
- try {
22344
- raw = await this.provider.publicClient.readContract({
22345
- abi: iCreditAccountCompressorAbi,
22346
- address: this.#compressor,
22347
- functionName: "getCreditAccountData",
22348
- args: [account],
22349
- blockNumber
22350
- });
22351
- } catch (e) {
22352
- return void 0;
22353
- }
22354
- if (raw.success) {
22355
- return raw;
22347
+ const result = [];
22348
+ let currentPo = [...initPO];
22349
+ for (let i = 0; i < totalLoops; i++) {
22350
+ if (i % loopsInTx === 0) {
22351
+ result.push(currentPo);
22352
+ }
22353
+ if (i < totalLoops - 1) {
22354
+ currentPo = _PathOptionFactory.next(currentPo);
22355
+ }
22356
22356
  }
22357
- const { txs: priceUpdateTxs, timestamp: _ } = await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs();
22358
- const resp = await simulateMulticall(this.provider.publicClient, {
22359
- contracts: [
22360
- ...priceUpdateTxs.map(rawTxToMulticallPriceUpdate),
22361
- {
22362
- abi: iCreditAccountCompressorAbi,
22363
- address: this.#compressor,
22364
- functionName: "getCreditAccountData",
22365
- args: [account]
22366
- }
22367
- ],
22368
- allowFailure: false,
22369
- gas: 550000000n,
22370
- batchSize: 0,
22371
- // we cannot have price updates and compressor request in different batches
22372
- blockNumber
22373
- });
22374
- const cad = resp.pop();
22375
- return cad;
22357
+ return result;
22376
22358
  }
22377
- /**
22378
- * Methods to get all credit accounts with some optional filtering
22379
- * Performs all necessary price feed updates under the hood
22380
- *
22381
- * TODO: do we want to expose pagination?
22382
- * TODO: do we want to expose "reverting"?
22383
- * TODO: do we want to expose MarketFilter in any way? If so, we need to check that the MarketFilter is compatibled with attached markets?
22384
- * @param args
22385
- * @param options
22386
- * @returns returned credit accounts are sorted by health factor in ascending order
22387
- */
22388
- async getCreditAccounts(args, options) {
22389
- const {
22390
- creditManager,
22391
- includeZeroDebt = false,
22392
- maxHealthFactor = 65535,
22393
- // TODO: this will change to bigint
22394
- minHealthFactor = 0,
22395
- owner = ADDRESS_0X0
22396
- } = args ?? {};
22397
- const arg0 = creditManager ?? {
22398
- curators: [],
22399
- pools: this.pools,
22400
- underlying: ADDRESS_0X0
22401
- };
22402
- const caFilter = {
22403
- owner,
22404
- includeZeroDebt,
22405
- minHealthFactor,
22406
- maxHealthFactor
22407
- };
22408
- const { txs: priceUpdateTxs, timestamp: _ } = await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs();
22409
- const allCAs = [];
22410
- for (const reverting of [false, true]) {
22411
- let offset = 0n;
22412
- do {
22413
- const [accounts, newOffset] = await this.#getCreditAccounts(
22414
- [arg0, { ...caFilter, reverting }, offset],
22415
- priceUpdateTxs,
22416
- options
22417
- );
22418
- allCAs.push(...accounts);
22419
- offset = newOffset;
22420
- } while (offset !== 0n);
22359
+ static getCurvePools(balances) {
22360
+ const nonZeroBalances = balances.filter((b) => b.balance > 1n);
22361
+ const curvePools = nonZeroBalances.map((b) => sdkGov.getTokenSymbol(b.token)).filter((symbol) => sdkGov.isCurveLPToken(symbol));
22362
+ const yearnCurveTokens = Object.entries(sdkGov.yearnTokens).filter(([, data]) => sdkGov.isCurveLPToken(data.underlying)).map(([token]) => token);
22363
+ const curvePoolsFromYearn = nonZeroBalances.map((b) => sdkGov.getTokenSymbol(b.token)).filter((symbol) => yearnCurveTokens.includes(symbol)).map(
22364
+ (symbol) => sdkGov.yearnTokens[symbol].underlying
22365
+ );
22366
+ const convexCurveTokens = Object.entries(sdkGov.convexTokens).filter(([, data]) => sdkGov.isCurveLPToken(data.underlying)).map(([token]) => token);
22367
+ const curvePoolsFromConvex = nonZeroBalances.map((b) => sdkGov.getTokenSymbol(b.token)).filter((symbol) => convexCurveTokens.includes(symbol)).map((symbol) => sdkGov.convexTokens[symbol].underlying);
22368
+ const curveSet = /* @__PURE__ */ new Set([
22369
+ ...curvePools,
22370
+ ...curvePoolsFromYearn,
22371
+ ...curvePoolsFromConvex
22372
+ ]);
22373
+ return Array.from(curveSet.values());
22374
+ }
22375
+ static getBalancerPools(balances) {
22376
+ const nonZeroBalances = Object.entries(balances).filter(
22377
+ ([, balance]) => balance.balance > 1
22378
+ );
22379
+ const balancerPools = nonZeroBalances.map(([token]) => sdkGov.getTokenSymbol(token)).filter((symbol) => sdkGov.isBalancerLPToken(symbol));
22380
+ const balancerAuraTokens = Object.entries(sdkGov.auraTokens).filter(([, data]) => sdkGov.isBalancerLPToken(data.underlying)).map(([token]) => token);
22381
+ const balancerTokensFromAura = nonZeroBalances.map(([token]) => sdkGov.getTokenSymbol(token)).filter((symbol) => balancerAuraTokens.includes(symbol)).map(
22382
+ (symbol) => sdkGov.auraTokens[symbol].underlying
22383
+ );
22384
+ const balancerSet = /* @__PURE__ */ new Set([...balancerPools, ...balancerTokensFromAura]);
22385
+ return Array.from(balancerSet.values());
22386
+ }
22387
+ static next(path) {
22388
+ let newPath = [...path];
22389
+ for (let i = path.length - 1; i >= 0; i--) {
22390
+ const po = { ...newPath[i] };
22391
+ po.option++;
22392
+ newPath[i] = po;
22393
+ if (po.option < po.totalOptions) return newPath;
22394
+ po.option = 0;
22421
22395
  }
22422
- return allCAs.sort((a, b) => Number(a.healthFactor - b.healthFactor));
22396
+ throw new Error("Path options overflow");
22423
22397
  }
22424
- /**
22425
- * Generates transaction to liquidate credit account
22426
- * @param account
22427
- * @param to Address to transfer underlying left after liquidation
22428
- * @param slippage
22429
- * @returns
22430
- */
22431
- async fullyLiquidate(account, to, slippage = 50n) {
22432
- const cm = this.sdk.marketRegister.findCreditManager(account.creditManager);
22433
- const routerCloseResult = await this.sdk.router.findBestClosePath({
22434
- creditAccount: account,
22435
- creditManager: cm.creditManager,
22436
- slippage
22398
+ };
22399
+
22400
+ // src/sdk/router/RouterV3Contract.ts
22401
+ var MAX_GAS_PER_ROUTE = 200000000n;
22402
+ var GAS_PER_BLOCK = 400000000n;
22403
+ var LOOPS_PER_TX = Number(GAS_PER_BLOCK / MAX_GAS_PER_ROUTE);
22404
+ var SWAP_OPERATIONS = {
22405
+ EXACT_INPUT: 0,
22406
+ EXACT_INPUT_ALL: 1,
22407
+ EXACT_OUTPUT: 2
22408
+ };
22409
+ var RouterV3Contract = class extends BaseContract {
22410
+ #connectors;
22411
+ #hooks = new Hooks();
22412
+ constructor(sdk, address) {
22413
+ super(sdk, {
22414
+ addr: address,
22415
+ name: "RouterV3",
22416
+ abi: routerV3Abi
22437
22417
  });
22438
- const priceUpdates = await this.getPriceUpdatesForFacade(account);
22439
- const tx = cm.creditFacade.liquidateCreditAccount(
22440
- account.creditAccount,
22441
- to,
22442
- [...priceUpdates, ...routerCloseResult.calls]
22443
- );
22444
- return { tx, routerCloseResult };
22418
+ this.#connectors = sdkGov.getConnectors(sdk.provider.networkType);
22445
22419
  }
22420
+ addHook = this.#hooks.addHook.bind(this.#hooks);
22421
+ removeHook = this.#hooks.removeHook.bind(this.#hooks);
22446
22422
  /**
22447
- * Closes credit account or sets debt to zero (but keep account)
22448
- * @param operation
22449
- * @param creditAccount
22450
- * @param assetsToWithdraw Tokens to withdraw from credit account
22451
- * @param to Address to withdraw underlying to
22423
+ * Finds all available swaps for NORMAL tokens
22424
+ * @param ca
22425
+ * @param cm
22426
+ * @param swapOperation
22427
+ * @param tokenIn
22428
+ * @param tokenOut
22429
+ * @param amount
22430
+ * @param leftoverAmount
22452
22431
  * @param slippage
22453
- * @param closePath
22454
22432
  * @returns
22455
22433
  */
22456
- async closeCreditAccount({
22457
- operation,
22458
- assetsToWithdraw,
22434
+ async findAllSwaps({
22459
22435
  creditAccount: ca,
22460
- to,
22461
- slippage = 50n,
22462
- closePath
22436
+ creditManager: cm,
22437
+ swapOperation,
22438
+ tokenIn,
22439
+ tokenOut,
22440
+ amount,
22441
+ leftoverAmount,
22442
+ slippage
22463
22443
  }) {
22464
- const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
22465
- const routerCloseResult = closePath || await this.sdk.router.findBestClosePath({
22466
- creditAccount: ca,
22467
- creditManager: cm.creditManager,
22468
- slippage
22469
- });
22470
- const calls = [
22471
- ...routerCloseResult.calls,
22472
- ...this.#prepareDisableQuotas(ca),
22473
- ...this.#prepareDecreaseDebt(ca),
22474
- ...this.#prepareDisableTokens(ca),
22475
- ...assetsToWithdraw.map(
22476
- (t) => this.#prepareWithdrawToken(ca, t, MAX_UINT256, to)
22477
- )
22478
- ];
22479
- const tx = operation === "close" ? cm.creditFacade.closeCreditAccount(ca.creditAccount, calls) : cm.creditFacade.multicall(ca.creditAccount, calls);
22480
- return { tx, calls, routerCloseResult };
22481
- }
22482
- /**
22483
- * Repays credit account or sets debt to zero (but keep account)
22484
- * @param operation
22485
- * @param creditAccount
22486
- * @param assetsToWithdraw Tokens to withdraw from credit account
22487
- * @param collateralAssets Tokens to pay for
22488
- * @param to Address to withdraw underlying to
22489
- * @param slippage
22490
- * @param permits
22491
- * @returns
22492
- */
22493
- async repayCreditAccount({
22494
- operation,
22495
- collateralAssets,
22496
- assetsToWithdraw,
22497
- creditAccount: ca,
22498
- permits,
22499
- to
22500
- }) {
22501
- const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
22502
- const addCollateral = collateralAssets.filter((a) => a.balance > 0);
22503
- const calls = [
22504
- ...this.#prepareAddCollateralCalls(addCollateral, ca, permits),
22505
- ...this.#prepareDisableQuotas(ca),
22506
- ...this.#prepareDecreaseDebt(ca),
22507
- ...this.#prepareDisableTokens(ca),
22508
- // TODO: probably needs a way to handle reward tokens
22509
- ...assetsToWithdraw.map(
22510
- (t) => this.#prepareWithdrawToken(ca, t, MAX_UINT256, to)
22511
- )
22512
- ];
22513
- const tx = operation === "close" ? cm.creditFacade.closeCreditAccount(ca.creditAccount, calls) : cm.creditFacade.multicall(ca.creditAccount, calls);
22514
- return { tx, calls };
22444
+ const connectors = this.getAvailableConnectors(cm.collateralTokens);
22445
+ const swapTask = {
22446
+ swapOperation: SWAP_OPERATIONS[swapOperation],
22447
+ creditAccount: ca.creditAccount,
22448
+ tokenIn,
22449
+ tokenOut,
22450
+ connectors,
22451
+ amount,
22452
+ leftoverAmount
22453
+ };
22454
+ const { result } = await this.contract.simulate.findAllSwaps(
22455
+ [swapTask, BigInt(slippage)],
22456
+ {
22457
+ gas: GAS_PER_BLOCK
22458
+ }
22459
+ );
22460
+ const unique = {};
22461
+ result.forEach((r) => {
22462
+ const key = `${r.minAmount.toString()}${r.calls.map((c) => `${c.target.toLowerCase()}${c.callData}`).join("-")}`;
22463
+ unique[key] = {
22464
+ amount: r.amount,
22465
+ minAmount: r.minAmount,
22466
+ calls: [...r.calls]
22467
+ };
22468
+ });
22469
+ return Object.values(unique);
22515
22470
  }
22516
22471
  /**
22517
- * Repays liquidatable credit account
22472
+ * Finds best path to swap all Normal tokens and tokens "on the way" to target one and vice versa
22518
22473
  * @param creditAccount
22519
- * @param assetsToWithdraw Tokens to withdraw from credit account
22520
- * @param collateralAssets Tokens to pay for
22521
- * @param to Address to withdraw underlying to
22474
+ * @param creditManager
22475
+ * @param tokenIn
22476
+ * @param tokenOut
22477
+ * @param amount
22522
22478
  * @param slippage
22523
22479
  * @returns
22524
22480
  */
22525
- async repayAndLiquidateCreditAccount({
22526
- collateralAssets,
22527
- assetsToWithdraw,
22481
+ async findOneTokenPath({
22528
22482
  creditAccount: ca,
22529
- permits,
22530
- to
22483
+ creditManager: cm,
22484
+ tokenIn,
22485
+ tokenOut,
22486
+ amount,
22487
+ slippage
22531
22488
  }) {
22532
- const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
22533
- const priceUpdates = await this.getPriceUpdatesForFacade(ca);
22534
- const addCollateral = collateralAssets.filter((a) => a.balance > 0);
22535
- const calls = [
22536
- ...priceUpdates,
22537
- ...this.#prepareAddCollateralCalls(addCollateral, ca, permits),
22538
- ...assetsToWithdraw.map(
22539
- (t) => this.#prepareWithdrawToken(ca, t, MAX_UINT256, to)
22540
- )
22541
- ];
22542
- const tx = cm.creditFacade.liquidateCreditAccount(
22543
- ca.creditAccount,
22544
- to,
22545
- calls
22489
+ const connectors = this.getAvailableConnectors(cm.collateralTokens);
22490
+ const { result } = await this.contract.simulate.findOneTokenPath(
22491
+ [
22492
+ tokenIn,
22493
+ amount,
22494
+ tokenOut,
22495
+ ca.creditAccount,
22496
+ connectors,
22497
+ BigInt(slippage)
22498
+ ],
22499
+ {
22500
+ gas: GAS_PER_BLOCK
22501
+ }
22546
22502
  );
22547
- return { tx, calls };
22548
- }
22549
- /**
22550
- * Internal wrapper for CreditAccountCompressor.getCreditAccounts + price updates wrapped into multicall
22551
- * @param args
22552
- * @param priceUpdateTxs
22553
- * @param options
22554
- * @returns
22555
- */
22556
- async #getCreditAccounts(args, priceUpdateTxs, options) {
22557
- const blockNumber = options?.blockNumber;
22558
- if (priceUpdateTxs?.length) {
22559
- const resp = await simulateMulticall(this.provider.publicClient, {
22560
- contracts: [
22561
- ...priceUpdateTxs.map(rawTxToMulticallPriceUpdate),
22562
- {
22563
- abi: iCreditAccountCompressorAbi,
22564
- address: this.#compressor,
22565
- functionName: "getCreditAccounts",
22566
- args
22567
- }
22568
- ],
22569
- allowFailure: false,
22570
- gas: 550000000n,
22571
- batchSize: 0,
22572
- // we cannot have price updates and compressor request in different batches
22573
- blockNumber
22574
- });
22575
- const getCreditAccountsResp = resp.pop();
22576
- return getCreditAccountsResp;
22577
- }
22578
- return this.provider.publicClient.readContract({
22579
- abi: iCreditAccountCompressorAbi,
22580
- address: this.#compressor,
22581
- functionName: "getCreditAccounts",
22582
- args,
22583
- blockNumber
22584
- });
22503
+ return {
22504
+ amount: result.amount,
22505
+ minAmount: result.minAmount,
22506
+ calls: [...result.calls]
22507
+ };
22585
22508
  }
22586
22509
  /**
22587
- * Returns raw txs that are needed to update all price feeds so that all credit accounts (possibly from different markets) compute
22588
- * @param accounts
22589
- * @returns
22510
+ * @dev Finds the best path for opening Credit Account and converting all NORMAL tokens and LP token in the way to TARGET
22511
+ * @param creditManager CreditManagerData which represents credit manager you want to use to open Credit Account
22512
+ * @param expectedBalances Expected balances which would be on account accounting also debt. For example,
22513
+ * if you open an USDC Credit Account, borrow 50_000 USDC and provide 10 WETH and 10_USDC as collateral
22514
+ * from your own funds, expectedBalances should be: { "USDC": 60_000 * (10**6), "<address of WETH>": WAD.mul(10) }
22515
+ * @param leftoverBalances Balances to keep on account after opening
22516
+ * @param target Address of symbol of desired token
22517
+ * @param slippage Slippage in PERCENTAGE_FORMAT (100% = 10_000) per operation
22518
+ * @returns PathFinderOpenStrategyResult which
22590
22519
  */
22591
- async getUpdateForAccounts(accounts) {
22592
- const tokensByPool = /* @__PURE__ */ new Map();
22593
- const oracleByPool = /* @__PURE__ */ new Map();
22594
- for (const acc of accounts) {
22595
- const market = this.sdk.marketRegister.findByCreditManager(
22596
- acc.creditManager
22597
- );
22598
- const pool = market.poolFactory.pool.address;
22599
- oracleByPool.set(pool, market.priceOracle);
22600
- for (const t of acc.tokens) {
22601
- if (t.balance > 10n) {
22602
- const tokens = tokensByPool.get(pool) ?? /* @__PURE__ */ new Set();
22603
- tokens.add(t.token);
22604
- tokensByPool.set(pool, tokens);
22605
- }
22520
+ async findOpenStrategyPath({
22521
+ creditManager: cm,
22522
+ expectedBalances,
22523
+ leftoverBalances,
22524
+ target,
22525
+ slippage
22526
+ }) {
22527
+ const [expectedMap, leftoverMap] = [
22528
+ balancesMap(expectedBalances),
22529
+ balancesMap(leftoverBalances)
22530
+ ];
22531
+ const input = cm.collateralTokens.map((token) => ({
22532
+ token,
22533
+ balance: expectedMap.get(token) ?? 0n
22534
+ }));
22535
+ const leftover = cm.collateralTokens.map((token) => ({
22536
+ token,
22537
+ balance: leftoverMap.get(token) ?? 0n
22538
+ }));
22539
+ const connectors = this.getAvailableConnectors(cm.collateralTokens);
22540
+ const {
22541
+ result: [outBalances, result]
22542
+ } = await this.contract.simulate.findOpenStrategyPath(
22543
+ [cm.address, input, leftover, target, connectors, BigInt(slippage)],
22544
+ {
22545
+ gas: GAS_PER_BLOCK
22606
22546
  }
22607
- }
22608
- const priceFeeds = [];
22609
- for (const [pool, priceFeedFactory] of oracleByPool.entries()) {
22610
- const tokens = Array.from(tokensByPool.get(pool) ?? []);
22611
- priceFeeds.push(...priceFeedFactory.priceFeedsForTokens(tokens));
22612
- }
22613
- return this.sdk.priceFeeds.generatePriceFeedsUpdateTxs(priceFeeds);
22614
- }
22615
- /**
22616
- * Returns account price updates in a non-encoded format
22617
- * @param acc
22618
- * @returns
22619
- */
22620
- async getOnDemandPriceUpdates(acc) {
22621
- const market = this.sdk.marketRegister.findByCreditManager(
22622
- acc.creditManager
22623
22547
  );
22624
- const update = await this.getUpdateForAccounts([acc]);
22625
- return market.priceOracle.onDemandPriceUpdates(update);
22548
+ const balancesAfter = Object.fromEntries(
22549
+ outBalances.map((b) => [b.token.toLowerCase(), b.balance])
22550
+ );
22551
+ return {
22552
+ balances: {
22553
+ ...balancesAfter,
22554
+ [target]: (expectedMap.get(target) ?? 0n) + result.amount
22555
+ },
22556
+ minBalances: {
22557
+ ...balancesAfter,
22558
+ [target]: (expectedMap.get(target) ?? 0n) + result.minAmount
22559
+ },
22560
+ amount: result.amount,
22561
+ minAmount: result.minAmount,
22562
+ calls: [...result.calls]
22563
+ };
22626
22564
  }
22627
22565
  /**
22628
- * Returns price updates in format that is accepted by various credit facade methods (multicall, close/liquidate, etc...)
22629
- * @param acc
22630
- * @returns
22566
+ * @dev Finds the path to swap / withdraw all assets from CreditAccount into underlying asset
22567
+ * Can bu used for closing Credit Account and for liquidations as well.
22568
+ * @param creditAccount CreditAccountStruct object used for close path computation
22569
+ * @param creditManager CreditManagerSlice for corresponding credit manager
22570
+ * @param slippage Slippage in PERCENTAGE_FORMAT (100% = 10_000) per operation
22571
+ * @return The best option in PathFinderCloseResult format, which
22572
+ * - underlyingBalance - total balance of underlying token
22573
+ * - calls - list of calls which should be done to swap & unwrap everything to underlying token
22631
22574
  */
22632
- async getPriceUpdatesForFacade(acc) {
22633
- const cm = this.sdk.marketRegister.findCreditManager(acc.creditManager);
22634
- const updates = await this.getOnDemandPriceUpdates(acc);
22635
- return cm.creditFacade.encodeOnDemandPriceUpdates(updates);
22636
- }
22637
- #prepareDisableQuotas(ca) {
22638
- const calls = [];
22639
- for (const { token, quota } of ca.tokens) {
22640
- if (quota > 0n) {
22641
- calls.push({
22642
- target: ca.creditFacade,
22643
- callData: viem.encodeFunctionData({
22644
- abi: iCreditFacadeV3MulticallAbi,
22645
- functionName: "updateQuota",
22646
- args: [token, MIN_INT96, 0n]
22647
- })
22648
- });
22649
- }
22650
- }
22651
- return calls;
22652
- }
22653
- #prepareDecreaseDebt(ca) {
22654
- if (ca.debt > 0n) {
22655
- return [
22656
- {
22657
- target: ca.creditFacade,
22658
- callData: viem.encodeFunctionData({
22659
- abi: iCreditFacadeV3MulticallAbi,
22660
- functionName: "decreaseDebt",
22661
- args: [MAX_UINT256]
22662
- })
22663
- }
22664
- ];
22665
- }
22666
- return [];
22667
- }
22668
- #prepareDisableTokens(ca) {
22669
- const calls = [];
22670
- for (const t of ca.tokens) {
22671
- if (t.token !== ca.underlying && (t.mask & ca.enabledTokensMask) !== 0n && t.quota === 0n) {
22672
- calls.push({
22673
- target: ca.creditFacade,
22674
- callData: viem.encodeFunctionData({
22675
- abi: iCreditFacadeV3MulticallAbi,
22676
- functionName: "disableToken",
22677
- args: [t.token]
22678
- })
22679
- });
22680
- }
22681
- }
22682
- return calls;
22683
- }
22684
- #prepareWithdrawToken(ca, token, amount, to) {
22685
- return {
22686
- target: ca.creditFacade,
22687
- callData: viem.encodeFunctionData({
22688
- abi: iCreditFacadeV3MulticallAbi,
22689
- functionName: "withdrawCollateral",
22690
- args: [token, amount, to]
22691
- })
22692
- };
22693
- }
22694
- #prepareAddCollateralCalls(assets, ca, permits) {
22695
- const calls = assets.map(({ token, balance }) => {
22696
- const p = permits[token];
22697
- if (p) {
22698
- return {
22699
- target: ca.creditFacade,
22700
- callData: viem.encodeFunctionData({
22701
- abi: iCreditFacadeV3MulticallAbi,
22702
- functionName: "addCollateralWithPermit",
22703
- args: [token, balance, p.deadline, p.v, p.r, p.s]
22704
- })
22705
- };
22706
- }
22707
- return {
22708
- target: ca.creditFacade,
22709
- callData: viem.encodeFunctionData({
22710
- abi: iCreditFacadeV3MulticallAbi,
22711
- functionName: "addCollateral",
22712
- args: [token, balance]
22713
- })
22714
- };
22715
- });
22716
- return calls;
22717
- }
22718
- /**
22719
- * Returns addresses of pools of attached markets
22720
- */
22721
- get pools() {
22722
- return this.sdk.marketRegister.pools.map((p) => p.pool.address);
22723
- }
22724
- };
22725
- var AddressProviderContractV3_1 = class extends BaseContract {
22726
- #addresses = {};
22727
- versions = {};
22728
- latest = {};
22729
- constructor(sdk, address) {
22730
- super(sdk, {
22731
- addr: address,
22732
- name: "AddressProviderV3",
22733
- abi: iAddressProviderV3_1Abi
22734
- });
22735
- }
22736
- parseFunctionParams(params) {
22737
- switch (params.functionName) {
22738
- case "setAddress": {
22739
- if (params.args.length !== 3) {
22740
- const [key2, saveVersion2] = params.args;
22741
- return [key2, `${saveVersion2}`];
22742
- }
22743
- const [key, value, saveVersion] = params.args;
22744
- return [viem.bytesToString(viem.toBytes(key)), value, `${saveVersion}`];
22745
- }
22746
- default:
22747
- return void 0;
22748
- }
22749
- }
22750
- setInternalAddress(key, address, version) {
22751
- if (!this.#addresses[key]) {
22752
- this.#addresses[key] = {};
22753
- }
22754
- this.#addresses[key][version] = address;
22755
- if (!this.latest[key] || version > this.latest[key]) {
22756
- this.latest[key] = version;
22757
- }
22758
- if (!this.versions[key]) {
22759
- this.versions[key] = /* @__PURE__ */ new Set();
22760
- }
22761
- this.versions[key].add(version);
22762
- }
22763
- getAddress(contract, version = NO_VERSION) {
22764
- if (!this.#addresses[contract]) {
22765
- throw new Error(`Address ${contract}, version: ${version} not found`);
22766
- }
22767
- const result = this.#addresses[contract][version];
22768
- if (!result) {
22769
- throw new Error(`Address ${contract}, version: ${version} not found`);
22770
- }
22771
- return result;
22772
- }
22773
- getLatestVersion(contract) {
22774
- if (!this.latest[contract]) {
22775
- throw new Error(`Latest version for ${contract} not found`);
22776
- }
22777
- this.logger?.debug(
22778
- `Latest version found for ${contract} : ${this.latest[contract]}`
22575
+ async findBestClosePath({
22576
+ creditAccount: ca,
22577
+ creditManager: cm,
22578
+ slippage,
22579
+ balances
22580
+ }) {
22581
+ const { pathOptions, expected, leftover, connectors } = this.getFindClosePathInput(
22582
+ ca,
22583
+ cm,
22584
+ balances ? {
22585
+ expectedBalances: assetsMap(balances.expectedBalances),
22586
+ leftoverBalances: assetsMap(balances.leftoverBalances)
22587
+ } : void 0
22779
22588
  );
22780
- return this.getAddress(contract, this.latest[contract]);
22781
- }
22782
- async syncState(blockNumber) {
22783
- const entries = await this.contract.read.getAllSavedContracts({
22784
- blockNumber
22785
- });
22786
- for (const log of entries) {
22787
- this.setInternalAddress(log.key, log.value, Number(log.version));
22788
- }
22789
- }
22790
- stateHuman(raw = true) {
22791
- return {
22792
- ...super.stateHuman(raw),
22793
- addresses: Object.entries(this.#addresses).map(([key, value]) => {
22794
- return Object.entries(value).map(([version, address]) => {
22795
- return {
22796
- key,
22797
- version,
22798
- address: this.sdk.provider.addressLabels.get(address)
22799
- };
22800
- });
22801
- }).reduce(
22802
- (acc, vals) => {
22803
- for (const val of vals) {
22804
- if (!acc[val.key]) {
22805
- acc[val.key] = {};
22806
- }
22807
- acc[val.key][val.version] = val.address;
22808
- }
22809
- return acc;
22810
- },
22811
- {}
22812
- )
22813
- };
22814
- }
22815
- processLog(log) {
22816
- switch (log.eventName) {
22817
- case "SetAddress": {
22818
- const parsedLog2 = viem.parseEventLogs({
22819
- abi: this.abi,
22820
- eventName: "SetAddress",
22821
- logs: [log]
22822
- })[0];
22823
- const key = parsedLog2.args.key;
22824
- this.setInternalAddress(
22825
- key,
22826
- log.args.value,
22827
- Number(parsedLog2.args.version)
22828
- );
22829
- break;
22830
- }
22831
- default:
22832
- this.logger?.warn(`Unknown event: ${log.eventName}`);
22833
- break;
22834
- }
22835
- }
22836
- };
22837
-
22838
- // src/sdk/core/BotListV3Contract.ts
22839
- var BotListContract = class extends BaseContract {
22840
- #approvedCreditManagers;
22841
- #currentBlock;
22842
- constructor(sdk, address) {
22843
- super(sdk, { addr: address, name: "BotListV3", abi: botListV3Abi });
22844
- this.#currentBlock = ADDRESS_PROVIDER_BLOCK[sdk.provider.networkType];
22845
- }
22846
- parseFunctionParams(params) {
22847
- switch (params.functionName) {
22848
- case "setCreditManagerApprovedStatus": {
22849
- const [creditManager, status] = params.args;
22850
- return [this.addressLabels.get(creditManager), `${status}`];
22851
- }
22852
- case "setBotSpecialPermissions": {
22853
- const [bot, creditManager, permissions] = params.args;
22854
- return [
22855
- this.addressLabels.get(bot),
22856
- this.addressLabels.get(creditManager),
22857
- botPermissionsToString(permissions)
22858
- ];
22859
- }
22860
- default:
22861
- return void 0;
22862
- }
22863
- }
22864
- async syncState(toBlock) {
22865
- const logs = await this.provider.publicClient.getContractEvents({
22866
- address: this.address,
22867
- abi: this.abi,
22868
- fromBlock: this.#currentBlock,
22869
- toBlock
22589
+ await this.#hooks.triggerHooks("foundPathOptions", {
22590
+ creditAccount: ca.creditAccount,
22591
+ pathOptions,
22592
+ expected,
22593
+ leftover,
22594
+ connectors
22870
22595
  });
22871
- logs.forEach((e) => this.processLog(e));
22872
- this.#currentBlock = toBlock;
22873
- }
22874
- processLog(log) {
22875
- switch (log.eventName) {
22876
- case "SetCreditManagerApprovedStatus":
22877
- if (log.args.approved) {
22878
- this.approvedCreditManagers.add(log.args.creditManager);
22879
- } else {
22880
- this.approvedCreditManagers.delete(log.args.creditManager);
22881
- }
22882
- break;
22883
- case "SetBotSpecialPermissions":
22884
- this.logger?.debug(
22885
- `Bot ${log.args.bot} has been given permissions ${botPermissionsToString(
22886
- log.args.permissions
22887
- )} for credit manager ${log.args.creditManager}`
22888
- );
22889
- break;
22890
- default:
22891
- this.logger?.warn(`Unknown event: ${log.eventName}`);
22892
- break;
22596
+ let results = [];
22597
+ for (const po of pathOptions) {
22598
+ const { result: result2 } = await this.contract.simulate.findBestClosePath(
22599
+ [
22600
+ ca.creditAccount,
22601
+ expected,
22602
+ leftover,
22603
+ connectors,
22604
+ BigInt(slippage),
22605
+ po,
22606
+ BigInt(LOOPS_PER_TX),
22607
+ false
22608
+ ],
22609
+ {
22610
+ gas: GAS_PER_BLOCK
22611
+ }
22612
+ );
22613
+ results.push({
22614
+ ...result2,
22615
+ calls: [...result2.calls]
22616
+ });
22893
22617
  }
22618
+ const bestResult = results.reduce(compareRouterResults, {
22619
+ amount: 0n,
22620
+ minAmount: 0n,
22621
+ calls: []
22622
+ });
22623
+ const underlyingBalance = ca.tokens.find((t) => t.token === ca.underlying)?.balance ?? 0n;
22624
+ const result = {
22625
+ amount: bestResult.amount,
22626
+ minAmount: bestResult.minAmount,
22627
+ calls: bestResult.calls.map((c) => ({
22628
+ callData: c.callData,
22629
+ target: c.target
22630
+ })),
22631
+ underlyingBalance: underlyingBalance + bestResult.minAmount
22632
+ };
22633
+ return result;
22894
22634
  }
22895
- get approvedCreditManagers() {
22896
- if (!this.#approvedCreditManagers) {
22897
- throw new Error(
22898
- "BotListContract state needs to be synced to load approvedCreditManagers"
22899
- );
22635
+ /**
22636
+ * Finds input to be used with findBestClosePath
22637
+ * @param ca
22638
+ * @param cm
22639
+ * @returns
22640
+ */
22641
+ getFindClosePathInput(ca, cm, balances) {
22642
+ const b = balances || this.getDefaultExpectedAndLeftover(ca);
22643
+ const { leftoverBalances, expectedBalances } = b;
22644
+ const pathOptions = PathOptionFactory.generatePathOptions(
22645
+ ca.tokens,
22646
+ this.provider.networkType,
22647
+ LOOPS_PER_TX
22648
+ );
22649
+ const expected = cm.collateralTokens.map((token) => {
22650
+ const actual = expectedBalances.get(token)?.balance || 0n;
22651
+ return {
22652
+ token,
22653
+ balance: actual > 10n ? actual : 0n
22654
+ };
22655
+ });
22656
+ const leftover = cm.collateralTokens.map((token) => ({
22657
+ token,
22658
+ balance: leftoverBalances.get(token)?.balance || 1n
22659
+ }));
22660
+ const connectors = this.getAvailableConnectors(cm.collateralTokens);
22661
+ return { expected, leftover, connectors, pathOptions };
22662
+ }
22663
+ getDefaultExpectedAndLeftover(ca) {
22664
+ const expectedBalances = new AddressMap();
22665
+ const leftoverBalances = new AddressMap();
22666
+ for (const { token: t, balance, mask } of ca.tokens) {
22667
+ const token = t;
22668
+ const isEnabled = (mask & ca.enabledTokensMask) !== 0n;
22669
+ expectedBalances.upsert(token, { token, balance });
22670
+ const decimals = this.sdk.tokensMeta.decimals(token);
22671
+ const minBalance = 10n ** BigInt(Math.max(8, decimals) - 8);
22672
+ if (balance < minBalance || !isEnabled) {
22673
+ leftoverBalances.upsert(token, { token, balance });
22674
+ }
22900
22675
  }
22901
- return this.#approvedCreditManagers;
22676
+ return { expectedBalances, leftoverBalances };
22902
22677
  }
22903
- stateHuman(raw = true) {
22904
- return super.stateHuman(raw);
22678
+ getAvailableConnectors(collateralTokens) {
22679
+ return collateralTokens.filter(
22680
+ (t) => this.#connectors.includes(t.toLowerCase())
22681
+ );
22905
22682
  }
22906
22683
  };
22684
+ function compareRouterResults(a, b) {
22685
+ return a.amount > b.amount ? a : b;
22686
+ }
22687
+ function balancesMap(assets) {
22688
+ return new AddressMap(assets.map(({ token, balance }) => [token, balance]));
22689
+ }
22690
+ function assetsMap(assets) {
22691
+ return new AddressMap(assets.map((a) => [a.token, a]));
22692
+ }
22907
22693
 
22908
- // src/sdk/core/GearStakingV3Contract.ts
22909
- var GearStakingContract = class extends BaseContract {
22910
- constructor(sdk, address) {
22911
- super(sdk, { addr: address, name: "GearStakingV3", abi: gearStakingV3Abi });
22694
+ // src/sdk/accounts/CreditAccountsService.ts
22695
+ var CreditAccountsService = class extends SDKConstruct {
22696
+ #compressor;
22697
+ constructor(sdk) {
22698
+ super(sdk);
22699
+ this.#compressor = sdk.addressProvider.getLatestVersion(
22700
+ AP_CREDIT_ACCOUNT_COMPRESSOR
22701
+ );
22912
22702
  }
22913
- parseFunctionParams(params) {
22914
- switch (params.functionName) {
22915
- case "setVotingContractStatus": {
22916
- const [address, status] = params.args;
22917
- return [this.addressLabels.get(address), VotingContractStatus[status]];
22918
- }
22919
- default:
22920
- return void 0;
22703
+ /**
22704
+ * Returns single credit account data, or undefined if it's not found
22705
+ * Performs all necessary price feed updates under the hood
22706
+ * @param account
22707
+ * @param options
22708
+ * @returns
22709
+ */
22710
+ async getCreditAccountData(account, options) {
22711
+ const blockNumber = options?.blockNumber;
22712
+ let raw;
22713
+ try {
22714
+ raw = await this.provider.publicClient.readContract({
22715
+ abi: iCreditAccountCompressorAbi,
22716
+ address: this.#compressor,
22717
+ functionName: "getCreditAccountData",
22718
+ args: [account],
22719
+ blockNumber
22720
+ });
22721
+ } catch (e) {
22722
+ return void 0;
22921
22723
  }
22724
+ if (raw.success) {
22725
+ return raw;
22726
+ }
22727
+ const { txs: priceUpdateTxs, timestamp: _ } = await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs();
22728
+ const resp = await simulateMulticall(this.provider.publicClient, {
22729
+ contracts: [
22730
+ ...priceUpdateTxs.map(rawTxToMulticallPriceUpdate),
22731
+ {
22732
+ abi: iCreditAccountCompressorAbi,
22733
+ address: this.#compressor,
22734
+ functionName: "getCreditAccountData",
22735
+ args: [account]
22736
+ }
22737
+ ],
22738
+ allowFailure: false,
22739
+ gas: 550000000n,
22740
+ batchSize: 0,
22741
+ // we cannot have price updates and compressor request in different batches
22742
+ blockNumber
22743
+ });
22744
+ const cad = resp.pop();
22745
+ return cad;
22922
22746
  }
22923
- stateHuman(raw = true) {
22924
- return {
22925
- ...super.stateHuman(raw),
22926
- successor: this.labelAddress(ADDRESS_0X0),
22927
- migrator: this.labelAddress(ADDRESS_0X0)
22747
+ /**
22748
+ * Methods to get all credit accounts with some optional filtering
22749
+ * Performs all necessary price feed updates under the hood
22750
+ *
22751
+ * TODO: do we want to expose pagination?
22752
+ * TODO: do we want to expose "reverting"?
22753
+ * TODO: do we want to expose MarketFilter in any way? If so, we need to check that the MarketFilter is compatibled with attached markets?
22754
+ * @param args
22755
+ * @param options
22756
+ * @returns returned credit accounts are sorted by health factor in ascending order
22757
+ */
22758
+ async getCreditAccounts(args, options) {
22759
+ const {
22760
+ creditManager,
22761
+ includeZeroDebt = false,
22762
+ maxHealthFactor = 65535,
22763
+ // TODO: this will change to bigint
22764
+ minHealthFactor = 0,
22765
+ owner = ADDRESS_0X0
22766
+ } = args ?? {};
22767
+ const arg0 = creditManager ?? {
22768
+ curators: [],
22769
+ pools: this.pools,
22770
+ underlying: ADDRESS_0X0
22771
+ };
22772
+ const caFilter = {
22773
+ owner,
22774
+ includeZeroDebt,
22775
+ minHealthFactor,
22776
+ maxHealthFactor
22928
22777
  };
22778
+ const { txs: priceUpdateTxs, timestamp: _ } = await this.sdk.priceFeeds.generatePriceFeedsUpdateTxs();
22779
+ const allCAs = [];
22780
+ for (const reverting of [false, true]) {
22781
+ let offset = 0n;
22782
+ do {
22783
+ const [accounts, newOffset] = await this.#getCreditAccounts(
22784
+ [arg0, { ...caFilter, reverting }, offset],
22785
+ priceUpdateTxs,
22786
+ options
22787
+ );
22788
+ allCAs.push(...accounts);
22789
+ offset = newOffset;
22790
+ } while (offset !== 0n);
22791
+ }
22792
+ return allCAs.sort((a, b) => Number(a.healthFactor - b.healthFactor));
22929
22793
  }
22930
- };
22931
- var PathOptionFactory = class _PathOptionFactory {
22932
- // TODO: get rid of token data from SDK
22933
- static generatePathOptions(balances, network, loopsInTx) {
22934
- const curvePools = _PathOptionFactory.getCurvePools(balances);
22935
- const balancerPools = _PathOptionFactory.getBalancerPools(balances);
22936
- const curveInitPO = curvePools.map((symbol) => {
22937
- return {
22938
- target: sdkGov.tokenDataByNetwork[network][symbol],
22939
- option: 0,
22940
- totalOptions: sdkGov.contractParams[sdkGov.curveTokens[symbol].pool].tokens.length
22941
- };
22794
+ /**
22795
+ * Generates transaction to liquidate credit account
22796
+ * @param account
22797
+ * @param to Address to transfer underlying left after liquidation
22798
+ * @param slippage
22799
+ * @returns
22800
+ */
22801
+ async fullyLiquidate(account, to, slippage = 50n) {
22802
+ const cm = this.sdk.marketRegister.findCreditManager(account.creditManager);
22803
+ const routerCloseResult = await this.sdk.router.findBestClosePath({
22804
+ creditAccount: account,
22805
+ creditManager: cm.creditManager,
22806
+ slippage
22942
22807
  });
22943
- const balancerInitPO = balancerPools.map((symbol) => {
22944
- return {
22945
- target: sdkGov.tokenDataByNetwork[network][symbol],
22946
- option: 0,
22947
- totalOptions: sdkGov.balancerLpTokens[symbol].underlying.length
22948
- };
22808
+ const priceUpdates = await this.getPriceUpdatesForFacade(account);
22809
+ const tx = cm.creditFacade.liquidateCreditAccount(
22810
+ account.creditAccount,
22811
+ to,
22812
+ [...priceUpdates, ...routerCloseResult.calls]
22813
+ );
22814
+ return { tx, routerCloseResult };
22815
+ }
22816
+ /**
22817
+ * Closes credit account or sets debt to zero (but keep account)
22818
+ * @param operation
22819
+ * @param creditAccount
22820
+ * @param assetsToWithdraw Tokens to withdraw from credit account
22821
+ * @param to Address to withdraw underlying to
22822
+ * @param slippage
22823
+ * @param closePath
22824
+ * @returns
22825
+ */
22826
+ async closeCreditAccount({
22827
+ operation,
22828
+ assetsToWithdraw,
22829
+ creditAccount: ca,
22830
+ to,
22831
+ slippage = 50n,
22832
+ closePath
22833
+ }) {
22834
+ const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
22835
+ const routerCloseResult = closePath || await this.sdk.router.findBestClosePath({
22836
+ creditAccount: ca,
22837
+ creditManager: cm.creditManager,
22838
+ slippage
22949
22839
  });
22950
- const initPO = [...curveInitPO, ...balancerInitPO];
22951
- const totalLoops = initPO.reduce(
22952
- (acc, item) => acc * item.totalOptions,
22953
- 1
22840
+ const calls = [
22841
+ ...routerCloseResult.calls,
22842
+ ...this.#prepareDisableQuotas(ca),
22843
+ ...this.#prepareDecreaseDebt(ca),
22844
+ ...this.#prepareDisableTokens(ca),
22845
+ ...assetsToWithdraw.map(
22846
+ (t) => this.#prepareWithdrawToken(ca, t, MAX_UINT256, to)
22847
+ )
22848
+ ];
22849
+ const tx = operation === "close" ? cm.creditFacade.closeCreditAccount(ca.creditAccount, calls) : cm.creditFacade.multicall(ca.creditAccount, calls);
22850
+ return { tx, calls, routerCloseResult };
22851
+ }
22852
+ /**
22853
+ * Repays credit account or sets debt to zero (but keep account)
22854
+ * @param operation
22855
+ * @param creditAccount
22856
+ * @param assetsToWithdraw Tokens to withdraw from credit account
22857
+ * @param collateralAssets Tokens to pay for
22858
+ * @param to Address to withdraw underlying to
22859
+ * @param slippage
22860
+ * @param permits
22861
+ * @returns
22862
+ */
22863
+ async repayCreditAccount({
22864
+ operation,
22865
+ collateralAssets,
22866
+ assetsToWithdraw,
22867
+ creditAccount: ca,
22868
+ permits,
22869
+ to
22870
+ }) {
22871
+ const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
22872
+ const addCollateral = collateralAssets.filter((a) => a.balance > 0);
22873
+ const calls = [
22874
+ ...this.#prepareAddCollateral(addCollateral, ca, permits),
22875
+ ...this.#prepareDisableQuotas(ca),
22876
+ ...this.#prepareDecreaseDebt(ca),
22877
+ ...this.#prepareDisableTokens(ca),
22878
+ // TODO: probably needs a way to handle reward tokens
22879
+ ...assetsToWithdraw.map(
22880
+ (t) => this.#prepareWithdrawToken(ca, t, MAX_UINT256, to)
22881
+ )
22882
+ ];
22883
+ const tx = operation === "close" ? cm.creditFacade.closeCreditAccount(ca.creditAccount, calls) : cm.creditFacade.multicall(ca.creditAccount, calls);
22884
+ return { tx, calls };
22885
+ }
22886
+ /**
22887
+ * Repays liquidatable credit account
22888
+ * @param creditAccount
22889
+ * @param assetsToWithdraw Tokens to withdraw from credit account
22890
+ * @param collateralAssets Tokens to pay for
22891
+ * @param to Address to withdraw underlying to
22892
+ * @param slippage
22893
+ * @returns
22894
+ */
22895
+ async repayAndLiquidateCreditAccount({
22896
+ collateralAssets,
22897
+ assetsToWithdraw,
22898
+ creditAccount: ca,
22899
+ permits,
22900
+ to
22901
+ }) {
22902
+ const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
22903
+ const priceUpdates = await this.getPriceUpdatesForFacade(ca);
22904
+ const addCollateral = collateralAssets.filter((a) => a.balance > 0);
22905
+ const calls = [
22906
+ ...priceUpdates,
22907
+ ...this.#prepareAddCollateral(addCollateral, ca, permits),
22908
+ ...assetsToWithdraw.map(
22909
+ (t) => this.#prepareWithdrawToken(ca, t, MAX_UINT256, to)
22910
+ )
22911
+ ];
22912
+ const tx = cm.creditFacade.liquidateCreditAccount(
22913
+ ca.creditAccount,
22914
+ to,
22915
+ calls
22954
22916
  );
22955
- const result = [];
22956
- let currentPo = [...initPO];
22957
- for (let i = 0; i < totalLoops; i++) {
22958
- if (i % loopsInTx === 0) {
22959
- result.push(currentPo);
22960
- }
22961
- if (i < totalLoops - 1) {
22962
- currentPo = _PathOptionFactory.next(currentPo);
22963
- }
22964
- }
22965
- return result;
22917
+ return { tx, calls };
22966
22918
  }
22967
- static getCurvePools(balances) {
22968
- const nonZeroBalances = balances.filter((b) => b.balance > 1n);
22969
- const curvePools = nonZeroBalances.map((b) => sdkGov.getTokenSymbol(b.token)).filter((symbol) => sdkGov.isCurveLPToken(symbol));
22970
- const yearnCurveTokens = Object.entries(sdkGov.yearnTokens).filter(([, data]) => sdkGov.isCurveLPToken(data.underlying)).map(([token]) => token);
22971
- const curvePoolsFromYearn = nonZeroBalances.map((b) => sdkGov.getTokenSymbol(b.token)).filter((symbol) => yearnCurveTokens.includes(symbol)).map(
22972
- (symbol) => sdkGov.yearnTokens[symbol].underlying
22919
+ async updateQuotas(props) {
22920
+ const { creditAccount } = props;
22921
+ const cm = this.sdk.marketRegister.findCreditManager(
22922
+ creditAccount.creditManager
22973
22923
  );
22974
- const convexCurveTokens = Object.entries(sdkGov.convexTokens).filter(([, data]) => sdkGov.isCurveLPToken(data.underlying)).map(([token]) => token);
22975
- const curvePoolsFromConvex = nonZeroBalances.map((b) => sdkGov.getTokenSymbol(b.token)).filter((symbol) => convexCurveTokens.includes(symbol)).map((symbol) => sdkGov.convexTokens[symbol].underlying);
22976
- const curveSet = /* @__PURE__ */ new Set([
22977
- ...curvePools,
22978
- ...curvePoolsFromYearn,
22979
- ...curvePoolsFromConvex
22924
+ const priceUpdates = await this.getPriceUpdatesForFacade(creditAccount);
22925
+ const calls = [
22926
+ ...priceUpdates,
22927
+ ...this.#prepareUpdateQuotas(props)
22928
+ ];
22929
+ const tx = cm.creditFacade.multicall(creditAccount.creditAccount, [
22930
+ ...priceUpdates,
22931
+ ...this.#prepareUpdateQuotas(props)
22980
22932
  ]);
22981
- return Array.from(curveSet.values());
22933
+ return {
22934
+ tx,
22935
+ calls
22936
+ };
22982
22937
  }
22983
- static getBalancerPools(balances) {
22984
- const nonZeroBalances = Object.entries(balances).filter(
22985
- ([, balance]) => balance.balance > 1
22938
+ async addCollateral(props) {
22939
+ const { creditAccount, asset, permit, ethAmount } = props;
22940
+ const cm = this.sdk.marketRegister.findCreditManager(
22941
+ creditAccount.creditManager
22986
22942
  );
22987
- const balancerPools = nonZeroBalances.map(([token]) => sdkGov.getTokenSymbol(token)).filter((symbol) => sdkGov.isBalancerLPToken(symbol));
22988
- const balancerAuraTokens = Object.entries(sdkGov.auraTokens).filter(([, data]) => sdkGov.isBalancerLPToken(data.underlying)).map(([token]) => token);
22989
- const balancerTokensFromAura = nonZeroBalances.map(([token]) => sdkGov.getTokenSymbol(token)).filter((symbol) => balancerAuraTokens.includes(symbol)).map(
22990
- (symbol) => sdkGov.auraTokens[symbol].underlying
22943
+ const priceUpdatesCalls = await this.getPriceUpdatesForFacade(creditAccount);
22944
+ const addCollateralCalls = this.#prepareAddCollateral(
22945
+ [asset],
22946
+ creditAccount,
22947
+ permit ? { [asset.token]: permit } : {}
22991
22948
  );
22992
- const balancerSet = /* @__PURE__ */ new Set([...balancerPools, ...balancerTokensFromAura]);
22993
- return Array.from(balancerSet.values());
22949
+ const updateQuotaCalls = this.#prepareUpdateQuotas(props);
22950
+ const calls = [
22951
+ ...priceUpdatesCalls,
22952
+ ...addCollateralCalls,
22953
+ ...updateQuotaCalls
22954
+ ];
22955
+ const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
22956
+ tx.value = ethAmount.toString(10);
22957
+ return { tx, calls };
22994
22958
  }
22995
- static next(path) {
22996
- let newPath = [...path];
22997
- for (let i = path.length - 1; i >= 0; i--) {
22998
- const po = { ...newPath[i] };
22999
- po.option++;
23000
- newPath[i] = po;
23001
- if (po.option < po.totalOptions) return newPath;
23002
- po.option = 0;
22959
+ async changeDebt({
22960
+ creditAccount,
22961
+ amount
22962
+ }) {
22963
+ if (amount === 0n) {
22964
+ throw new Error("debt increase or decrease must be non-zero");
23003
22965
  }
23004
- throw new Error("Path options overflow");
22966
+ const isDecrease = amount < 0n;
22967
+ const change = isDecrease ? -amount : amount;
22968
+ const cm = this.sdk.marketRegister.findCreditManager(
22969
+ creditAccount.creditManager
22970
+ );
22971
+ const priceUpdatesCalls = await this.getPriceUpdatesForFacade(creditAccount);
22972
+ const underlyingEnabled = (creditAccount.enabledTokensMask & 1n) === 1n;
22973
+ const shouldEnable = !isDecrease && !underlyingEnabled;
22974
+ const calls = [
22975
+ ...priceUpdatesCalls,
22976
+ ...shouldEnable ? this.#prepareEnableTokens(creditAccount, [creditAccount.underlying]) : [],
22977
+ {
22978
+ target: creditAccount.creditFacade,
22979
+ callData: viem.encodeFunctionData({
22980
+ abi: iCreditFacadeV3MulticallAbi,
22981
+ functionName: isDecrease ? "increaseDebt" : "decreaseDebt",
22982
+ args: [change]
22983
+ })
22984
+ }
22985
+ ];
22986
+ const tx = cm.creditFacade.multicall(creditAccount.creditAccount, calls);
22987
+ return { tx, calls };
23005
22988
  }
23006
- };
23007
-
23008
- // src/sdk/router/RouterV3Contract.ts
23009
- var MAX_GAS_PER_ROUTE = 200000000n;
23010
- var GAS_PER_BLOCK = 400000000n;
23011
- var LOOPS_PER_TX = Number(GAS_PER_BLOCK / MAX_GAS_PER_ROUTE);
23012
- var SWAP_OPERATIONS = {
23013
- EXACT_INPUT: 0,
23014
- EXACT_INPUT_ALL: 1,
23015
- EXACT_OUTPUT: 2
23016
- };
23017
- var RouterV3Contract = class extends BaseContract {
23018
- #connectors;
23019
- #hooks = new Hooks();
23020
- constructor(sdk, address) {
23021
- super(sdk, {
23022
- addr: address,
23023
- name: "RouterV3",
23024
- abi: routerV3Abi
22989
+ /**
22990
+ * Internal wrapper for CreditAccountCompressor.getCreditAccounts + price updates wrapped into multicall
22991
+ * @param args
22992
+ * @param priceUpdateTxs
22993
+ * @param options
22994
+ * @returns
22995
+ */
22996
+ async #getCreditAccounts(args, priceUpdateTxs, options) {
22997
+ const blockNumber = options?.blockNumber;
22998
+ if (priceUpdateTxs?.length) {
22999
+ const resp = await simulateMulticall(this.provider.publicClient, {
23000
+ contracts: [
23001
+ ...priceUpdateTxs.map(rawTxToMulticallPriceUpdate),
23002
+ {
23003
+ abi: iCreditAccountCompressorAbi,
23004
+ address: this.#compressor,
23005
+ functionName: "getCreditAccounts",
23006
+ args
23007
+ }
23008
+ ],
23009
+ allowFailure: false,
23010
+ gas: 550000000n,
23011
+ batchSize: 0,
23012
+ // we cannot have price updates and compressor request in different batches
23013
+ blockNumber
23014
+ });
23015
+ const getCreditAccountsResp = resp.pop();
23016
+ return getCreditAccountsResp;
23017
+ }
23018
+ return this.provider.publicClient.readContract({
23019
+ abi: iCreditAccountCompressorAbi,
23020
+ address: this.#compressor,
23021
+ functionName: "getCreditAccounts",
23022
+ args,
23023
+ blockNumber
23025
23024
  });
23026
- this.#connectors = sdkGov.getConnectors(sdk.provider.networkType);
23027
23025
  }
23028
- addHook = this.#hooks.addHook.bind(this.#hooks);
23029
- removeHook = this.#hooks.removeHook.bind(this.#hooks);
23030
23026
  /**
23031
- * Finds all available swaps for NORMAL tokens
23032
- * @param ca
23033
- * @param cm
23034
- * @param swapOperation
23035
- * @param tokenIn
23036
- * @param tokenOut
23037
- * @param amount
23038
- * @param leftoverAmount
23039
- * @param slippage
23027
+ * Returns raw txs that are needed to update all price feeds so that all credit accounts (possibly from different markets) compute
23028
+ * @param accounts
23040
23029
  * @returns
23041
23030
  */
23042
- async findAllSwaps({
23043
- creditAccount: ca,
23044
- creditManager: cm,
23045
- swapOperation,
23046
- tokenIn,
23047
- tokenOut,
23048
- amount,
23049
- leftoverAmount,
23050
- slippage
23051
- }) {
23052
- const connectors = this.getAvailableConnectors(cm.collateralTokens);
23053
- const swapTask = {
23054
- swapOperation: SWAP_OPERATIONS[swapOperation],
23055
- creditAccount: ca.creditAccount,
23056
- tokenIn,
23057
- tokenOut,
23058
- connectors,
23059
- amount,
23060
- leftoverAmount
23061
- };
23062
- const { result } = await this.contract.simulate.findAllSwaps(
23063
- [swapTask, BigInt(slippage)],
23064
- {
23065
- gas: GAS_PER_BLOCK
23031
+ async getUpdateForAccounts(accounts) {
23032
+ const tokensByPool = /* @__PURE__ */ new Map();
23033
+ const oracleByPool = /* @__PURE__ */ new Map();
23034
+ for (const acc of accounts) {
23035
+ const market = this.sdk.marketRegister.findByCreditManager(
23036
+ acc.creditManager
23037
+ );
23038
+ const pool = market.poolFactory.pool.address;
23039
+ oracleByPool.set(pool, market.priceOracle);
23040
+ for (const t of acc.tokens) {
23041
+ if (t.balance > 10n) {
23042
+ const tokens = tokensByPool.get(pool) ?? /* @__PURE__ */ new Set();
23043
+ tokens.add(t.token);
23044
+ tokensByPool.set(pool, tokens);
23045
+ }
23066
23046
  }
23047
+ }
23048
+ const priceFeeds = [];
23049
+ for (const [pool, priceFeedFactory] of oracleByPool.entries()) {
23050
+ const tokens = Array.from(tokensByPool.get(pool) ?? []);
23051
+ priceFeeds.push(...priceFeedFactory.priceFeedsForTokens(tokens));
23052
+ }
23053
+ return this.sdk.priceFeeds.generatePriceFeedsUpdateTxs(priceFeeds);
23054
+ }
23055
+ /**
23056
+ * Returns account price updates in a non-encoded format
23057
+ * @param acc
23058
+ * @returns
23059
+ */
23060
+ async getOnDemandPriceUpdates(acc) {
23061
+ const market = this.sdk.marketRegister.findByCreditManager(
23062
+ acc.creditManager
23067
23063
  );
23068
- const unique = {};
23069
- result.forEach((r) => {
23070
- const key = `${r.minAmount.toString()}${r.calls.map((c) => `${c.target.toLowerCase()}${c.callData}`).join("-")}`;
23071
- unique[key] = {
23072
- amount: r.amount,
23073
- minAmount: r.minAmount,
23074
- calls: [...r.calls]
23075
- };
23076
- });
23077
- return Object.values(unique);
23064
+ const update = await this.getUpdateForAccounts([acc]);
23065
+ return market.priceOracle.onDemandPriceUpdates(update);
23078
23066
  }
23079
23067
  /**
23080
- * Finds best path to swap all Normal tokens and tokens "on the way" to target one and vice versa
23081
- * @param creditAccount
23082
- * @param creditManager
23083
- * @param tokenIn
23084
- * @param tokenOut
23085
- * @param amount
23086
- * @param slippage
23068
+ * Returns price updates in format that is accepted by various credit facade methods (multicall, close/liquidate, etc...)
23069
+ * @param acc
23087
23070
  * @returns
23088
23071
  */
23089
- async findOneTokenPath({
23090
- creditAccount: ca,
23091
- creditManager: cm,
23092
- tokenIn,
23093
- tokenOut,
23094
- amount,
23095
- slippage
23096
- }) {
23097
- const connectors = this.getAvailableConnectors(cm.collateralTokens);
23098
- const { result } = await this.contract.simulate.findOneTokenPath(
23099
- [
23100
- tokenIn,
23101
- amount,
23102
- tokenOut,
23103
- ca.creditAccount,
23104
- connectors,
23105
- BigInt(slippage)
23106
- ],
23107
- {
23108
- gas: GAS_PER_BLOCK
23072
+ async getPriceUpdatesForFacade(acc) {
23073
+ const cm = this.sdk.marketRegister.findCreditManager(acc.creditManager);
23074
+ const updates = await this.getOnDemandPriceUpdates(acc);
23075
+ return cm.creditFacade.encodeOnDemandPriceUpdates(updates);
23076
+ }
23077
+ #prepareDisableQuotas(ca) {
23078
+ const calls = [];
23079
+ for (const { token, quota } of ca.tokens) {
23080
+ if (quota > 0n) {
23081
+ calls.push({
23082
+ target: ca.creditFacade,
23083
+ callData: viem.encodeFunctionData({
23084
+ abi: iCreditFacadeV3MulticallAbi,
23085
+ functionName: "updateQuota",
23086
+ args: [token, MIN_INT96, 0n]
23087
+ })
23088
+ });
23109
23089
  }
23110
- );
23090
+ }
23091
+ return calls;
23092
+ }
23093
+ #prepareUpdateQuotas(props) {
23094
+ const { creditAccount, averageQuota, minQuota } = props;
23095
+ const minRecord = assetsMap(minQuota);
23096
+ const calls = averageQuota.map((q) => {
23097
+ const minAsset = minRecord.get(q.token);
23098
+ const min = minAsset && minAsset?.balance > 0 ? minAsset.balance : 0n;
23099
+ return {
23100
+ target: creditAccount.creditFacade,
23101
+ callData: viem.encodeFunctionData({
23102
+ abi: iCreditFacadeV3MulticallAbi,
23103
+ functionName: "updateQuota",
23104
+ args: [q.token, q.balance, min]
23105
+ })
23106
+ };
23107
+ });
23108
+ return calls;
23109
+ }
23110
+ #prepareDecreaseDebt(ca) {
23111
+ if (ca.debt > 0n) {
23112
+ return [
23113
+ {
23114
+ target: ca.creditFacade,
23115
+ callData: viem.encodeFunctionData({
23116
+ abi: iCreditFacadeV3MulticallAbi,
23117
+ functionName: "decreaseDebt",
23118
+ args: [MAX_UINT256]
23119
+ })
23120
+ }
23121
+ ];
23122
+ }
23123
+ return [];
23124
+ }
23125
+ #prepareDisableTokens(ca) {
23126
+ const calls = [];
23127
+ for (const t of ca.tokens) {
23128
+ if (t.token !== ca.underlying && (t.mask & ca.enabledTokensMask) !== 0n && t.quota === 0n) {
23129
+ calls.push({
23130
+ target: ca.creditFacade,
23131
+ callData: viem.encodeFunctionData({
23132
+ abi: iCreditFacadeV3MulticallAbi,
23133
+ functionName: "disableToken",
23134
+ args: [t.token]
23135
+ })
23136
+ });
23137
+ }
23138
+ }
23139
+ return calls;
23140
+ }
23141
+ #prepareEnableTokens(ca, tokens) {
23142
+ return tokens.map((t) => ({
23143
+ target: ca.creditFacade,
23144
+ callData: viem.encodeFunctionData({
23145
+ abi: iCreditFacadeV3MulticallAbi,
23146
+ functionName: "enableToken",
23147
+ args: [t]
23148
+ })
23149
+ }));
23150
+ }
23151
+ #prepareWithdrawToken(ca, token, amount, to) {
23111
23152
  return {
23112
- amount: result.amount,
23113
- minAmount: result.minAmount,
23114
- calls: [...result.calls]
23153
+ target: ca.creditFacade,
23154
+ callData: viem.encodeFunctionData({
23155
+ abi: iCreditFacadeV3MulticallAbi,
23156
+ functionName: "withdrawCollateral",
23157
+ args: [token, amount, to]
23158
+ })
23115
23159
  };
23116
23160
  }
23161
+ #prepareAddCollateral(assets, ca, permits) {
23162
+ const calls = assets.map(({ token, balance }) => {
23163
+ const p = permits[token];
23164
+ if (p) {
23165
+ return {
23166
+ target: ca.creditFacade,
23167
+ callData: viem.encodeFunctionData({
23168
+ abi: iCreditFacadeV3MulticallAbi,
23169
+ functionName: "addCollateralWithPermit",
23170
+ args: [token, balance, p.deadline, p.v, p.r, p.s]
23171
+ })
23172
+ };
23173
+ }
23174
+ return {
23175
+ target: ca.creditFacade,
23176
+ callData: viem.encodeFunctionData({
23177
+ abi: iCreditFacadeV3MulticallAbi,
23178
+ functionName: "addCollateral",
23179
+ args: [token, balance]
23180
+ })
23181
+ };
23182
+ });
23183
+ return calls;
23184
+ }
23117
23185
  /**
23118
- * @dev Finds the best path for opening Credit Account and converting all NORMAL tokens and LP token in the way to TARGET
23119
- * @param creditManager CreditManagerData which represents credit manager you want to use to open Credit Account
23120
- * @param expectedBalances Expected balances which would be on account accounting also debt. For example,
23121
- * if you open an USDC Credit Account, borrow 50_000 USDC and provide 10 WETH and 10_USDC as collateral
23122
- * from your own funds, expectedBalances should be: { "USDC": 60_000 * (10**6), "<address of WETH>": WAD.mul(10) }
23123
- * @param leftoverBalances Balances to keep on account after opening
23124
- * @param target Address of symbol of desired token
23125
- * @param slippage Slippage in PERCENTAGE_FORMAT (100% = 10_000) per operation
23126
- * @returns PathFinderOpenStrategyResult which
23186
+ * Returns addresses of pools of attached markets
23127
23187
  */
23128
- async findOpenStrategyPath({
23129
- creditManager: cm,
23130
- expectedBalances,
23131
- leftoverBalances,
23132
- target,
23133
- slippage
23134
- }) {
23135
- const [expectedMap, leftoverMap] = [
23136
- balancesMap(expectedBalances),
23137
- balancesMap(leftoverBalances)
23138
- ];
23139
- const input = cm.collateralTokens.map((token) => ({
23140
- token,
23141
- balance: expectedMap.get(token) ?? 0n
23142
- }));
23143
- const leftover = cm.collateralTokens.map((token) => ({
23144
- token,
23145
- balance: leftoverMap.get(token) ?? 0n
23146
- }));
23147
- const connectors = this.getAvailableConnectors(cm.collateralTokens);
23148
- const {
23149
- result: [outBalances, result]
23150
- } = await this.contract.simulate.findOpenStrategyPath(
23151
- [cm.address, input, leftover, target, connectors, BigInt(slippage)],
23152
- {
23153
- gas: GAS_PER_BLOCK
23188
+ get pools() {
23189
+ return this.sdk.marketRegister.pools.map((p) => p.pool.address);
23190
+ }
23191
+ };
23192
+ var AddressProviderContractV3_1 = class extends BaseContract {
23193
+ #addresses = {};
23194
+ versions = {};
23195
+ latest = {};
23196
+ constructor(sdk, address) {
23197
+ super(sdk, {
23198
+ addr: address,
23199
+ name: "AddressProviderV3",
23200
+ abi: iAddressProviderV3_1Abi
23201
+ });
23202
+ }
23203
+ parseFunctionParams(params) {
23204
+ switch (params.functionName) {
23205
+ case "setAddress": {
23206
+ if (params.args.length !== 3) {
23207
+ const [key2, saveVersion2] = params.args;
23208
+ return [key2, `${saveVersion2}`];
23209
+ }
23210
+ const [key, value, saveVersion] = params.args;
23211
+ return [viem.bytesToString(viem.toBytes(key)), value, `${saveVersion}`];
23154
23212
  }
23213
+ default:
23214
+ return void 0;
23215
+ }
23216
+ }
23217
+ setInternalAddress(key, address, version) {
23218
+ if (!this.#addresses[key]) {
23219
+ this.#addresses[key] = {};
23220
+ }
23221
+ this.#addresses[key][version] = address;
23222
+ if (!this.latest[key] || version > this.latest[key]) {
23223
+ this.latest[key] = version;
23224
+ }
23225
+ if (!this.versions[key]) {
23226
+ this.versions[key] = /* @__PURE__ */ new Set();
23227
+ }
23228
+ this.versions[key].add(version);
23229
+ }
23230
+ getAddress(contract, version = NO_VERSION) {
23231
+ if (!this.#addresses[contract]) {
23232
+ throw new Error(`Address ${contract}, version: ${version} not found`);
23233
+ }
23234
+ const result = this.#addresses[contract][version];
23235
+ if (!result) {
23236
+ throw new Error(`Address ${contract}, version: ${version} not found`);
23237
+ }
23238
+ return result;
23239
+ }
23240
+ getLatestVersion(contract) {
23241
+ if (!this.latest[contract]) {
23242
+ throw new Error(`Latest version for ${contract} not found`);
23243
+ }
23244
+ this.logger?.debug(
23245
+ `Latest version found for ${contract} : ${this.latest[contract]}`
23155
23246
  );
23156
- const balancesAfter = Object.fromEntries(
23157
- outBalances.map((b) => [b.token.toLowerCase(), b.balance])
23158
- );
23247
+ return this.getAddress(contract, this.latest[contract]);
23248
+ }
23249
+ async syncState(blockNumber) {
23250
+ const entries = await this.contract.read.getAllSavedContracts({
23251
+ blockNumber
23252
+ });
23253
+ for (const log of entries) {
23254
+ this.setInternalAddress(log.key, log.value, Number(log.version));
23255
+ }
23256
+ }
23257
+ stateHuman(raw = true) {
23159
23258
  return {
23160
- balances: {
23161
- ...balancesAfter,
23162
- [target]: (expectedMap.get(target) ?? 0n) + result.amount
23163
- },
23164
- minBalances: {
23165
- ...balancesAfter,
23166
- [target]: (expectedMap.get(target) ?? 0n) + result.minAmount
23167
- },
23168
- amount: result.amount,
23169
- minAmount: result.minAmount,
23170
- calls: [...result.calls]
23259
+ ...super.stateHuman(raw),
23260
+ addresses: Object.entries(this.#addresses).map(([key, value]) => {
23261
+ return Object.entries(value).map(([version, address]) => {
23262
+ return {
23263
+ key,
23264
+ version,
23265
+ address: this.sdk.provider.addressLabels.get(address)
23266
+ };
23267
+ });
23268
+ }).reduce(
23269
+ (acc, vals) => {
23270
+ for (const val of vals) {
23271
+ if (!acc[val.key]) {
23272
+ acc[val.key] = {};
23273
+ }
23274
+ acc[val.key][val.version] = val.address;
23275
+ }
23276
+ return acc;
23277
+ },
23278
+ {}
23279
+ )
23171
23280
  };
23172
23281
  }
23173
- /**
23174
- * @dev Finds the path to swap / withdraw all assets from CreditAccount into underlying asset
23175
- * Can bu used for closing Credit Account and for liquidations as well.
23176
- * @param creditAccount CreditAccountStruct object used for close path computation
23177
- * @param creditManager CreditManagerSlice for corresponding credit manager
23178
- * @param slippage Slippage in PERCENTAGE_FORMAT (100% = 10_000) per operation
23179
- * @return The best option in PathFinderCloseResult format, which
23180
- * - underlyingBalance - total balance of underlying token
23181
- * - calls - list of calls which should be done to swap & unwrap everything to underlying token
23182
- */
23183
- async findBestClosePath({
23184
- creditAccount: ca,
23185
- creditManager: cm,
23186
- slippage,
23187
- balances
23188
- }) {
23189
- const { pathOptions, expected, leftover, connectors } = this.getFindClosePathInput(
23190
- ca,
23191
- cm,
23192
- balances ? {
23193
- expectedBalances: assetsMap(balances.expectedBalances),
23194
- leftoverBalances: assetsMap(balances.leftoverBalances)
23195
- } : void 0
23196
- );
23197
- await this.#hooks.triggerHooks("foundPathOptions", {
23198
- creditAccount: ca.creditAccount,
23199
- pathOptions,
23200
- expected,
23201
- leftover,
23202
- connectors
23282
+ processLog(log) {
23283
+ switch (log.eventName) {
23284
+ case "SetAddress": {
23285
+ const parsedLog2 = viem.parseEventLogs({
23286
+ abi: this.abi,
23287
+ eventName: "SetAddress",
23288
+ logs: [log]
23289
+ })[0];
23290
+ const key = parsedLog2.args.key;
23291
+ this.setInternalAddress(
23292
+ key,
23293
+ log.args.value,
23294
+ Number(parsedLog2.args.version)
23295
+ );
23296
+ break;
23297
+ }
23298
+ default:
23299
+ this.logger?.warn(`Unknown event: ${log.eventName}`);
23300
+ break;
23301
+ }
23302
+ }
23303
+ };
23304
+
23305
+ // src/sdk/core/BotListV3Contract.ts
23306
+ var BotListContract = class extends BaseContract {
23307
+ #approvedCreditManagers;
23308
+ #currentBlock;
23309
+ constructor(sdk, address) {
23310
+ super(sdk, { addr: address, name: "BotListV3", abi: botListV3Abi });
23311
+ this.#currentBlock = ADDRESS_PROVIDER_BLOCK[sdk.provider.networkType];
23312
+ }
23313
+ parseFunctionParams(params) {
23314
+ switch (params.functionName) {
23315
+ case "setCreditManagerApprovedStatus": {
23316
+ const [creditManager, status] = params.args;
23317
+ return [this.addressLabels.get(creditManager), `${status}`];
23318
+ }
23319
+ case "setBotSpecialPermissions": {
23320
+ const [bot, creditManager, permissions] = params.args;
23321
+ return [
23322
+ this.addressLabels.get(bot),
23323
+ this.addressLabels.get(creditManager),
23324
+ botPermissionsToString(permissions)
23325
+ ];
23326
+ }
23327
+ default:
23328
+ return void 0;
23329
+ }
23330
+ }
23331
+ async syncState(toBlock) {
23332
+ const logs = await this.provider.publicClient.getContractEvents({
23333
+ address: this.address,
23334
+ abi: this.abi,
23335
+ fromBlock: this.#currentBlock,
23336
+ toBlock
23203
23337
  });
23204
- let results = [];
23205
- for (const po of pathOptions) {
23206
- const { result: result2 } = await this.contract.simulate.findBestClosePath(
23207
- [
23208
- ca.creditAccount,
23209
- expected,
23210
- leftover,
23211
- connectors,
23212
- BigInt(slippage),
23213
- po,
23214
- BigInt(LOOPS_PER_TX),
23215
- false
23216
- ],
23217
- {
23218
- gas: GAS_PER_BLOCK
23338
+ logs.forEach((e) => this.processLog(e));
23339
+ this.#currentBlock = toBlock;
23340
+ }
23341
+ processLog(log) {
23342
+ switch (log.eventName) {
23343
+ case "SetCreditManagerApprovedStatus":
23344
+ if (log.args.approved) {
23345
+ this.approvedCreditManagers.add(log.args.creditManager);
23346
+ } else {
23347
+ this.approvedCreditManagers.delete(log.args.creditManager);
23219
23348
  }
23349
+ break;
23350
+ case "SetBotSpecialPermissions":
23351
+ this.logger?.debug(
23352
+ `Bot ${log.args.bot} has been given permissions ${botPermissionsToString(
23353
+ log.args.permissions
23354
+ )} for credit manager ${log.args.creditManager}`
23355
+ );
23356
+ break;
23357
+ default:
23358
+ this.logger?.warn(`Unknown event: ${log.eventName}`);
23359
+ break;
23360
+ }
23361
+ }
23362
+ get approvedCreditManagers() {
23363
+ if (!this.#approvedCreditManagers) {
23364
+ throw new Error(
23365
+ "BotListContract state needs to be synced to load approvedCreditManagers"
23220
23366
  );
23221
- results.push({
23222
- ...result2,
23223
- calls: [...result2.calls]
23224
- });
23225
23367
  }
23226
- const bestResult = results.reduce(compareRouterResults, {
23227
- amount: 0n,
23228
- minAmount: 0n,
23229
- calls: []
23230
- });
23231
- const underlyingBalance = ca.tokens.find((t) => t.token === ca.underlying)?.balance ?? 0n;
23232
- const result = {
23233
- amount: bestResult.amount,
23234
- minAmount: bestResult.minAmount,
23235
- calls: bestResult.calls.map((c) => ({
23236
- callData: c.callData,
23237
- target: c.target
23238
- })),
23239
- underlyingBalance: underlyingBalance + bestResult.minAmount
23240
- };
23241
- return result;
23368
+ return this.#approvedCreditManagers;
23242
23369
  }
23243
- /**
23244
- * Finds input to be used with findBestClosePath
23245
- * @param ca
23246
- * @param cm
23247
- * @returns
23248
- */
23249
- getFindClosePathInput(ca, cm, balances) {
23250
- const b = balances || this.getDefaultExpectedAndLeftover(ca);
23251
- const { leftoverBalances, expectedBalances } = b;
23252
- const pathOptions = PathOptionFactory.generatePathOptions(
23253
- ca.tokens,
23254
- this.provider.networkType,
23255
- LOOPS_PER_TX
23256
- );
23257
- const expected = cm.collateralTokens.map((token) => {
23258
- const actual = expectedBalances.get(token)?.balance || 0n;
23259
- return {
23260
- token,
23261
- balance: actual > 10n ? actual : 0n
23262
- };
23263
- });
23264
- const leftover = cm.collateralTokens.map((token) => ({
23265
- token,
23266
- balance: leftoverBalances.get(token)?.balance || 1n
23267
- }));
23268
- const connectors = this.getAvailableConnectors(cm.collateralTokens);
23269
- return { expected, leftover, connectors, pathOptions };
23370
+ stateHuman(raw = true) {
23371
+ return super.stateHuman(raw);
23270
23372
  }
23271
- getDefaultExpectedAndLeftover(ca) {
23272
- const expectedBalances = new AddressMap();
23273
- const leftoverBalances = new AddressMap();
23274
- for (const { token: t, balance, mask } of ca.tokens) {
23275
- const token = t;
23276
- const isEnabled = (mask & ca.enabledTokensMask) !== 0n;
23277
- expectedBalances.upsert(token, { token, balance });
23278
- const decimals = this.sdk.tokensMeta.decimals(token);
23279
- const minBalance = 10n ** BigInt(Math.max(8, decimals) - 8);
23280
- if (balance < minBalance || !isEnabled) {
23281
- leftoverBalances.upsert(token, { token, balance });
23373
+ };
23374
+
23375
+ // src/sdk/core/GearStakingV3Contract.ts
23376
+ var GearStakingContract = class extends BaseContract {
23377
+ constructor(sdk, address) {
23378
+ super(sdk, { addr: address, name: "GearStakingV3", abi: gearStakingV3Abi });
23379
+ }
23380
+ parseFunctionParams(params) {
23381
+ switch (params.functionName) {
23382
+ case "setVotingContractStatus": {
23383
+ const [address, status] = params.args;
23384
+ return [this.addressLabels.get(address), VotingContractStatus[status]];
23282
23385
  }
23386
+ default:
23387
+ return void 0;
23283
23388
  }
23284
- return { expectedBalances, leftoverBalances };
23285
23389
  }
23286
- getAvailableConnectors(collateralTokens) {
23287
- return collateralTokens.filter(
23288
- (t) => this.#connectors.includes(t.toLowerCase())
23289
- );
23390
+ stateHuman(raw = true) {
23391
+ return {
23392
+ ...super.stateHuman(raw),
23393
+ successor: this.labelAddress(ADDRESS_0X0),
23394
+ migrator: this.labelAddress(ADDRESS_0X0)
23395
+ };
23290
23396
  }
23291
23397
  };
23292
- function compareRouterResults(a, b) {
23293
- return a.amount > b.amount ? a : b;
23294
- }
23295
- function balancesMap(assets) {
23296
- return new AddressMap(assets.map(({ token, balance }) => [token, balance]));
23297
- }
23298
- function assetsMap(assets) {
23299
- return new AddressMap(assets.map((a) => [a.token, a]));
23300
- }
23301
-
23302
- // src/sdk/GearboxSDK.ts
23303
23398
  var GearboxSDK = class _GearboxSDK {
23304
23399
  #hooks = new Hooks();
23305
23400
  // Represents chain object
@@ -23698,6 +23793,8 @@ exports.WstETHV1AdapterContract = WstETHV1AdapterContract;
23698
23793
  exports.YearnPriceFeedContract = YearnPriceFeedContract;
23699
23794
  exports.YearnV2RouterAdapterContract = YearnV2RouterAdapterContract;
23700
23795
  exports.ZeroPriceFeedContract = ZeroPriceFeedContract;
23796
+ exports.assetsMap = assetsMap;
23797
+ exports.balancesMap = balancesMap;
23701
23798
  exports.botPermissionsToString = botPermissionsToString;
23702
23799
  exports.bytes32ToString = bytes32ToString;
23703
23800
  exports.chains = chains;