@@ -6,9 +6,12 @@ import type {
6
6
} from "@ignored/edr-optimism" ;
7
7
8
8
import { LogKind , CallKind } from "@ignored/edr-optimism" ;
9
+ import { assertHardhatInvariant } from "@nomicfoundation/hardhat-errors" ;
9
10
import { bytesToHexString } from "@nomicfoundation/hardhat-utils/hex" ;
10
11
import chalk from "chalk" ;
11
12
13
+ type NestedArray < T > = Array < T | NestedArray < T > > ;
14
+
12
15
export function formatArtifactId (
13
16
artifactId : ArtifactId ,
14
17
sourceNameToUserSourceName : Map < string , string > ,
@@ -25,51 +28,68 @@ export function formatLogs(logs: string[], indent: number): string {
25
28
) ;
26
29
}
27
30
28
- export function formatInputs ( inputs : DecodedTraceParameters | Uint8Array ) : string {
31
+ export function formatInputs (
32
+ inputs : DecodedTraceParameters | Uint8Array ,
33
+ color ?: ( text : string ) => string ,
34
+ ) : string | undefined {
29
35
if ( inputs instanceof Uint8Array ) {
30
- return bytesToHexString ( inputs ) ;
36
+ return inputs . length > 0 ? bytesToHexString ( inputs ) : undefined ;
31
37
} else {
32
- return `${ inputs . name } (${ inputs . arguments . join ( ", " ) } )` ;
38
+ const formattedName =
39
+ color !== undefined ? color ( inputs . name ) : inputs . name ;
40
+ return `${ formattedName } (${ inputs . arguments . join ( ", " ) } )` ;
33
41
}
34
42
}
35
43
36
- function formatOutputs ( outputs : string | Uint8Array ) : string {
44
+ function formatOutputs ( outputs : string | Uint8Array ) : string | undefined {
37
45
if ( outputs instanceof Uint8Array ) {
38
- return bytesToHexString ( outputs ) ;
46
+ return outputs . length > 0 ? bytesToHexString ( outputs ) : undefined ;
39
47
} else {
40
48
return outputs ;
41
49
}
42
50
}
43
51
44
- function formatLog ( log : LogTrace , indent : number ) : string {
52
+ function formatLog ( log : LogTrace ) : string [ ] {
45
53
const { parameters } = log ;
54
+ const lines = [ ] ;
46
55
if ( Array . isArray ( parameters ) ) {
47
- const topics = parameters
48
- . slice ( 0 , parameters . length - 1 )
49
- . map ( ( topic ) => bytesToHexString ( topic ) ) ;
50
- const data = bytesToHexString ( parameters [ parameters . length - 1 ] ) ;
51
- return `${ " " . repeat ( indent ) } ${ chalk . grey ( `(topics: [${ topics . join ( ", " ) } ], data: ${ data } )` ) } ` ;
56
+ const topics = parameters . map ( ( topic ) => bytesToHexString ( topic ) ) ;
57
+ if ( topics . length > 0 ) {
58
+ lines . push ( `emit topic 0: ${ chalk . cyan ( topics [ 0 ] ) } ` ) ;
59
+ }
60
+ for ( let i = 1 ; i < topics . length - 1 ; i ++ ) {
61
+ lines . push ( ` topic ${ i } : ${ chalk . cyan ( topics [ i ] ) } ` ) ;
62
+ }
63
+ if ( topics . length > 1 ) {
64
+ lines . push ( ` data: ${ chalk . cyan ( topics [ topics . length - 1 ] ) } ` ) ;
65
+ }
52
66
} else {
53
- return `${ " " . repeat ( indent ) } ${ parameters . name } (${ parameters . arguments . join ( ", " ) } )` ;
67
+ lines . push (
68
+ `emit ${ parameters . name } (chalk.cyan(${ parameters . arguments . join ( ", " ) } ))` ,
69
+ ) ;
54
70
}
71
+ return lines ;
55
72
}
56
73
57
- function formatKind ( kind : CallKind ) : string {
74
+ function formatKind ( kind : CallKind ) : string | undefined {
75
+ assertHardhatInvariant (
76
+ kind !== CallKind . Create ,
77
+ "Unexpected call kind 'Create'" ,
78
+ ) ;
79
+
58
80
switch ( kind ) {
59
81
case CallKind . Call :
60
- return "Call" ;
82
+ return undefined ;
61
83
case CallKind . CallCode :
62
- return "CallCode " ;
84
+ return "callcode " ;
63
85
case CallKind . DelegateCall :
64
- return "DelegateCall " ;
86
+ return "delegatecall " ;
65
87
case CallKind . StaticCall :
66
- return "StaticCall" ;
67
- case CallKind . Create :
68
- return "Create" ;
88
+ return "staticcall" ;
69
89
}
70
90
}
71
91
72
- function formatTrace ( trace : CallTrace , indent : number ) : string {
92
+ function formatTrace ( trace : CallTrace ) : NestedArray < string > {
73
93
const {
74
94
success,
75
95
contract,
@@ -80,22 +100,91 @@ function formatTrace(trace: CallTrace, indent: number): string {
80
100
isCheatcode,
81
101
outputs,
82
102
} = trace ;
83
- const color = success ? chalk . blue : chalk . yellow ;
84
- const sign = success ? "✔" : "✘" ;
85
- const label = success ? "Succeeded" : "Failed" ;
86
- const lines = [
87
- `${ " " . repeat ( indent ) } ${ color ( `${ sign } ${ formatKind ( kind ) } ${ label } ` ) } : ${ contract } ::${ formatInputs ( inputs ) } → ${ formatOutputs ( outputs ) } ${ chalk . grey ( `(gas: ${ gasUsed } , tokens: ${ value } , cheatcode: ${ isCheatcode } )` ) } ` ,
88
- ] ;
103
+ let color ;
104
+ if ( isCheatcode ) {
105
+ color = chalk . blue ;
106
+ } else if ( success ) {
107
+ color = chalk . green ;
108
+ } else {
109
+ color = chalk . red ;
110
+ }
111
+
112
+ const formattedInputs = formatInputs ( inputs , color ) ;
113
+ const formattedOutputs = formatOutputs ( outputs ) ;
114
+
115
+ let openingLine : string ;
116
+ let closingLine : string | undefined ;
117
+ if ( kind === CallKind . Create ) {
118
+ openingLine = `[${ gasUsed } ] ${ chalk . yellow ( "→ new" ) } ${ contract } ` ;
119
+ if ( formattedInputs !== undefined ) {
120
+ openingLine = `${ openingLine } @${ formattedInputs } ` ;
121
+ }
122
+ } else {
123
+ const formattedKind = formatKind ( kind ) ;
124
+ openingLine = `[${ gasUsed } ] ${ color ( contract ) } ` ;
125
+ if ( formattedInputs !== undefined ) {
126
+ openingLine = `${ openingLine } ::${ formattedInputs } ` ;
127
+ }
128
+ if ( value !== BigInt ( 0 ) ) {
129
+ openingLine = `${ openingLine } {value: ${ value } }` ;
130
+ }
131
+ if ( formattedKind !== undefined ) {
132
+ openingLine = `${ openingLine } ${ chalk . yellow ( `[${ formattedKind } ]` ) } ` ;
133
+ }
134
+ }
135
+ if ( formattedOutputs !== undefined ) {
136
+ closingLine = `${ color ( "←" ) } ${ formattedOutputs } ` ;
137
+ }
138
+
139
+ const lines = [ ] ;
140
+ lines . push ( openingLine ) ;
89
141
for ( const child of trace . children ) {
90
142
if ( child . kind === LogKind . Log ) {
91
- lines . push ( formatLog ( child , indent + 2 ) ) ;
143
+ lines . push ( formatLog ( child ) ) ;
92
144
} else {
93
- lines . push ( formatTrace ( child , indent + 2 ) ) ;
145
+ lines . push ( formatTrace ( child ) ) ;
146
+ }
147
+ }
148
+ if ( closingLine !== undefined ) {
149
+ lines . push ( [ closingLine ] ) ;
150
+ }
151
+ return lines ;
152
+ }
153
+
154
+ function formatNestedArray (
155
+ data : NestedArray < string > ,
156
+ prefix = "" ,
157
+ isTopLevel = true ,
158
+ ) : string {
159
+ let output = "" ;
160
+
161
+ for ( let i = 0 ; i < data . length ; i ++ ) {
162
+ const item = data [ i ] ;
163
+
164
+ if ( Array . isArray ( item ) && typeof item [ 0 ] === "string" ) {
165
+ const [ label , ...children ] = item ;
166
+
167
+ if ( isTopLevel ) {
168
+ output += `${ prefix } ${ label } \n` ;
169
+ output += formatNestedArray ( children , prefix , false ) ;
170
+ } else {
171
+ const isLast = i === data . length - 1 ;
172
+ const connector = isLast ? " └─ " : " ├─ " ;
173
+ const childPrefix = isLast ? " " : " | " ;
174
+ output += `${ prefix } ${ connector } ${ label } \n` ;
175
+ output += formatNestedArray ( children , prefix + childPrefix , false ) ;
176
+ }
177
+ } else if ( typeof item === "string" ) {
178
+ const isLast = i === data . length - 1 ;
179
+ const connector = isLast ? " └─ " : " ├─ " ;
180
+ output += `${ prefix } ${ connector } ${ item } \n` ;
94
181
}
95
182
}
96
- return lines . join ( "\n" ) ;
183
+
184
+ return output ;
97
185
}
98
186
99
187
export function formatTraces ( traces : CallTrace [ ] , indent : number ) : string {
100
- return traces . map ( ( trace ) => formatTrace ( trace , indent ) ) . join ( "\n" ) ;
188
+ const lines = traces . map ( formatTrace ) ;
189
+ return formatNestedArray ( lines , " " . repeat ( indent ) ) ;
101
190
}
0 commit comments