Skip to content

Commit 9ab4b13

Browse files
committed
fix: account for differences in signatures of partial liquidation contracts v3 and v3.1
1 parent b15a608 commit 9ab4b13

File tree

9 files changed

+328
-107
lines changed

9 files changed

+328
-107
lines changed

src/data/exceptions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ export const exceptionsAbis = [
1212
...iRedstoneErrorsAbi,
1313
...ilpPriceFeedExceptionsAbi,
1414
...iRouterV3ErrorsAbi,
15-
];
15+
] as const;

src/services/liquidate/SingularPartialLiquidator.ts

Lines changed: 28 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
import { iPartialLiquidatorAbi } from "@gearbox-protocol/liquidator-v2-contracts/abi";
21
import type { CreditAccountData } from "@gearbox-protocol/sdk";
3-
import { ADDRESS_0X0, formatBN, TypedObjectUtils } from "@gearbox-protocol/sdk";
2+
import { TypedObjectUtils } from "@gearbox-protocol/sdk";
43
import {
54
calcLiquidatableLTs,
65
createAnvilClient,
76
setLTs,
87
} from "@gearbox-protocol/sdk/dev";
98
import type { Address, SimulateContractReturnType } from "viem";
109

11-
import { exceptionsAbis } from "../../data/index.js";
1210
import {
1311
createPartialLiquidators,
12+
humanizeOptimalLiquidation,
1413
type IPartialLiquidatorContract,
1514
} from "./partial/index.js";
1615
import SingularFullLiquidator from "./SingularFullLiquidator.js";
@@ -158,69 +157,30 @@ export default class SingularPartialLiquidator extends SingularLiquidator<Partia
158157
ca,
159158
undefined,
160159
);
161-
const liquidatorAddr = this.liquidatorForCA(ca);
162-
if (!liquidatorAddr) {
160+
const liquidatorContract = this.liquidatorForCA(ca);
161+
if (!liquidatorContract) {
163162
throw new Error(
164163
`no partial liquidator contract found for account ${ca.creditAccount} in ${cm.name}`,
165164
);
166165
}
167-
const {
168-
result: [
169-
tokenOut,
170-
optimalAmount,
171-
repaidAmount,
172-
flashLoanAmount,
173-
isOptimalRepayable,
174-
],
175-
} = await this.client.pub.simulateContract({
176-
account: this.client.account,
177-
abi: [...iPartialLiquidatorAbi, ...exceptionsAbis],
178-
address: liquidatorAddr,
179-
functionName: "getOptimalLiquidation",
180-
args: [ca.creditAccount, 10100n, priceUpdates as any],
181-
});
182-
const [symb, decimals, uSymb, uDec] = [
183-
this.sdk.tokensMeta.symbol(tokenOut),
184-
this.sdk.tokensMeta.decimals(tokenOut),
185-
this.sdk.tokensMeta.symbol(cm.underlying),
186-
this.sdk.tokensMeta.decimals(cm.underlying),
187-
];
166+
const optimalLiquidation = await liquidatorContract.getOptimalLiquidation(
167+
ca.creditAccount,
168+
priceUpdates,
169+
);
188170
logger.debug(
189-
{
190-
tokenOut: `${symb} (${tokenOut})`,
191-
optimalAmount:
192-
formatBN(optimalAmount, decimals) + ` ${symb} (${optimalAmount})`,
193-
flashLoanAmount:
194-
formatBN(flashLoanAmount, uDec) + ` ${uSymb} (${flashLoanAmount})`,
195-
repaidAmount:
196-
formatBN(repaidAmount, uDec) + ` ${uSymb} (${repaidAmount})`,
197-
isOptimalRepayable,
198-
},
171+
humanizeOptimalLiquidation(cm, optimalLiquidation),
199172
"found optimal liquidation",
200173
);
201-
const connectors = this.sdk
202-
.routerFor(cm)
203-
.getAvailableConnectors(cm.creditManager.collateralTokens);
204174

205175
try {
206-
const { result: preview } = await this.client.pub.simulateContract({
207-
account: ADDRESS_0X0,
208-
address: liquidatorAddr,
209-
abi: [...iPartialLiquidatorAbi, ...exceptionsAbis],
210-
functionName: "previewPartialLiquidation",
211-
args: [
212-
ca.creditManager,
213-
ca.creditAccount,
214-
tokenOut,
215-
optimalAmount,
216-
flashLoanAmount,
217-
priceUpdates,
218-
connectors,
219-
BigInt(this.config.slippage),
220-
],
221-
});
176+
const preview = await liquidatorContract.previewPartialLiquidation(
177+
ca,
178+
cm,
179+
optimalLiquidation,
180+
priceUpdates,
181+
);
222182
if (preview.profit < 0n) {
223-
if (isOptimalRepayable) {
183+
if (optimalLiquidation.isOptimalRepayable) {
224184
throw new Error("optimal liquidation is not profitable or errored");
225185
} else {
226186
throw new Error(
@@ -229,19 +189,19 @@ export default class SingularPartialLiquidator extends SingularLiquidator<Partia
229189
}
230190
}
231191
return {
232-
assetOut: tokenOut as Address,
233-
amountOut: optimalAmount,
234-
flashLoanAmount,
192+
assetOut: optimalLiquidation.tokenOut,
193+
amountOut: optimalLiquidation.optimalAmount,
194+
flashLoanAmount: optimalLiquidation.flashLoanAmount,
235195
priceUpdates,
236196
calls: preview.calls.map(c => ({
237197
callData: c.callData,
238198
target: c.target,
239199
})),
240200
underlyingBalance: preview.profit,
241-
skipOnFailure: !isOptimalRepayable,
201+
skipOnFailure: !optimalLiquidation.isOptimalRepayable,
242202
};
243203
} catch (e) {
244-
if (!isOptimalRepayable) {
204+
if (!optimalLiquidation.isOptimalRepayable) {
245205
throw new Error(`warning: ${e}`);
246206
}
247207
throw e;
@@ -267,27 +227,13 @@ export default class SingularPartialLiquidator extends SingularLiquidator<Partia
267227
account: CreditAccountData,
268228
preview: PartialLiquidationPreview,
269229
): Promise<SimulateContractReturnType> {
270-
const liquidatorAddr = this.liquidatorForCA(account);
271-
if (!liquidatorAddr) {
230+
const liquidator = this.liquidatorForCA(account);
231+
if (!liquidator) {
272232
throw new Error(
273233
`no partial liquidator contract found for account ${account.creditAccount} in ${account.creditManager}`,
274234
);
275235
}
276-
return this.client.pub.simulateContract({
277-
account: this.client.account,
278-
address: liquidatorAddr,
279-
abi: [...iPartialLiquidatorAbi, ...exceptionsAbis],
280-
functionName: "partialLiquidateAndConvert",
281-
args: [
282-
account.creditManager,
283-
account.creditAccount,
284-
preview.assetOut,
285-
preview.amountOut,
286-
preview.flashLoanAmount,
287-
preview.priceUpdates,
288-
preview.calls,
289-
],
290-
});
236+
return liquidator.partialLiquidateAndConvert(account, preview);
291237
}
292238

293239
/**
@@ -316,8 +262,9 @@ export default class SingularPartialLiquidator extends SingularLiquidator<Partia
316262
* @param ca
317263
* @returns
318264
*/
319-
private liquidatorForCA(ca: CreditAccountData): Address | undefined {
320-
const contract = this.#liquidatorForCM[ca.creditManager];
321-
return contract?.address;
265+
private liquidatorForCA(
266+
ca: CreditAccountData,
267+
): IPartialLiquidatorContract | undefined {
268+
return this.#liquidatorForCM[ca.creditManager];
322269
}
323270
}

src/services/liquidate/partial/AbstractPartialLiquidatorContract.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1-
import { iPartialLiquidatorAbi } from "@gearbox-protocol/liquidator-v2-contracts/abi";
21
import type {
2+
CreditAccountData,
33
CreditAccountsService,
44
CreditSuite,
55
Curator,
66
GearboxSDK,
7+
OnDemandPriceUpdate,
78
} from "@gearbox-protocol/sdk";
89
import { ADDRESS_0X0 } from "@gearbox-protocol/sdk";
910
import { iDegenDistributorV3Abi } from "@gearbox-protocol/types/abi";
10-
import type { Address } from "viem";
11+
import type { Address, SimulateContractReturnType } from "viem";
12+
import { parseAbi } from "viem";
1113

1214
import type { Config } from "../../../config/index.js";
1315
import { DI } from "../../../di.js";
1416
import type { ILogger } from "../../../log/index.js";
1517
import type Client from "../../Client.js";
18+
import type { PartialLiquidationPreview } from "../types.js";
1619
import type {
1720
IPartialLiquidatorContract,
1821
MerkleDistributorInfo,
22+
OptimalPartialLiquidation,
23+
RawPartialLiquidationPreview,
1924
} from "./types.js";
2025

2126
export abstract class AbstractPartialLiquidatorContract
@@ -82,7 +87,7 @@ export abstract class AbstractPartialLiquidatorContract
8287

8388
protected async updateRouterAddress(router: Address): Promise<void> {
8489
const receipt = await this.client.simulateAndWrite({
85-
abi: iPartialLiquidatorAbi,
90+
abi: parseAbi(["function setRouter(address newRouter)"]),
8691
address: this.address,
8792
functionName: "setRouter",
8893
args: [router],
@@ -114,7 +119,9 @@ export abstract class AbstractPartialLiquidatorContract
114119
const results = await this.client.pub.multicall({
115120
allowFailure: false,
116121
contracts: this.#creditManagers.map(cm => ({
117-
abi: iPartialLiquidatorAbi,
122+
abi: parseAbi([
123+
"function cmToCA(address creditManager) view returns (address creditAccount)",
124+
]),
118125
address: this.address,
119126
functionName: "cmToCA",
120127
args: [cm.creditManager.address],
@@ -221,7 +228,7 @@ export abstract class AbstractPartialLiquidatorContract
221228
try {
222229
this.logger.debug(`need to register credit manager ${name} (${address})`);
223230
const receipt = await this.client.simulateAndWrite({
224-
abi: iPartialLiquidatorAbi,
231+
abi: parseAbi(["function registerCM(address creditManager)"]),
225232
address: this.address,
226233
functionName: "registerCM",
227234
args: [address],
@@ -243,6 +250,23 @@ export abstract class AbstractPartialLiquidatorContract
243250
}
244251
}
245252

253+
public abstract getOptimalLiquidation(
254+
creditAccount: Address,
255+
priceUpdates: OnDemandPriceUpdate[],
256+
): Promise<OptimalPartialLiquidation>;
257+
258+
public abstract previewPartialLiquidation(
259+
ca: CreditAccountData,
260+
cm: CreditSuite,
261+
optimalLiquidation: OptimalPartialLiquidation,
262+
priceUpdates: OnDemandPriceUpdate[],
263+
): Promise<RawPartialLiquidationPreview>;
264+
265+
public abstract partialLiquidateAndConvert(
266+
account: CreditAccountData,
267+
preview: PartialLiquidationPreview,
268+
): Promise<SimulateContractReturnType>;
269+
246270
public get envVariables(): Record<string, string> {
247271
return {};
248272
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from "./createPartialLiquidators.js";
22
export type { IPartialLiquidatorContract } from "./types.js";
3+
export * from "./utils.js";

src/services/liquidate/partial/types.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
1-
import type { CreditSuite } from "@gearbox-protocol/sdk";
2-
import type { Address, Hash } from "viem";
1+
import type {
2+
CreditAccountData,
3+
CreditSuite,
4+
MultiCall,
5+
OnDemandPriceUpdate,
6+
} from "@gearbox-protocol/sdk";
7+
import type { Address, Hash, SimulateContractReturnType } from "viem";
8+
9+
import type { PartialLiquidationPreview } from "../types.js";
10+
11+
export interface OptimalPartialLiquidation {
12+
tokenOut: Address;
13+
optimalAmount: bigint;
14+
repaidAmount: bigint;
15+
flashLoanAmount: bigint;
16+
isOptimalRepayable: boolean;
17+
}
18+
19+
export interface RawPartialLiquidationPreview {
20+
profit: bigint;
21+
calls: readonly MultiCall[];
22+
amountIn: bigint;
23+
amountOut: bigint;
24+
}
325

426
export interface IPartialLiquidatorContract {
527
address: Address;
@@ -15,6 +37,37 @@ export interface IPartialLiquidatorContract {
1537
* Deploys the liquidator contracts, if necessary
1638
*/
1739
deploy: () => Promise<void>;
40+
/**
41+
* Cross-version call to getOptimalLiquidation on liquidator contracts for rouuters v300 and v310
42+
* @param creditAccount
43+
* @param priceUpdates
44+
*/
45+
getOptimalLiquidation: (
46+
creditAccount: Address,
47+
priceUpdates: OnDemandPriceUpdate[],
48+
) => Promise<OptimalPartialLiquidation>;
49+
/**
50+
* Cross-version call to previewPartialLiquidation on liquidator contracts for rouuters v300 and v310
51+
* @param ca
52+
* @param cm
53+
* @param optimalLiquidation
54+
* @param priceUpdates
55+
*/
56+
previewPartialLiquidation: (
57+
ca: CreditAccountData,
58+
cm: CreditSuite,
59+
optimalLiquidation: OptimalPartialLiquidation,
60+
priceUpdates: OnDemandPriceUpdate[],
61+
) => Promise<RawPartialLiquidationPreview>;
62+
/**
63+
* Cross-version call to partialLiquidateAndConvert on liquidator contracts for rouuters v300 and v310
64+
* @param account
65+
* @param preview
66+
*/
67+
partialLiquidateAndConvert: (
68+
account: CreditAccountData,
69+
preview: PartialLiquidationPreview,
70+
) => Promise<SimulateContractReturnType>;
1871
}
1972

2073
export interface IPartialLiqudatorContractFactory {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { CreditSuite } from "@gearbox-protocol/sdk";
2+
import { formatBN } from "@gearbox-protocol/sdk";
3+
4+
import type { OptimalPartialLiquidation } from "./types.js";
5+
6+
export function humanizeOptimalLiquidation(
7+
cm: CreditSuite,
8+
data: OptimalPartialLiquidation,
9+
): Record<string, any> {
10+
const [symb, decimals, uSymb, uDec] = [
11+
cm.sdk.tokensMeta.symbol(data.tokenOut),
12+
cm.sdk.tokensMeta.decimals(data.tokenOut),
13+
cm.sdk.tokensMeta.symbol(cm.underlying),
14+
cm.sdk.tokensMeta.decimals(cm.underlying),
15+
];
16+
return {
17+
tokenOut: `${symb} (${data.tokenOut})`,
18+
optimalAmount:
19+
formatBN(data.optimalAmount, decimals) +
20+
` ${symb} (${data.optimalAmount})`,
21+
flashLoanAmount:
22+
formatBN(data.flashLoanAmount, uDec) +
23+
` ${uSymb} (${data.flashLoanAmount})`,
24+
repaidAmount:
25+
formatBN(data.repaidAmount, uDec) + ` ${uSymb} (${data.repaidAmount})`,
26+
isOptimalRepayable: data.isOptimalRepayable,
27+
};
28+
}

0 commit comments

Comments
 (0)