Skip to content

Commit 6578e8a

Browse files
committed
fix: debug failed cas
1 parent 02e935b commit 6578e8a

File tree

8 files changed

+150
-13
lines changed

8 files changed

+150
-13
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
"@commander-js/extra-typings": "^14.0.0",
2626
"@commitlint/cli": "^19.8.1",
2727
"@commitlint/config-conventional": "^19.8.1",
28-
"@gearbox-protocol/biome-config": "^1.0.0",
28+
"@gearbox-protocol/biome-config": "^1.0.1",
2929
"@gearbox-protocol/cli-utils": "^5.45.14",
3030
"@gearbox-protocol/liquidator-contracts": "^1.36.0-experimental.41",
3131
"@gearbox-protocol/liquidator-v2-contracts": "^2.4.0",
32-
"@gearbox-protocol/sdk": "8.26.4",
32+
"@gearbox-protocol/sdk": "8.26.6",
3333
"@gearbox-protocol/types": "^1.14.8",
3434
"@types/node": "^24.3.0",
3535
"@uniswap/sdk-core": "^7.7.2",

src/MulticallSpy.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { join } from "node:path";
2+
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
3+
import type {
4+
EIP1193Parameters,
5+
HttpTransportConfig,
6+
PublicRpcSchema,
7+
} from "viem";
8+
import type { Config } from "./config/index.js";
9+
import { DI } from "./di.js";
10+
import { type ILogger, Logger } from "./log/index.js";
11+
12+
/**
13+
* This is temporary solution to diagnose bug where compressor occasionally returns many accounts with HF = 0
14+
*/
15+
@DI.Injectable(DI.MulticallSpy)
16+
export default class MulticallSpy {
17+
@Logger("MulticallSpy")
18+
log!: ILogger;
19+
20+
@DI.Inject(DI.Config)
21+
config!: Config;
22+
23+
#client = new S3Client({});
24+
#detectedCalls: EIP1193Parameters<PublicRpcSchema>[] = [];
25+
#detectedBlock = 0n;
26+
27+
public multicallSpy: Required<HttpTransportConfig>["onFetchRequest"] =
28+
async request => {
29+
if (!this.config.debugGetCreditAccounts) {
30+
return;
31+
}
32+
const data = (await request.json()) as EIP1193Parameters<PublicRpcSchema>;
33+
const blockNumber = isGetCreditAccountsMulticall(data);
34+
if (blockNumber) {
35+
this.#storeCall(blockNumber, data);
36+
this.log.debug(
37+
`stored getCreditAccounts multicall at block ${blockNumber}, total calls: ${this.#detectedCalls.length}`,
38+
);
39+
}
40+
};
41+
42+
public async dumpCalls(): Promise<void> {
43+
if (!this.config.debugGetCreditAccounts) {
44+
return;
45+
}
46+
if (!this.config.outS3Bucket) {
47+
this.log.error("outS3Bucket is not set");
48+
return;
49+
}
50+
const key = join(
51+
this.config.outS3Prefix,
52+
`getCreditAccounts_${this.#detectedBlock}.json`,
53+
);
54+
const s3Url = `s3://${this.config.outS3Bucket}/${key}`;
55+
try {
56+
this.log.debug(`uploading to ${s3Url}`);
57+
await this.#client.send(
58+
new PutObjectCommand({
59+
Bucket: this.config.outS3Bucket,
60+
Key: key,
61+
ContentType: "application/json",
62+
Body: JSON.stringify(this.#detectedCalls),
63+
}),
64+
);
65+
} catch (e) {
66+
this.log.error(e, `failed to upload to ${s3Url}`);
67+
}
68+
}
69+
70+
#storeCall(
71+
blockNumber: bigint,
72+
data: EIP1193Parameters<PublicRpcSchema>,
73+
): void {
74+
if (blockNumber !== this.#detectedBlock) {
75+
this.#detectedBlock = blockNumber;
76+
this.#detectedCalls = [];
77+
}
78+
this.#detectedCalls.push(data);
79+
}
80+
}
81+
82+
/**
83+
* Detects multicalls to CreditAccounts.getCreditAccounts
84+
* @param data
85+
* @returns block number if it's a getCreditAccounts multicall, undefined otherwise
86+
*/
87+
function isGetCreditAccountsMulticall(
88+
data: EIP1193Parameters<PublicRpcSchema>,
89+
): bigint | undefined {
90+
try {
91+
if (
92+
data.method === "eth_call" &&
93+
// detect eth_call to multicall3: 0xca11bde05977b3631167028862be2a173976ca11
94+
data.params[0].to === "0xca11bde05977b3631167028862be2a173976ca11" &&
95+
typeof data.params[1] === "string" &&
96+
data.params[1]?.startsWith("0x") && // non-latest block
97+
// that contain CA compressor: "0x4115708Fc8fe6bB392De2e0C21c2C81dA2222394"
98+
data.params[0].data?.includes(
99+
"4115708fc8fe6bb392de2e0c21c2c81da2222394",
100+
) &&
101+
// includes getCreditAccounts signature
102+
// cast 4byte 0xf43bdb34
103+
// getCreditAccounts((address[],address[],address[],address),(address,bool,uint256,uint256,bool),uint256)
104+
// cast 4byte 0x8b59b911
105+
// getCreditAccounts((address[],address[],address[],address),(address,bool,uint256,uint256,bool),uint256,uint256)
106+
(data.params[0].data?.includes("f43bdb34") ||
107+
data.params[0].data?.includes("8b59b911"))
108+
) {
109+
return BigInt(data.params[1]);
110+
}
111+
} catch {}
112+
}

