1
1
import {
2
2
Address ,
3
3
Hash ,
4
- Abi ,
5
4
Account ,
6
5
InferFunctionName ,
7
6
GetFunctionArgs ,
@@ -17,12 +16,15 @@ import {
17
16
zeroAddress ,
18
17
GetFilterLogsReturnType ,
19
18
InferEventName ,
19
+ Abi ,
20
+ CreateContractEventFilterParameters ,
20
21
} from 'viem' ;
21
22
import { stringify } from 'superjson' ;
22
23
import {
23
24
marketABI ,
24
25
erc20_18ABI ,
25
26
entitiesRegistryABI ,
27
+ configABI ,
26
28
kinds ,
27
29
} from '@windingtree/contracts' ;
28
30
import {
@@ -52,6 +54,11 @@ export interface ProtocolContractsOptions {
52
54
walletClient ?: WalletClient ;
53
55
}
54
56
57
+ /**
58
+ * Generic filter options type.
59
+ */
60
+ type FilterOptions = CreateContractEventFilterParameters < Abi , string > ;
61
+
55
62
/**
56
63
* Common API of the protocol smart contracts set
57
64
*
@@ -688,69 +695,154 @@ export class ProtocolContracts<
688
695
}
689
696
690
697
/**
691
- * Subscribes to specific events emitted by the market smart contract.
698
+ * Subscribes to events from a specified smart contract.
692
699
*
693
- * @param eventName - The name of the event to listen for.
694
- * @param onLogs - Callback function to handle the event logs.
695
- * @param fromBlock - (Optional) The starting block number for listening to events.
696
- * @param pollInterval - (Optional) Interval in milliseconds for polling new events.
697
- * @returns A function to unsubscribe from the event.
698
- * @template TEventName - Generic type parameter for event name.
700
+ * @template TAbi The ABI type of the contract.
701
+ * @template TEventName The name of the event to subscribe to.
702
+ * @param {TAbi } abi The ABI of the contract to subscribe to.
703
+ * @param {Address } address The address of the contract.
704
+ * @param {InferEventName<TAbi, TEventName> } eventName The name of the event.
705
+ * @param {(logs: GetFilterLogsReturnType<TAbi>) => void } onLogs Callback to execute when logs are received.
706
+ * @param {bigint } [fromBlock] The block number from which to start listening for events.
707
+ * @param {number } [pollInterval=1000] The interval in milliseconds at which to poll for new events.
708
+ * @returns {Promise<() => void> } A promise that resolves to an unsubscribe function.
709
+ * @private
699
710
*/
700
- async subscribeMarket < TEventName extends string | undefined = undefined > (
701
- eventName : InferEventName < typeof marketABI , TEventName > ,
702
- onLogs : ( logs : GetFilterLogsReturnType < typeof marketABI > ) => void ,
711
+ private async subscribeToEvents <
712
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
713
+ const TAbi extends Abi | readonly unknown [ ] = Abi ,
714
+ TEventName extends string | undefined = undefined ,
715
+ > (
716
+ abi : TAbi ,
717
+ address : Address ,
718
+ eventName : InferEventName < TAbi , TEventName > ,
719
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
720
+ onLogs : ( logs : any ) => void ,
703
721
fromBlock ?: bigint ,
704
- pollInterval = 1000 ,
722
+ pollInterval : number = 1000 ,
705
723
) : Promise < ( ) => void > {
706
724
let blockNumber = await this . publicClient . getBlockNumber ( ) ;
707
725
let isUnsubscribed = false ;
708
726
let timeoutId : NodeJS . Timeout ;
709
727
710
- // Use the specified fromBlock or the current block number
728
+ // Adjust starting block number if fromBlock is provided and valid
711
729
if ( fromBlock && fromBlock < blockNumber ) {
712
730
blockNumber = fromBlock ;
713
731
}
714
732
715
733
// Function to fetch and process logs
716
734
const getLogs = async ( ) => {
717
- if ( isUnsubscribed ) return ; // Stop if unsubscribed
735
+ if ( isUnsubscribed ) return ;
718
736
719
- // Create an event filter
720
737
const filter = await this . publicClient . createContractEventFilter ( {
721
- abi : marketABI ,
722
- address : this . contracts [ 'market' ] . address ,
723
- eventName,
738
+ abi,
739
+ address,
724
740
fromBlock : blockNumber ,
725
741
strict : true ,
726
- } ) ;
742
+ eventName : eventName ,
743
+ } as unknown as FilterOptions ) ;
727
744
728
- // Retrieve logs based on the filter
729
745
const logs = await this . publicClient . getFilterLogs ( { filter } ) ;
730
746
731
- // Process logs and update the block number
732
747
if ( logs . length > 0 ) {
733
- const maxBlockNumber = logs . reduce (
734
- ( max , log ) => ( log . blockNumber > max ? log . blockNumber : max ) ,
735
- BigInt ( 0 ) ,
736
- ) ;
737
- blockNumber = maxBlockNumber + BigInt ( 1 ) ;
738
748
onLogs ( logs ) ;
749
+ // Update the block number to the next after the last log's block
750
+ const bn = logs [ logs . length - 1 ] . blockNumber ;
751
+ blockNumber = ( bn !== null ? bn : BigInt ( 0 ) ) + BigInt ( 1 ) ;
739
752
}
740
753
741
- // Schedule the next call
742
- timeoutId = setTimeout ( ( ) => {
743
- getLogs ( ) . catch ( logger . error ) ;
744
- } , pollInterval ) ;
754
+ if ( ! isUnsubscribed ) {
755
+ timeoutId = setTimeout ( ( ) => {
756
+ getLogs ( ) . catch ( logger . error ) ;
757
+ } , pollInterval ) ;
758
+ }
745
759
} ;
746
760
747
- // Initial call to start the polling process
761
+ // Initial call to start polling
748
762
getLogs ( ) . catch ( logger . error ) ;
749
763
750
- // Return the unsubscribe function
764
+ // Return unsubscribe function
751
765
return ( ) => {
752
- isUnsubscribed = true ; // Set the flag to stop further polling
753
- clearTimeout ( timeoutId ) ; // Clear the timeout to stop scheduled calls
766
+ isUnsubscribed = true ;
767
+ clearTimeout ( timeoutId ) ;
754
768
} ;
755
769
}
770
+
771
+ /**
772
+ * Subscribes to market contract events.
773
+ *
774
+ * @template TEventName Type of event name.
775
+ * @param {InferEventName<typeof marketABI, TEventName> } eventName The event name to subscribe to.
776
+ * @param {(logs: GetFilterLogsReturnType<typeof marketABI>) => void } onLogs Callback for when logs are received.
777
+ * @param {bigint } [fromBlock] Starting block number for listening for events.
778
+ * @param {number } [pollInterval=1000] Polling interval in milliseconds.
779
+ * @returns {Promise<() => void> } Unsubscribe function.
780
+ */
781
+ async subscribeMarket < TEventName extends string | undefined = undefined > (
782
+ eventName : InferEventName < typeof marketABI , TEventName > ,
783
+ onLogs : ( logs : GetFilterLogsReturnType < typeof marketABI > ) => void ,
784
+ fromBlock ?: bigint ,
785
+ pollInterval : number = 1000 ,
786
+ ) : Promise < ( ) => void > {
787
+ return this . subscribeToEvents (
788
+ marketABI ,
789
+ this . contracts [ 'market' ] . address ,
790
+ eventName ,
791
+ onLogs ,
792
+ fromBlock ,
793
+ pollInterval ,
794
+ ) ;
795
+ }
796
+
797
+ /**
798
+ * Subscribes to entities contract events.
799
+ *
800
+ * @template TEventName Type of event name.
801
+ * @param {InferEventName<typeof entitiesRegistryABI, TEventName> } eventName The event name to subscribe to.
802
+ * @param {(logs: GetFilterLogsReturnType<typeof entitiesRegistryABI>) => void } onLogs Callback for when logs are received.
803
+ * @param {bigint } [fromBlock] Starting block number for listening for events.
804
+ * @param {number } [pollInterval=1000] Polling interval in milliseconds.
805
+ * @returns {Promise<() => void> } Unsubscribe function.
806
+ */
807
+ async subscribeEntities < TEventName extends string | undefined = undefined > (
808
+ eventName : InferEventName < typeof entitiesRegistryABI , TEventName > ,
809
+ onLogs : ( logs : GetFilterLogsReturnType < typeof entitiesRegistryABI > ) => void ,
810
+ fromBlock ?: bigint ,
811
+ pollInterval : number = 1000 ,
812
+ ) : Promise < ( ) => void > {
813
+ return this . subscribeToEvents (
814
+ entitiesRegistryABI ,
815
+ this . contracts [ 'entities' ] . address ,
816
+ eventName ,
817
+ onLogs ,
818
+ fromBlock ,
819
+ pollInterval ,
820
+ ) ;
821
+ }
822
+
823
+ /**
824
+ * Subscribes to config contract events.
825
+ *
826
+ * @template TEventName Type of event name.
827
+ * @param {InferEventName<typeof configABI, TEventName> } eventName The event name to subscribe to.
828
+ * @param {(logs: GetFilterLogsReturnType<typeof configABI>) => void } onLogs Callback for when logs are received.
829
+ * @param {bigint } [fromBlock] Starting block number for listening for events.
830
+ * @param {number } [pollInterval=1000] Polling interval in milliseconds.
831
+ * @returns {Promise<() => void> } Unsubscribe function.
832
+ */
833
+ async subscribeConfig < TEventName extends string | undefined = undefined > (
834
+ eventName : InferEventName < typeof configABI , TEventName > ,
835
+ onLogs : ( logs : GetFilterLogsReturnType < typeof configABI > ) => void ,
836
+ fromBlock ?: bigint ,
837
+ pollInterval : number = 1000 ,
838
+ ) : Promise < ( ) => void > {
839
+ return this . subscribeToEvents (
840
+ configABI ,
841
+ this . contracts [ 'config' ] . address ,
842
+ eventName ,
843
+ onLogs ,
844
+ fromBlock ,
845
+ pollInterval ,
846
+ ) ;
847
+ }
756
848
}
0 commit comments