@@ -16,7 +16,7 @@ import {
16
16
} from '@solana/web3.js' ;
17
17
import { findFromSignatureAndToSignature } from '../utils/solana' ;
18
18
import { makeBlockKey } from '../databases/utils' ;
19
- import { BorshCoder , EventParser , Instruction } from '@coral-xyz/anchor' ;
19
+ import { BorshCoder , Event , EventParser , Instruction } from '@coral-xyz/anchor' ;
20
20
import { decodeTransferInstruction } from '@solana/spl-token' ;
21
21
22
22
import MATCHING_ENGINE_IDL from '../idls/matching_engine.json' ;
@@ -37,6 +37,8 @@ import {
37
37
FastTransferStatus ,
38
38
ParsedLogs ,
39
39
isOfferArgs ,
40
+ AuctionUpdated ,
41
+ AuctionUpdatedEvent ,
40
42
} from '../fastTransfer/types' ;
41
43
import knex , { Knex } from 'knex' ;
42
44
import { assertEnvironmentVariable } from '@wormhole-foundation/wormhole-monitor-common' ;
@@ -79,7 +81,7 @@ export class FastTransferSolanaWatcher extends SolanaWatcher {
79
81
this . MATCHING_ENGINE_PROGRAM_ID ,
80
82
new PublicKey ( this . USDC_MINT )
81
83
) ;
82
- this . getSignaturesLimit = 100 ;
84
+ this . connection = new Connection ( this . rpc ) ;
83
85
this . logger = getLogger ( `fast_transfer_solana_${ network . toLowerCase ( ) } ` ) ;
84
86
this . eventParser = new EventParser (
85
87
new PublicKey ( this . MATCHING_ENGINE_PROGRAM_ID ) ,
@@ -327,20 +329,9 @@ export class FastTransferSolanaWatcher extends SolanaWatcher {
327
329
) ;
328
330
const fastVaaMessage = LiquidityLayerMessage . decode ( fastVaaAccount . payload ( ) ) ;
329
331
330
- let message_protocol : FastTransferProtocol = FastTransferProtocol . NONE ;
331
- let cctp_domain : number | undefined ;
332
- let local_program_id : string | undefined ;
333
-
334
- if ( res . meta ?. logMessages ) {
335
- const auctionUpdate = await this . fetchEventFromLogs ( 'AuctionUpdated' , res . meta . logMessages ) ;
336
- if ( auctionUpdate . target_protocol . Cctp ) {
337
- message_protocol = FastTransferProtocol . CCTP ;
338
- cctp_domain = auctionUpdate . target_protocol . Cctp . domain ;
339
- } else if ( auctionUpdate . target_protocol . Local ) {
340
- message_protocol = FastTransferProtocol . LOCAL ;
341
- local_program_id = auctionUpdate . target_protocol . Local . program_id . toBase58 ( ) ;
342
- }
343
- }
332
+ const { message_protocol, cctp_domain, local_program_id } = this . checkMessageProtocols (
333
+ res . meta ?. logMessages || [ ]
334
+ ) ;
344
335
345
336
if ( ! fastVaaMessage . fastMarketOrder ) {
346
337
throw new Error (
@@ -393,6 +384,40 @@ export class FastTransferSolanaWatcher extends SolanaWatcher {
393
384
return { auction : fast_transfer , auction_offer } ;
394
385
}
395
386
387
+ checkMessageProtocols ( logs : string [ ] ) : {
388
+ message_protocol : FastTransferProtocol ;
389
+ cctp_domain : number | undefined ;
390
+ local_program_id : string | undefined ;
391
+ } {
392
+ const auctionUpdate = this . getAuctionUpdatedFromLogs ( logs ) ;
393
+ if ( ! auctionUpdate ) {
394
+ return {
395
+ message_protocol : FastTransferProtocol . NONE ,
396
+ cctp_domain : undefined ,
397
+ local_program_id : undefined ,
398
+ } ;
399
+ }
400
+
401
+ let message_protocol : FastTransferProtocol = FastTransferProtocol . NONE ;
402
+ let cctp_domain : number | undefined ;
403
+ let local_program_id : string | undefined ;
404
+
405
+ const { target_protocol } = auctionUpdate ;
406
+ if ( target_protocol . Cctp ) {
407
+ message_protocol = FastTransferProtocol . CCTP ;
408
+ cctp_domain = target_protocol . Cctp . domain ;
409
+ } else if ( target_protocol . Local ) {
410
+ message_protocol = FastTransferProtocol . LOCAL ;
411
+ local_program_id = target_protocol . Local . program_id . toBase58 ( ) ;
412
+ }
413
+
414
+ return {
415
+ message_protocol,
416
+ cctp_domain,
417
+ local_program_id,
418
+ } ;
419
+ }
420
+
396
421
/**
397
422
* This function parses the `improve_offer` instruction
398
423
* We can safely assume that the offer price here is better than the previous offer price since
@@ -648,7 +673,7 @@ export class FastTransferSolanaWatcher extends SolanaWatcher {
648
673
ix : MessageCompiledInstruction
649
674
) {
650
675
// Slow relay is not done yet, will implement after
651
- throw new Error ( '[parseSettleAuctionNoneCctp ] not implemented' ) ;
676
+ throw new Error ( '[parseSettleAuctionNoneLocal ] not implemented' ) ;
652
677
}
653
678
654
679
async parseSettleAuctionNoneCctp (
@@ -661,59 +686,76 @@ export class FastTransferSolanaWatcher extends SolanaWatcher {
661
686
662
687
/*
663
688
* `fetchAuction` fetches the auction from the chain first
664
- * if the auction is closed, `matchingEngineProgram.fetchAuction` will throw an error
665
- * we can catch this error and fetch the auction from the auction history
666
- * it's more suitable to use try-catch here because it accounts for when the auction account
667
- * is used to store other data.
689
+ * if `auctionAccount` is not null, decode it using borsh program and return
690
+ * otherwise, fetch the auction from the auction history
691
+ * if no auction is found even from history, return null
668
692
*/
669
693
async fetchAuction ( pubkey : string ) : Promise < {
670
694
vaaHash : string ;
671
- info : AuctionInfo ;
695
+ info : AuctionInfo | null ;
672
696
} | null > {
673
- try {
674
- const auction = await this . matchingEngineProgram . fetchAuction ( {
675
- address : new PublicKey ( pubkey ) ,
676
- } ) ;
677
-
678
- if ( ! auction . info ) {
679
- throw new Error ( 'Auction info not found' ) ;
680
- }
697
+ const auctionAccount = await this . connection ?. getAccountInfo ( new PublicKey ( pubkey ) ) ;
681
698
699
+ if ( auctionAccount ) {
700
+ const auctionInfo = this . matchingEngineBorshCoder . accounts . decode (
701
+ 'Auction' ,
702
+ auctionAccount . data
703
+ ) ;
704
+ // We need to do this manually because the account info given is in snake_case
682
705
return {
683
- vaaHash : Buffer . from ( auction . vaaHash ) . toString ( 'hex' ) ,
684
- info : auction . info ,
706
+ vaaHash : Buffer . from ( auctionInfo . vaa_hash ) . toString ( 'hex' ) ,
707
+ info : {
708
+ configId : auctionInfo . info . config_id ,
709
+ custodyTokenBump : auctionInfo . info . custody_token_bump ,
710
+ vaaSequence : auctionInfo . info . vaa_sequence ,
711
+ sourceChain : auctionInfo . info . source_chain ,
712
+ bestOfferToken : auctionInfo . info . best_offer_token ,
713
+ initialOfferToken : auctionInfo . info . initial_offer_token ,
714
+ startSlot : auctionInfo . info . start_slot ,
715
+ amountIn : auctionInfo . info . amount_in ,
716
+ securityDeposit : auctionInfo . info . security_deposit ,
717
+ offerPrice : auctionInfo . info . offer_price ,
718
+ redeemerMessageLen : auctionInfo . info . redeemer_message_len ,
719
+ destinationAssetInfo : auctionInfo . info . destination_asset_info ,
720
+ } ,
685
721
} ;
686
- } catch ( e ) {
687
- try {
688
- const auction = await this . fetchAuctionFromHistory ( pubkey ) ;
722
+ }
689
723
690
- if ( ! auction ) {
691
- throw new Error ( 'Auction not found' ) ;
692
- }
724
+ const auction = await this . fetchAuctionFromHistory ( pubkey ) ;
693
725
694
- return {
695
- vaaHash : Buffer . from ( auction . vaaHash ) . toString ( 'hex' ) ,
696
- info : auction . info ,
697
- } ;
698
- } catch ( e ) {
699
- this . logger . error ( 'Failed to fetch auction from history:' , e ) ;
700
- }
726
+ if ( ! auction ) {
727
+ this . logger . error ( `[fetchAuction] no auction found for ${ pubkey } ` ) ;
728
+ return null ;
701
729
}
702
730
703
- return null ;
731
+ return {
732
+ vaaHash : Buffer . from ( auction . vaaHash ) . toString ( 'hex' ) ,
733
+ info : auction . info ,
734
+ } ;
704
735
}
705
736
706
- async fetchEventFromLogs ( name : string , logs : string [ ] ) {
737
+ /*
738
+ * `getAuctionUpdatedFromLogs` fetches the auction updated event from the logs
739
+ * it's used to get the auction info from the auction updated event
740
+ * We only need `AuctionUpdated` event for now. If we need more events in the future, we can add them here
741
+ */
742
+ getAuctionUpdatedFromLogs ( logs : string [ ] ) : AuctionUpdated | null {
707
743
const parsedLogs = this . eventParser . parseLogs ( logs ) ;
708
744
for ( let event of parsedLogs ) {
709
- if ( event . name === name ) {
745
+ if ( this . isAuctionUpdatedEvent ( event ) ) {
710
746
return event . data ;
711
747
}
712
748
}
713
-
714
749
return null ;
715
750
}
716
751
752
+ /*
753
+ * `isAuctionUpdatedEvent` is a type guard that checks if the event is an `AuctionUpdated` event
754
+ */
755
+ isAuctionUpdatedEvent ( event : Event ) : event is AuctionUpdatedEvent {
756
+ return event . name === 'AuctionUpdated' && event . data !== null && typeof event . data === 'object' ;
757
+ }
758
+
717
759
/*
718
760
* `fetchAuctionFromHistory` fetches the auction from the auction history
719
761
* if there is a mapping in the db, we fetch the auction from the auction history using the mapping
@@ -745,7 +787,8 @@ export class FastTransferSolanaWatcher extends SolanaWatcher {
745
787
let latestAuctionHistoryIndex = await this . getDbLatestAuctionHistoryIndex ( ) ;
746
788
const auctionHistories = [ ] ;
747
789
748
- while ( true ) {
790
+ let foundAllAuctionHistory = false ;
791
+ while ( ! foundAllAuctionHistory ) {
749
792
try {
750
793
const auctionHistory = await this . matchingEngineProgram . fetchAuctionHistory (
751
794
latestAuctionHistoryIndex
@@ -754,8 +797,7 @@ export class FastTransferSolanaWatcher extends SolanaWatcher {
754
797
latestAuctionHistoryIndex ++ ;
755
798
} catch ( error ) {
756
799
// if no more auction history records to fetch or an error occurred, break the loop
757
- this . logger . error ( 'No more auction history records to fetch or an error occurred:' , error ) ;
758
- break ;
800
+ foundAllAuctionHistory = true ;
759
801
}
760
802
}
761
803
@@ -790,7 +832,6 @@ export class FastTransferSolanaWatcher extends SolanaWatcher {
790
832
791
833
try {
792
834
const result = await this . pg ( 'auction_history_mapping' ) . max ( 'index as maxIndex' ) . first ( ) ;
793
- console . log ( result ) ;
794
835
return result && result . maxIndex !== null ? BigInt ( result . maxIndex ) : 0n ;
795
836
} catch ( error ) {
796
837
this . logger . error ( 'Failed to fetch the largest index from auction_history_mapping:' , error ) ;
0 commit comments