Skip to content

Commit

Permalink
feat: batch liquidator
Browse files Browse the repository at this point in the history
BREAKING CHANGE: this affects existing liquidation modes a lot
  • Loading branch information
doomsower committed Aug 13, 2024
1 parent bd57f96 commit dfa6069
Show file tree
Hide file tree
Showing 29 changed files with 1,594 additions and 1,070 deletions.
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@
"pino-pretty": "^11.2.2"
},
"devDependencies": {
"@aws-sdk/client-s3": "^3.624.0",
"@aws-sdk/client-s3": "^3.629.0",
"@commitlint/cli": "^19.4.0",
"@commitlint/config-conventional": "^19.2.2",
"@flashbots/ethers-provider-bundle": "^1.0.0",
"@gearbox-protocol/eslint-config": "2.0.0-next.2",
"@gearbox-protocol/liquidator-v2-contracts": "^2.1.0-next.17",
"@gearbox-protocol/liquidator-v2-contracts": "^2.1.0",
"@gearbox-protocol/prettier-config": "2.0.0-next.0",
"@gearbox-protocol/sdk-gov": "^2.14.1",
"@gearbox-protocol/sdk-gov": "^2.15.0",
"@gearbox-protocol/types": "^1.12.1",
"@redstone-finance/evm-connector": "^0.6.1",
"@types/node": "^22.1.0",
"@types/node": "^22.2.0",
"@uniswap/sdk-core": "^5.3.1",
"@uniswap/v3-sdk": "^3.13.1",
"@vlad-yakovlev/telegram-md": "^2.0.0",
Expand All @@ -46,15 +46,15 @@
"eslint": "^8.57.0",
"ethers": "^6.13.2",
"husky": "^9.1.4",
"lint-staged": "^15.2.8",
"lint-staged": "^15.2.9",
"nanoid": "^5.0.7",
"node-pty": "^1.0.0",
"pino": "^9.3.2",
"prettier": "^3.3.3",
"redstone-protocol": "^1.0.5",
"tsx": "^4.16.5",
"tsx": "^4.17.0",
"typescript": "^5.5.4",
"viem": "^2.19.1",
"viem": "^2.19.4",
"vitest": "^2.0.5"
},
"prettier": "@gearbox-protocol/prettier-config",
Expand Down
6 changes: 3 additions & 3 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type Client from "./services/Client.js";
import type HealthCheckerService from "./services/HealthCheckerService.js";
import type { IOptimisticOutputWriter } from "./services/output/index.js";
import type { RedstoneServiceV3 } from "./services/RedstoneServiceV3.js";
import type { ScanServiceV3 } from "./services/scan/index.js";
import type { Scanner } from "./services/scanner/index.js";
import type { ISwapper } from "./services/swap/index.js";
import version from "./version.js";