src/attachSDK.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import {
1111
import type { Config } from "./config/index.js";
1212
import { DI } from "./di.js";
1313
import type { ILogger } from "./log/index.js";
14+
import type MulticallSpy from "./MulticallSpy.js";
1415
import type Client from "./services/Client.js";
1516
import { createTransport, formatTs } from "./utils/index.js";
1617

1718
export default async function attachSDK(): Promise<ICreditAccountsService> {
1819
const config: Config = DI.get(DI.Config);
1920
const client: Client = DI.get(DI.Client);
2021
const logger: ILogger = DI.create(DI.Logger, "sdk");
22+
const multicallSpy: MulticallSpy = DI.get(DI.MulticallSpy);
2123

2224
await client.launch();
2325
let optimisticTimestamp: number | undefined;
@@ -59,6 +61,9 @@ export default async function attachSDK(): Promise<ICreditAccountsService> {
5961

6062
const transport = createTransport(config, {
6163
timeout: 600_000,
64+
onFetchRequest: config.debugGetCreditAccounts
65+
? (r, o) => multicallSpy.multicallSpy(r, o)
66+
: undefined,
6267
});
6368

6469
const sdk = await GearboxSDK.attach({

src/config/common.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ export const CommonSchema = z.object({
6060
description: "These accounts will not be liquidated",
6161
env: "IGNORE_ACCOUNTS",
6262
}),
63+
/**
64+
* Temporary flag to diagnose situations where compressor returns many accounts with HF = 0
65+
*/
66+
debugGetCreditAccounts: boolLike().optional().register(zommandRegistry, {
67+
flags: "--debug-get-credit-accounts",
68+
description:
69+
"Temporary flag to diagnose situations where compressor returns many accounts with HF = 0",
70+
env: "DEBUG_GET_CREDIT_ACCOUNTS",
71+
}),
6372
/**
6473
* Only check this account during local debug session
6574
*/

src/di.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const Injectables = {
1313
Scanner: "Scanner",
1414
Swapper: "Swapper",
1515
CreditAccountService: "CreditAccountService",
16+
MulticallSpy: "MulticallSpy",
1617
} as const;
1718

1819
export const DI = Object.assign(
@@ -32,6 +33,7 @@ export const DI = Object.assign(
3233
Scanner: [];
3334
Swapper: [];
3435
CreditAccountService: [];
36+
MulticallSpy: [];
3537
}>(),
3638
Injectables,
3739
);

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import "./services/liquidate/index.js";
66
import "./services/output/index.js";
77
import "./services/notifier/index.js";
88
import "./services/swap/index.js";
9+
import "./MulticallSpy.js";
910

1011
import { setTimeout } from "node:timers/promises";
1112

src/services/Scanner.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { getContract } from "viem";
1919
import type { Config } from "../config/index.js";
2020
import { DI } from "../di.js";
2121
import { type ILogger, Logger } from "../log/index.js";
22+
import type MulticallSpy from "../MulticallSpy.js";
2223
import type Client from "./Client.js";
2324
import type { ILiquidatorService } from "./liquidate/index.js";
2425

@@ -46,6 +47,9 @@ export class Scanner {
4647
@DI.Inject(DI.CreditAccountService)
4748
caService!: ICreditAccountsService;
4849

50+
@DI.Inject(DI.MulticallSpy)
51+
multicallSpy!: MulticallSpy;
52+
4953
#processing: bigint | null = null;
5054
#restakingCMAddr?: Address;
5155
#restakingMinHF?: bigint;
@@ -201,6 +205,10 @@ export class Scanner {
201205
`${accounts.length} accounts to ${verb}${blockS}, time: ${time}s`,
202206
);
203207

208+
if (this.config.debugGetCreditAccounts && accounts.length > 0) {
209+
await this.multicallSpy.dumpCalls();
210+
}
211+
204212
if (this.config.optimistic) {
205213
await this.liquidatorService.liquidateOptimistic(accounts);
206214
} else {

yarn.lock

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,12 +1719,12 @@ __metadata:
17191719
languageName: node
17201720
linkType: hard
17211721

1722-
"@gearbox-protocol/biome-config@npm:^1.0.0":
1723-
version: 1.0.0
1724-
resolution: "@gearbox-protocol/biome-config@npm:1.0.0"
1722+
"@gearbox-protocol/biome-config@npm:^1.0.1":
1723+
version: 1.0.1
1724+
resolution: "@gearbox-protocol/biome-config@npm:1.0.1"
17251725
peerDependencies:
1726-
"@biomejs/biome": ^2.0.5
1727-
checksum: 10c0/e62b9c3dac5a4941ba074665718adc8b4a79af5be15da336be77cbcf64969a03b2624315f24be4d678b56935a8e37ce1c75c2875a158bb0c1cd5373a22eea8df
1726+
"@biomejs/biome": ^2.2.2
1727+
checksum: 10c0/517461a34a6ec07885083e7d93ba57197ebf11b019b563b88206e82fd0d773d454af43445bedfb9518d4b847e704327254b82532bfcecfeef40bcc0be98ab501
17281728
languageName: node
17291729
linkType: hard
17301730

@@ -1766,11 +1766,11 @@ __metadata:
17661766
"@commander-js/extra-typings": "npm:^14.0.0"
17671767
"@commitlint/cli": "npm:^19.8.1"
17681768
"@commitlint/config-conventional": "npm:^19.8.1"
1769-
"@gearbox-protocol/biome-config": "npm:^1.0.0"
1769+
"@gearbox-protocol/biome-config": "npm:^1.0.1"
17701770
"@gearbox-protocol/cli-utils": "npm:^5.45.14"
17711771
"@gearbox-protocol/liquidator-contracts": "npm:^1.36.0-experimental.41"
17721772
"@gearbox-protocol/liquidator-v2-contracts": "npm:^2.4.0"
1773-
"@gearbox-protocol/sdk": "npm:8.26.4"
1773+
"@gearbox-protocol/sdk": "npm:8.26.6"
17741774
"@gearbox-protocol/types": "npm:^1.14.8"
17751775
"@types/node": "npm:^24.3.0"
17761776
"@uniswap/sdk-core": "npm:^7.7.2"
@@ -1796,9 +1796,9 @@ __metadata:
17961796
languageName: unknown
17971797
linkType: soft
17981798

1799-
"@gearbox-protocol/sdk@npm:8.26.4":
1800-
version: 8.26.4
1801-
resolution: "@gearbox-protocol/sdk@npm:8.26.4"
1799+
"@gearbox-protocol/sdk@npm:8.26.6":
1800+
version: 8.26.6
1801+
resolution: "@gearbox-protocol/sdk@npm:8.26.6"
18021802
dependencies:
18031803
"@redstone-finance/evm-connector": "npm:^0.7.5"
18041804
"@redstone-finance/protocol": "npm:^0.7.5"
@@ -1812,7 +1812,7 @@ __metadata:
18121812
zod: "npm:^4.1.1"
18131813
peerDependencies:
18141814
axios: ^1.0.0
1815-
checksum: 10c0/d8f2264217bd27e5734dc813fedc2c4493769a7c9bc13bc72f2e2ef1267be0a6a17144144608d0072af3df6f058a851ac3122931bf6b680e90c8f27fb8ea5da0
1815+
checksum: 10c0/6d15f74b6ca8816881fc47bddbcc5e64a003eda492637f2aafdae463741d9a1ad815b51c6dbbb61c511a15c3bdfaa794e72d8f0ddf8e1a1ea5983ff19121c6b6
18161816
languageName: node
18171817
linkType: hard
18181818

0 commit comments

Comments
 (0)