11import { Program , Event } from '@coral-xyz/anchor' ;
2+ import { CuUsageEvent } from './types' ;
23
34const driftProgramId = 'dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH' ;
45const PROGRAM_LOG = 'Program log: ' ;
6+ const PROGRAM_INSTRUCTION = 'Program log: Instruction: ' ;
57const PROGRAM_DATA = 'Program data: ' ;
68const PROGRAM_LOG_START_INDEX = PROGRAM_LOG . length ;
79const PROGRAM_DATA_START_INDEX = PROGRAM_DATA . length ;
10+ const PROGRAM_INSTRUCTION_START_INDEX = PROGRAM_INSTRUCTION . length ;
811
912export function parseLogs (
1013 program : Program ,
@@ -112,6 +115,7 @@ function handleSystemLog(
112115// executing for a given log.
113116class ExecutionContext {
114117 stack : string [ ] = [ ] ;
118+ ixStack : string [ ] = [ ] ;
115119
116120 program ( ) : string {
117121 if ( ! this . stack . length ) {
@@ -130,4 +134,115 @@ class ExecutionContext {
130134 }
131135 this . stack . pop ( ) ;
132136 }
137+
138+ ix ( ) : string {
139+ if ( ! this . ixStack . length ) {
140+ throw new Error ( 'Expected the ix stack to have elements' ) ;
141+ }
142+ return this . ixStack [ this . ixStack . length - 1 ] ;
143+ }
144+
145+ pushIx ( newIx : string ) {
146+ this . ixStack . push ( newIx ) ;
147+ }
148+
149+ popIx ( ) {
150+ this . ixStack . pop ( ) ;
151+ }
152+ }
153+
154+ export function parseLogsForCuUsage (
155+ logs : string [ ] ,
156+ programId = driftProgramId
157+ ) : Event < CuUsageEvent > [ ] {
158+ const cuUsageEvents : Event < CuUsageEvent > [ ] = [ ] ;
159+
160+ const execution = new ExecutionContext ( ) ;
161+ for ( const log of logs ) {
162+ if ( log . startsWith ( 'Log truncated' ) ) {
163+ break ;
164+ }
165+
166+ const [ newProgram , newIx , didPopProgram , didPopIx ] = handleLogForCuUsage (
167+ execution ,
168+ log ,
169+ programId
170+ ) ;
171+ if ( newProgram ) {
172+ execution . push ( newProgram ) ;
173+ }
174+ if ( newIx ) {
175+ execution . pushIx ( newIx ) ;
176+ }
177+ if ( didPopProgram ) {
178+ execution . pop ( ) ;
179+ }
180+ if ( didPopIx !== null ) {
181+ cuUsageEvents . push ( {
182+ name : 'CuUsage' ,
183+ data : {
184+ instruction : execution . ix ( ) ,
185+ cuUsage : didPopIx ! ,
186+ } ,
187+ } as any ) ;
188+ execution . popIx ( ) ;
189+ }
190+ }
191+ return cuUsageEvents ;
192+ }
193+
194+ function handleLogForCuUsage (
195+ execution : ExecutionContext ,
196+ log : string ,
197+ programId = driftProgramId
198+ ) : [ string | null , string | null , boolean , number | null ] {
199+ if ( execution . stack . length > 0 && execution . program ( ) === programId ) {
200+ return handleProgramLogForCuUsage ( log , programId ) ;
201+ } else {
202+ return handleSystemLogForCuUsage ( log , programId ) ;
203+ }
204+ }
205+
206+ function handleProgramLogForCuUsage (
207+ log : string ,
208+ programId = driftProgramId
209+ ) : [ string | null , string | null , boolean , number | null ] {
210+ if ( log . startsWith ( PROGRAM_INSTRUCTION ) ) {
211+ const ixStr = log . slice ( PROGRAM_INSTRUCTION_START_INDEX ) ;
212+ return [ null , ixStr , false , null ] ;
213+ } else {
214+ return handleSystemLogForCuUsage ( log , programId ) ;
215+ }
216+ }
217+
218+ function handleSystemLogForCuUsage (
219+ log : string ,
220+ programId = driftProgramId
221+ ) : [ string | null , string | null , boolean , number | null ] {
222+ // System component.
223+ const logStart = log . split ( ':' ) [ 0 ] ;
224+ const programStart = `Program ${ programId } invoke` ;
225+
226+ // Did the program finish executing?
227+ if ( logStart . match ( / ^ P r o g r a m ( .* ) s u c c e s s / g) !== null ) {
228+ return [ null , null , true , null ] ;
229+ // Recursive call.
230+ } else if ( logStart . startsWith ( programStart ) ) {
231+ return [ programId , null , false , null ] ;
232+ // Consumed CU log.
233+ } else if ( log . startsWith ( `Program ${ programId } consumed ` ) ) {
234+ // Extract CU usage, e.g. 'Program ... consumed 29242 of 199700 compute units'
235+ // We need to extract the consumed value (29242)
236+ const matches = log . match ( / c o n s u m e d ( \d + ) o f \d + c o m p u t e u n i t s / ) ;
237+ if ( matches ) {
238+ return [ null , null , false , Number ( matches [ 1 ] ) ] ;
239+ }
240+ return [ null , null , false , null ] ;
241+ }
242+ // CPI call.
243+ else if ( logStart . includes ( 'invoke' ) ) {
244+ return [ 'cpi' , null , false , null ] ; // Any string will do.
245+ } else {
246+ return [ null , null , false , null ] ;
247+ }
133248}
0 commit comments