Expand All @@ -22,7 +22,7 @@ class App {
addressProvider!: AddressProviderService;

@DI.Inject(DI.Scanner)
scanServiceV3!: ScanServiceV3;
scanner!: Scanner;

@DI.Inject(DI.HealthChecker)
healthChecker!: HealthCheckerService;
Expand Down Expand Up @@ -56,7 +56,7 @@ class App {

this.healthChecker.launch();
await this.swapper.launch(this.config.network);
await this.scanServiceV3.launch();
await this.scanner.launch();

if (this.config.optimistic) {
this.log.debug("optimistic liquidation finished, writing output");
Expand Down
3 changes: 3 additions & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import type { ConfigSchema } from "./schema.js";
const envConfigMapping: Record<keyof ConfigSchema, string | string[]> = {
addressProviderOverride: "ADDRESS_PROVIDER",
appName: "APP_NAME",
batchLiquidatorAddress: "BATCH_LIQUIDATOR_ADDRESS",
debugAccounts: "DEBUG_ACCOUNTS",
debugManagers: "DEBUG_MANAGERS",
batchSize: "BATCH_SIZE",
castBin: "CAST_BIN",
deployPartialLiquidatorContracts: "DEPLOY_PARTIAL_LIQUIDATOR",
deployBatchLiquidatorContracts: "DEPLOY_BATCH_LIQUIDATOR",
ethProviderRpcs: ["JSON_RPC_PROVIDERS", "JSON_RPC_PROVIDER"],
hfThreshold: "HF_TRESHOLD",
restakingWorkaround: "RESTAKING_WORKAROUND",
Expand Down
3 changes: 3 additions & 0 deletions src/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export const ConfigSchema = z.object({
optimistic: booleanLike.pipe(z.boolean().optional()),
deployPartialLiquidatorContracts: booleanLike.pipe(z.boolean().optional()),
partialLiquidatorAddress: Address.optional(),
deployBatchLiquidatorContracts: booleanLike.pipe(z.boolean().optional()),
batchSize: z.coerce.number().nonnegative().default(10),
batchLiquidatorAddress: Address.optional(),
slippage: z.coerce.number().min(0).max(10000).int().default(50),
restakingWorkaround: booleanLike.pipe(z.boolean().optional()),

Expand Down
92 changes: 57 additions & 35 deletions src/errors/ErrorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,15 @@ export class ErrorHandler {
};
}

public async saveTransactionTrace(hash: string): Promise<string | undefined> {
return this.#runCast([
"run",
"--rpc-url",
this.config.ethProviderRpcs[0],
hash,
]);
}

/**
* Safely tries to save trace of failed transaction to configured output
* @param error
Expand All @@ -75,52 +84,65 @@ export class ErrorHandler {
e: BaseError,
context?: CreditAccountData,
): Promise<string | undefined> {
const logger = this.#caLogger(context);
if (!this.config.castBin || !this.config.outDir) {
return undefined;
}
try {
let cast: string[] = [];
if (e instanceof TransactionRevertedError) {
let cast: string[] = [];
if (e instanceof TransactionRevertedError) {
cast = [
"run",
"--rpc-url",
this.config.ethProviderRpcs[0],
e.receipt.transactionHash,
];
} else {
const exErr = e.walk(
err => err instanceof ContractFunctionExecutionError,
);
if (
exErr instanceof ContractFunctionExecutionError &&
exErr.contractAddress
) {
const data = encodeFunctionData({
abi: exErr.abi,
args: exErr.args,
functionName: exErr.functionName,
});
cast = [
"run",
"call",
"--trace",
"--rpc-url",
this.config.ethProviderRpcs[0],
e.receipt.transactionHash,
exErr.contractAddress,
data,
];
} else {
const exErr = e.walk(
err => err instanceof ContractFunctionExecutionError,
);
if (
exErr instanceof ContractFunctionExecutionError &&
exErr.contractAddress
) {
const data = encodeFunctionData({
abi: exErr.abi,
args: exErr.args,
functionName: exErr.functionName,
});
cast = [
"call",
"--trace",
"--rpc-url",
this.config.ethProviderRpcs[0],
exErr.contractAddress,
data,
];
}
}
if (!cast.length) {
return undefined;
}
}
if (!cast.length) {
return undefined;
}
return this.#runCast(cast, context);
}

/**
* Runs cast cli command and saves output to a unique file
* @param args
* @param context
* @returns
*/
async #runCast(
args: string[],
context?: CreditAccountData,
): Promise<string | undefined> {
if (!this.config.castBin || !this.config.outDir) {
return undefined;
}

const logger = this.#caLogger(context);
try {
const traceId = `${nanoid()}.trace`;
const traceFile = path.resolve(this.config.outDir, traceId);
const out = createWriteStream(traceFile, "utf-8");
await events.once(out, "open");
// use node-pty instead of node:child_process to have colored output
const pty = spawn(this.config.castBin, cast, { cols: 1024 });
const pty = spawn(this.config.castBin, args, { cols: 1024 });
pty.onData(data => out.write(data));
await new Promise(resolve => {
pty.onExit(() => resolve(undefined));
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "./services/Client.js";
import "./services/HealthCheckerService.js";
import "./services/OracleServiceV3.js";
import "./services/RedstoneServiceV3.js";
import "./services/scan/index.js";
import "./services/scanner/index.js";
import "./services/liquidate/index.js";
import "./services/output/index.js";
import "./services/notifier/index.js";
Expand Down
8 changes: 1 addition & 7 deletions src/services/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import { privateKeyToAccount } from "viem/accounts";
import { arbitrum, base, mainnet, optimism } from "viem/chains";

import type { Config } from "../config/index.js";
import type { CreditAccountData } from "../data/index.js";
import { exceptionsAbis } from "../data/index.js";
import { DI } from "../di.js";
import { TransactionRevertedError } from "../errors/TransactionRevertedError.js";
Expand Down Expand Up @@ -167,14 +166,9 @@ export default class Client {
}

public async liquidate(
ca: CreditAccountData,
request: SimulateContractReturnType["request"],
logger: ILogger,
): Promise<TransactionReceipt> {
const logger = this.logger.child({
account: ca.addr,
borrower: ca.borrower,
manager: ca.managerName,
});
logger.debug("sending liquidation tx");
const { abi, address, args, dataSuffix, functionName, ...rest } = request;
const data = encodeFunctionData({
Expand Down
8 changes: 4 additions & 4 deletions src/services/HealthCheckerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { DI } from "../di.js";
import type { ILogger } from "../log/index.js";
import { Logger } from "../log/index.js";
import version from "../version.js";
import type { ScanServiceV3 } from "./scan/index.js";
import type { Scanner } from "./scanner/index.js";

const nanoid = customAlphabet("1234567890abcdef", 8);

Expand All @@ -17,7 +17,7 @@ export default class HealthCheckerService {
log!: ILogger;

@DI.Inject(DI.Scanner)
scanServiceV3!: ScanServiceV3;
scanner!: Scanner;

@DI.Inject(DI.Config)
config!: Config;
Expand All @@ -40,7 +40,7 @@ export default class HealthCheckerService {
res.end(
JSON.stringify({
start_time: this.#start,
block_number: this.scanServiceV3.lastUpdated,
block_number: this.scanner.lastUpdated,
version,
}),
);
Expand Down Expand Up @@ -97,7 +97,7 @@ start_time{${labels}} ${this.#start}
# HELP block_number Latest processed block
# TYPE block_number gauge
block_number{${labels}} ${this.scanServiceV3.lastUpdated}
block_number{${labels}} ${this.scanner.lastUpdated}
`;
}
Expand Down
Loading

0 comments on commit dfa6069

Please sign in to comment.