@@ -167,6 +167,9 @@ export class EventManager {
167167export class EventParser {
168168 private coder : Coder ;
169169 private programId : PublicKey ;
170+ private static readonly INVOKE_RE =
171+ / ^ P r o g r a m ( [ 1 - 9 A - H J - N P - Z a - k m - z ] + ) i n v o k e \[ ( \d + ) \] $ / ;
172+ private static readonly ROOT_DEPTH = "1" ;
170173
171174 constructor ( programId : PublicKey , coder : Coder ) {
172175 this . coder = coder ;
@@ -184,26 +187,43 @@ export class EventParser {
184187 // its emission, thereby allowing us to know if a given log event was
185188 // emitted by *this* program. If it was, then we parse the raw string and
186189 // emit the event if the string matches the event being subscribed to.
187- public * parseLogs ( logs : string [ ] , errorOnDecodeFailure : boolean = false ) {
188- const logScanner = new LogScanner ( logs ) ;
190+ public * parseLogs (
191+ logs : string [ ] ,
192+ errorOnDecodeFailure = false
193+ ) : Generator < Event > {
194+ const scanner = new LogScanner ( [ ...logs ] ) ;
189195 const execution = new ExecutionContext ( ) ;
190- let log = logScanner . next ( ) ;
191- while ( log !== null ) {
196+
197+ const firstLog = scanner . next ( ) ;
198+ if ( firstLog === null ) return ;
199+
200+ const firstCap = EventParser . INVOKE_RE . exec ( firstLog ) ;
201+ if ( ! firstCap || firstCap [ 2 ] !== EventParser . ROOT_DEPTH ) {
202+ throw new Error ( `Unexpected first log line: ${ firstLog } ` ) ;
203+ }
204+ execution . push ( firstCap [ 1 ] ) ;
205+
206+ while ( scanner . peek ( ) !== null ) {
207+ const log = scanner . next ( ) ;
208+ if ( log === null ) break ;
209+
192210 let [ event , newProgram , didPop ] = this . handleLog (
193211 execution ,
194212 log ,
195213 errorOnDecodeFailure
196214 ) ;
197- if ( event ) {
198- yield event ;
199- }
200- if ( newProgram ) {
201- execution . push ( newProgram ) ;
202- }
215+
216+ if ( event ) yield event ;
217+ if ( newProgram ) execution . push ( newProgram ) ;
218+
203219 if ( didPop ) {
204220 execution . pop ( ) ;
221+ const nextLog = scanner . peek ( ) ;
222+ if ( nextLog && nextLog . endsWith ( "invoke [1]" ) ) {
223+ const m = EventParser . INVOKE_RE . exec ( nextLog ) ;
224+ if ( m ) execution . push ( m [ 1 ] ) ;
225+ }
205226 }
206- log = logScanner . next ( ) ;
207227 }
208228 }
209229
@@ -254,23 +274,17 @@ export class EventParser {
254274
255275 // Handles logs when the current program being executing is *not* this.
256276 private handleSystemLog ( log : string ) : [ string | null , boolean ] {
257- // System component.
258- const logStart = log . split ( ":" ) [ 0 ] ;
259-
260- // Did the program finish executing?
261- if ( logStart . match ( / ^ P r o g r a m ( .* ) s u c c e s s / g) !== null ) {
262- return [ null , true ] ;
263- // Recursive call.
264- } else if (
265- logStart . startsWith ( `Program ${ this . programId . toString ( ) } invoke` )
266- ) {
277+ if ( log . startsWith ( `Program ${ this . programId . toString ( ) } log:` ) ) {
267278 return [ this . programId . toString ( ) , false ] ;
268- }
269- // CPI call.
270- else if ( logStart . includes ( "invoke" ) ) {
271- return [ "cpi" , false ] ; // Any string will do.
279+ } else if ( log . includes ( "invoke" ) && ! log . endsWith ( "[1]" ) ) {
280+ return [ "cpi" , false ] ;
272281 } else {
273- return [ null , false ] ;
282+ let regex = / ^ P r o g r a m ( [ 1 - 9 A - H J - N P - Z a - k m - z ] + ) s u c c e s s $ / ;
283+ if ( regex . test ( log ) ) {
284+ return [ null , true ] ;
285+ } else {
286+ return [ null , false ] ;
287+ }
274288 }
275289 }
276290}
@@ -300,7 +314,12 @@ class ExecutionContext {
300314}
301315
302316class LogScanner {
303- constructor ( public logs : string [ ] ) { }
317+ constructor ( public logs : string [ ] ) {
318+ // remove any logs that don't start with "Program "
319+ // this can happen in loader logs.
320+ // e.g. 3psYALQ9s7SjdezXw2kxKkVuQLtSAQxPAjETvy765EVxJE7cYqfc4oGbpNYEWsAiuXuTnqcsSUHLQ3iZUenTHTsA on devnet
321+ this . logs = this . logs . filter ( ( log ) => log . startsWith ( "Program " ) ) ;
322+ }
304323
305324 next ( ) : string | null {
306325 if ( this . logs . length === 0 ) {
@@ -310,4 +329,8 @@ class LogScanner {
310329 this . logs = this . logs . slice ( 1 ) ;
311330 return l ;
312331 }
332+
333+ peek ( ) : string | null {
334+ return this . logs . length > 0 ? this . logs [ 0 ] : null ;
335+ }
313336}
0 commit comments