Skip to content

Commit

Permalink
feat: prepare for batch version
Browse files Browse the repository at this point in the history
  • Loading branch information
doomsower committed Jul 11, 2024
1 parent f7db320 commit 482c6f4
Show file tree
Hide file tree
Showing 11 changed files with 476 additions and 480 deletions.
118 changes: 0 additions & 118 deletions src/services/liquidate/AbstractLiquidationStrategyV3.ts

This file was deleted.

229 changes: 229 additions & 0 deletions src/services/liquidate/AbstractLiquidator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import { tokenSymbolByAddress } from "@gearbox-protocol/sdk-gov";
import { iDataCompressorV3Abi, ierc20Abi } from "@gearbox-protocol/types/abi";
import type { OptimisticResultV2 } from "@gearbox-protocol/types/optimist";
import type { Address, TransactionReceipt } from "viem";
import { getContract } from "viem";

import type { Config } from "../../config/index.js";
import { CreditAccountData, CreditManagerData } from "../../data/index.js";
import { DI } from "../../di.js";
import { ErrorHandler } from "../../errors/index.js";
import type { ILogger } from "../../log/index.js";
import { Logger } from "../../log/index.js";
import { PathFinder } from "../../utils/ethers-6-temp/pathfinder/index.js";
import { TxParserHelper } from "../../utils/ethers-6-temp/txparser/index.js";
import type { IDataCompressorContract } from "../../utils/index.js";
import type { AddressProviderService } from "../AddressProviderService.js";
import type Client from "../Client.js";
import type { INotifier } from "../notifier/index.js";
import type OracleServiceV3 from "../OracleServiceV3.js";
import type { IOptimisticOutputWriter } from "../output/index.js";
import type { RedstoneServiceV3 } from "../RedstoneServiceV3.js";
import type { ISwapper } from "../swap/index.js";
import type { OptimisticResults } from "./OptimisiticResults.js";
import type { StrategyPreview } from "./types.js";

export default abstract class AbstractLiquidator {
@Logger("Liquidator")
logger!: ILogger;

@DI.Inject(DI.Redstone)
redstone!: RedstoneServiceV3;

@DI.Inject(DI.Notifier)
notifier!: INotifier;

@DI.Inject(DI.Config)
config!: Config;

@DI.Inject(DI.AddressProvider)
addressProvider!: AddressProviderService;

@DI.Inject(DI.Oracle)
oracle!: OracleServiceV3;

@DI.Inject(DI.Output)
outputWriter!: IOptimisticOutputWriter;

@DI.Inject(DI.Swapper)
swapper!: ISwapper;

@DI.Inject(DI.OptimisticResults)
optimistic!: OptimisticResults;

@DI.Inject(DI.Client)
client!: Client;

skipList = new Set<Address>();

#errorHandler?: ErrorHandler;
#compressor?: IDataCompressorContract;
#pathFinder?: PathFinder;
#cmCache: Record<string, CreditManagerData> = {};

public async launch(): Promise<void> {
this.#errorHandler = new ErrorHandler(this.config, this.logger);
const [pfAddr, dcAddr] = await Promise.all([
this.addressProvider.findService("ROUTER", 300),
this.addressProvider.findService("DATA_COMPRESSOR", 300),
]);
this.#compressor = getContract({
abi: iDataCompressorV3Abi,
address: dcAddr,
client: this.client.pub,
});
this.#pathFinder = new PathFinder(
pfAddr,
this.client.pub,
this.config.network,
);
}

protected newOptimisticResult(acc: CreditAccountData): OptimisticResultV2 {
return {
version: "2",
creditManager: acc.creditManager,
borrower: acc.borrower,
account: acc.addr,
balancesBefore: acc.filterDust(),
hfBefore: acc.healthFactor,
balancesAfter: {},
hfAfter: 0,
gasUsed: 0,
calls: [],
callsHuman: [],
isError: true,
pathAmount: "0",
liquidatorPremium: "0",
liquidatorProfit: "0",
};
}

protected updateAfterPreview(
result: OptimisticResultV2,
preview: StrategyPreview,
): OptimisticResultV2 {
return {
...result,
assetOut: preview.assetOut,
amountOut: preview.amountOut,
flashLoanAmount: preview.flashLoanAmount,
calls: preview.calls,
pathAmount: preview.underlyingBalance.toString(),
priceUpdates: preview.priceUpdates,
callsHuman: TxParserHelper.parseMultiCall(preview),
};
}

protected async updateAfterLiquidation(
result: OptimisticResultV2,
acc: CreditAccountData,
underlyingBalanceBefore: bigint,
receipt: TransactionReceipt,
): Promise<OptimisticResultV2> {
const ca = await this.updateCreditAccountData(acc);
result.balancesAfter = ca.filterDust();
result.hfAfter = ca.healthFactor;

const balanceAfter = await this.getExecutorBalance(ca.underlyingToken);
result.gasUsed = Number(receipt.gasUsed);
result.liquidatorPremium = (
balanceAfter.underlying - underlyingBalanceBefore
).toString(10);
return result;
}

protected async getCreditManagerData(
addr: Address,
): Promise<CreditManagerData> {
let cm: CreditManagerData | undefined;
if (this.config.optimistic) {
cm = this.#cmCache[addr.toLowerCase()];
}
if (!cm) {
cm = new CreditManagerData(
await this.compressor.read.getCreditManagerData([addr]),
);
if (this.config.optimistic) {
this.#cmCache[addr.toLowerCase()] = cm;
}
}
// TODO: TxParser is really old and weird class, until we refactor it it's the best place to have this
TxParserHelper.addCreditManager(cm);
return cm;
}

protected async getCreditManagersV3List(): Promise<CreditManagerData[]> {
const raw = await this.compressor.read.getCreditManagersV3List();
const result = raw.map(d => new CreditManagerData(d));

if (this.config.optimistic) {
for (const cm of result) {
this.#cmCache[cm.address.toLowerCase()] = cm;
}
}

return result;
}

/**
* Fetches credit account data again for optimistic report
* @param ca
* @returns
*/
protected async updateCreditAccountData(
ca: CreditAccountData,
): Promise<CreditAccountData> {
if (!this.config.optimistic) {
throw new Error(
"updateCreditAccountData should only be used in optimistic mode",
);
}
const priceUpdates = await this.redstone.dataCompressorUpdates(ca);
const { result } = await this.compressor.simulate.getCreditAccountData([
ca.addr,
priceUpdates,
]);
return new CreditAccountData(result);
}

protected async getExecutorBalance(
underlyingToken: Address,
): Promise<{ eth: bigint; underlying: bigint }> {
// using promise.all here sometimes results in anvil being stuck
const isWeth = tokenSymbolByAddress[underlyingToken] === "WETH";
const eth = await this.client.pub.getBalance({
address: this.client.address,
});
const underlying = isWeth
? eth
: await this.client.pub.readContract({
address: underlyingToken,
abi: ierc20Abi,
functionName: "balanceOf",
args: [this.client.address],
});
return { eth, underlying };
}

protected get errorHandler(): ErrorHandler {
if (!this.#errorHandler) {
throw new Error("liquidator not launched");
}
return this.#errorHandler;
}

protected get compressor(): IDataCompressorContract {
if (!this.#compressor) {
throw new Error("liquidator not launched");
}
return this.#compressor;
}

protected get pathFinder(): PathFinder {
if (!this.#pathFinder) {
throw new Error("liquidator not launched");
}
return this.#pathFinder;
}
}
Loading

0 comments on commit 482c6f4

Please sign in to comment.