11import type {
22 CreditAccountData ,
3+ CreditAccountServiceOptions ,
4+ GearboxSDK ,
5+ GetCreditAccountsOptions ,
36 ICreditAccountsService ,
47 NetworkType ,
58} from "@gearbox-protocol/sdk" ;
69import {
10+ AbstractCreditAccountService ,
711 hexEq ,
812 MAX_UINT256 ,
913 PERCENTAGE_FACTOR ,
1014 WAD ,
1115} from "@gearbox-protocol/sdk" ;
1216import { iBotListV310Abi } from "@gearbox-protocol/sdk/abi/v310" ;
17+ import { getAlchemyUrl , getDrpcUrl } from "@gearbox-protocol/sdk/dev" ;
1318import {
1419 iCreditManagerV3Abi ,
1520 iPartialLiquidationBotV3Abi ,
1621} from "@gearbox-protocol/types/abi" ;
17- import type { Address , Block } from "viem" ;
18- import { getContract } from "viem" ;
22+ import type { Address , Block , PublicClient } from "viem" ;
23+ import { createPublicClient , getContract , http } from "viem" ;
1924import type { Config } from "../config/index.js" ;
2025import { DI } from "../di.js" ;
2126import { type ILogger , Logger } from "../log/index.js" ;
@@ -53,6 +58,7 @@ export class Scanner {
5358 #maxHealthFactor = MAX_UINT256 ;
5459 #minHealthFactor = 0n ;
5560 #unwatch?: ( ) => void ;
61+ #diagnoster?: ScannerDiagnoster ;
5662
5763 public async launch ( ) : Promise < void > {
5864 await this . liquidatorService . launch ( ) ;
@@ -86,6 +92,10 @@ export class Scanner {
8692 }
8793 }
8894
95+ if ( this . config . debugScanner ) {
96+ this . #diagnoster = new ScannerDiagnoster ( ) ;
97+ }
98+
8999 // we should not pin block during optimistic liquidations
90100 // because during optimistic liquidations we need to call evm_mine to make redstone work
91101 await this . #updateAccounts(
@@ -134,7 +144,7 @@ export class Scanner {
134144
135145 /**
136146 * Loads new data and recompute all health factors
137- * @param blockNumber Fiex block for archive node which is needed to get data
147+ * @param blockNumber Fixed block for archive node which is needed to get data
138148 */
139149 async #updateAccounts( blockNumber ?: bigint ) : Promise < void > {
140150 const start = Date . now ( ) ;
@@ -147,15 +157,14 @@ export class Scanner {
147157 ) ;
148158 accounts = acc ? [ acc ] : [ ] ;
149159 } else {
150- accounts = await this . caService . getCreditAccounts (
151- {
152- minHealthFactor : this . #minHealthFactor,
153- maxHealthFactor : this . #maxHealthFactor,
154- includeZeroDebt : false ,
155- creditManager : this . config . debugManager ,
156- } ,
157- blockNumber ,
158- ) ;
160+ const queue : GetCreditAccountsOptions = {
161+ minHealthFactor : this . #minHealthFactor,
162+ maxHealthFactor : this . #maxHealthFactor,
163+ includeZeroDebt : false ,
164+ creditManager : this . config . debugManager ,
165+ } ;
166+ accounts = await this . caService . getCreditAccounts ( queue , blockNumber ) ;
167+ await this . #diagnoster?. checkAccounts ( accounts , queue , blockNumber ) ;
159168 }
160169 if ( this . config . restakingWorkaround ) {
161170 const before = accounts . length ;
@@ -353,3 +362,103 @@ export class Scanner {
353362 this . log . info ( "stopped" ) ;
354363 }
355364}
365+
366+ class ScannerDiagnoster {
367+ @Logger ( "ScannerDiagnoster" )
368+ log ! : ILogger ;
369+
370+ @DI . Inject ( DI . Config )
371+ config ! : Config ;
372+
373+ @DI . Inject ( DI . CreditAccountService )
374+ caService ! : ICreditAccountsService ;
375+
376+ #drpc?: WorkaroundCAS ;
377+ #alchemy?: WorkaroundCAS ;
378+
379+ constructor ( ) {
380+ if ( this . config . drpcKeys ?. length ) {
381+ const rpcURL = getDrpcUrl (
382+ this . config . network ,
383+ this . config . drpcKeys [ 0 ] . value ,
384+ "http" ,
385+ ) ;
386+ if ( rpcURL ) {
387+ this . #drpc = new WorkaroundCAS ( this . caService . sdk , { rpcURL } ) ;
388+ }
389+ }
390+ if ( this . config . alchemyKeys ?. length ) {
391+ const rpcURL = getAlchemyUrl (
392+ this . config . network ,
393+ this . config . alchemyKeys [ 0 ] . value ,
394+ "http" ,
395+ ) ;
396+ if ( rpcURL ) {
397+ this . #alchemy = new WorkaroundCAS ( this . caService . sdk , { rpcURL } ) ;
398+ }
399+ }
400+ if ( ! ! this . #drpc && ! ! this . #alchemy) {
401+ this . log . info ( "scanner diagnoster enabled" ) ;
402+ }
403+ }
404+
405+ public async checkAccounts (
406+ accounts : CreditAccountData [ ] ,
407+ queue : GetCreditAccountsOptions ,
408+ blockNumber ?: bigint ,
409+ ) : Promise < void > {
410+ try {
411+ if ( ! accounts . length || ! blockNumber ) {
412+ return ;
413+ }
414+ let [ success , drpcSuccess , alchemySuccess ] = [ 0 , 0 , 0 ] ;
415+ for ( const a of accounts ) {
416+ success += a . success ? 1 : 0 ;
417+ }
418+ this . log . debug (
419+ `found ${ accounts . length } liquidatable accounts (${ success } successful) in block ${ blockNumber } ` ,
420+ ) ;
421+ if ( ! this . #drpc || ! this . #alchemy) {
422+ return ;
423+ }
424+ const [ drpcAccs , alchemyAccs ] = await Promise . all ( [
425+ this . #drpc. getCreditAccounts ( queue , blockNumber ) ,
426+ this . #alchemy. getCreditAccounts ( queue , blockNumber ) ,
427+ ] ) ;
428+ for ( const a of drpcAccs ) {
429+ drpcSuccess += a . success ? 1 : 0 ;
430+ }
431+ for ( const a of alchemyAccs ) {
432+ alchemySuccess += a . success ? 1 : 0 ;
433+ }
434+ this . log . debug (
435+ `found ${ drpcAccs . length } liquidatable accounts (${ drpcSuccess } successful) in block ${ blockNumber } with drpc` ,
436+ ) ;
437+ this . log . debug (
438+ `found ${ alchemyAccs . length } liquidatable accounts (${ alchemySuccess } successful) in block ${ blockNumber } with alchemy` ,
439+ ) ;
440+ } catch ( e ) {
441+ this . log . error ( e ) ;
442+ }
443+ }
444+ }
445+
446+ interface WorkaroundCASOptions extends CreditAccountServiceOptions {
447+ rpcURL : string ;
448+ }
449+
450+ class WorkaroundCAS extends AbstractCreditAccountService {
451+ #client: PublicClient ;
452+
453+ constructor ( sdk : GearboxSDK , opts : WorkaroundCASOptions ) {
454+ super ( sdk , opts ) ;
455+ this . #client = createPublicClient ( {
456+ transport : http ( opts . rpcURL , { timeout : 600_000 } ) ,
457+ chain : sdk . provider . chain ,
458+ } ) ;
459+ }
460+
461+ public override get client ( ) : PublicClient {
462+ return this . #client;
463+ }
464+ }
0 commit comments