11import { ethers } from 'ethers' ;
2+ import { EventEmitter } from 'eventemitter3' ;
23
34import {
45 canonicalAccessControlConditionFormatter ,
@@ -140,7 +141,7 @@ const FALLBACK_RPC_URLS = [
140141 'https://eth.llamarpc.com' ,
141142] ;
142143
143- export class LitCore {
144+ export class LitCore extends EventEmitter {
144145 config : LitNodeClientConfigWithDefaults = {
145146 alertWhenUnauthorized : false ,
146147 debug : true ,
@@ -173,6 +174,8 @@ export class LitCore {
173174
174175 // ========== Constructor ==========
175176 constructor ( config : LitNodeClientConfig | CustomNetwork ) {
177+ super ( ) ;
178+
176179 if ( ! ( config . litNetwork in LIT_NETWORKS ) ) {
177180 const validNetworks = Object . keys ( LIT_NETWORKS ) . join ( ', ' ) ;
178181 throw new InvalidParamType (
@@ -300,20 +303,16 @@ export class LitCore {
300303 private async _handleStakingContractStateChange (
301304 state : STAKING_STATES_VALUES
302305 ) {
303- log ( `New state detected: "${ state } "` ) ;
304-
305- const validatorData = await this . _getValidatorData ( ) ;
306-
307- if ( state === STAKING_STATES . Active ) {
308- // We always want to track the most recent epoch number on _all_ networks
306+ try {
307+ log ( `New state detected: "${ state } "` ) ;
309308
310- this . _epochState = await this . _fetchCurrentEpochState (
311- validatorData . epochInfo
312- ) ;
309+ if ( state === STAKING_STATES . Active ) {
310+ const validatorData = await this . _getValidatorData ( ) ;
313311
314- if ( CENTRALISATION_BY_NETWORK [ this . config . litNetwork ] !== 'centralised' ) {
315312 // We don't need to handle node urls changing on centralised networks, since their validator sets are static
316- try {
313+ if (
314+ CENTRALISATION_BY_NETWORK [ this . config . litNetwork ] !== 'centralised'
315+ ) {
317316 log (
318317 'State found to be new validator set locked, checking validator set'
319318 ) ;
@@ -322,39 +321,54 @@ export class LitCore {
322321 const delta : string [ ] = validatorData . bootstrapUrls . filter ( ( item ) =>
323322 existingNodeUrls . includes ( item )
324323 ) ;
325- // if the sets differ we reconnect.
324+
325+ // check if the node sets are non-matching and re-connect if they do not.
326326 if ( delta . length > 1 ) {
327- // check if the node sets are non-matching and re-connect if they do not.
328327 /*
329- TODO: This covers *most* cases where a node may come in or out of the active
330- set which we will need to re attest to the execution environments.
331- However, the sdk currently does not know if there is an active network operation pending.
332- Such that the state when the request was sent will now mutate when the response is sent back.
333- The sdk should be able to understand its current execution environment and wait on an active
334- network request to the previous epoch's node set before changing over.
335- */
328+ TODO: This covers *most* cases where a node may come in or out of the active
329+ set which we will need to re attest to the execution environments.
330+ However, the sdk currently does not know if there is an active network operation pending.
331+ Such that the state when the request was sent will now mutate when the response is sent back.
332+ The sdk should be able to understand its current execution environment and wait on an active
333+ network request to the previous epoch's node set before changing over.
334+ */
336335 log (
337336 'Active validator sets changed, new validators ' ,
338337 delta ,
339338 'starting node connection'
340339 ) ;
340+ await this . connect ( ) ; // Will update `epochInfo`
341341 }
342-
343- await this . connect ( ) ;
344- } catch ( err : unknown ) {
345- // FIXME: We should emit an error event so that consumers know that we are de-synced and can connect() again
346- // But for now, our every-30-second network sync will fix things in at most 30s from now.
347- // this.ready = false; Should we assume core is invalid if we encountered errors refreshing from an epoch change?
348- const { message = '' } = err as
349- | Error
350- | NodeClientErrorV0
351- | NodeClientErrorV1 ;
352- logError (
353- 'Error while attempting to reconnect to nodes after epoch transition:' ,
354- message
342+ } else {
343+ // In case of centralised networks, we don't run `connect()` flow, so we will manually update epochInfo here
344+ this . _epochState = await this . _fetchCurrentEpochState (
345+ validatorData . epochInfo
355346 ) ;
356347 }
357348 }
349+ } catch ( err : unknown ) {
350+ // Ensure that any methods that check `this.ready` throw errors to the caller, and any consumers can check appropriately
351+ this . ready = false ;
352+
353+ const { message = '' } = err as
354+ | Error
355+ | NodeClientErrorV0
356+ | NodeClientErrorV1 ;
357+ logError (
358+ 'Error while attempting to reconnect to nodes after epoch transition:' ,
359+ message
360+ ) ;
361+
362+ const handshakeError = new Error (
363+ 'Error while attempting to reconnect to nodes after epoch transition:' +
364+ message
365+ ) ;
366+
367+ // Signal to any listeners that we've encountered a fatal error
368+ this . emit ( 'error' , handshakeError ) ;
369+
370+ // Signal to any listeners that we're 'disconnected' from LIT network
371+ this . emit ( 'disconnected' , { reason : 'error' , error : handshakeError } ) ;
358372 }
359373 }
360374
@@ -399,6 +413,7 @@ export class LitCore {
399413 this . _stopListeningForNewEpoch ( ) ;
400414 // this._stopNetworkPolling();
401415 setMiscLitConfig ( undefined ) ;
416+ this . emit ( 'disconnected' , { reason : 'disconnect' } ) ;
402417 }
403418
404419 // _stopNetworkPolling() {
@@ -477,11 +492,6 @@ export class LitCore {
477492 }
478493
479494 private async _connect ( ) {
480- // Ensure an ill-timed epoch change event doesn't trigger concurrent config changes while we're already doing that
481- this . _stopListeningForNewEpoch ( ) ;
482- // Ensure we don't fire an existing network sync poll handler while we're in the midst of connecting anyway
483- // this._stopNetworkPolling();
484-
485495 // Initialize a contractContext if we were not given one; this allows interaction against the staking contract
486496 // to be handled locally from then on
487497 if ( ! this . config . contractContext ) {
@@ -523,7 +533,6 @@ export class LitCore {
523533 }
524534
525535 // Re-use staking contract instance from previous connect() executions that succeeded to improve performance
526- // noinspection ES6MissingAwait - intentionally not `awaiting` so we can run this in parallel below
527536 const validatorData = await this . _getValidatorData ( ) ;
528537
529538 this . _stakingContract = validatorData . stakingContract ;
@@ -554,6 +563,8 @@ export class LitCore {
554563 latestBlockhash : this . latestBlockhash ,
555564 } ) ;
556565
566+ this . emit ( 'connected' , true ) ;
567+
557568 // browser only
558569 if ( isBrowser ( ) ) {
559570 document . dispatchEvent ( new Event ( 'lit-ready' ) ) ;
0 commit comments