1
- import type { ArtifactId } from "@ignored/edr-optimism" ;
1
+ import type {
2
+ LogTrace ,
3
+ ArtifactId ,
4
+ CallTrace ,
5
+ DecodedTraceParameters ,
6
+ } from "@ignored/edr-optimism" ;
2
7
8
+ import { LogKind , CallKind } from "@ignored/edr-optimism" ;
9
+ import { assertHardhatInvariant } from "@nomicfoundation/hardhat-errors" ;
10
+ import { bytesToHexString } from "@nomicfoundation/hardhat-utils/hex" ;
3
11
import chalk from "chalk" ;
4
12
13
+ type NestedArray < T > = Array < T | NestedArray < T > > ;
14
+
5
15
export function formatArtifactId (
6
16
artifactId : ArtifactId ,
7
17
sourceNameToUserSourceName : Map < string , string > ,
@@ -11,3 +21,181 @@ export function formatArtifactId(
11
21
12
22
return `${ chalk . bold ( `${ sourceName } :${ artifactId . name } ` ) } (v${ artifactId . solcVersion } )` ;
13
23
}
24
+
25
+ export function formatLogs ( logs : string [ ] , indent : number ) : string {
26
+ return chalk . grey (
27
+ logs . map ( ( log ) => `${ " " . repeat ( indent ) } ${ log } ` ) . join ( "\n" ) ,
28
+ ) ;
29
+ }
30
+
31
+ function formatInputs (
32
+ inputs : DecodedTraceParameters | Uint8Array ,
33
+ color ?: ( text : string ) => string ,
34
+ ) : string | undefined {
35
+ if ( inputs instanceof Uint8Array ) {
36
+ return inputs . length > 0 ? bytesToHexString ( inputs ) : undefined ;
37
+ } else {
38
+ const formattedName =
39
+ color !== undefined ? color ( inputs . name ) : inputs . name ;
40
+ return `${ formattedName } (${ inputs . arguments . join ( ", " ) } )` ;
41
+ }
42
+ }
43
+
44
+ function formatOutputs ( outputs : string | Uint8Array ) : string | undefined {
45
+ if ( outputs instanceof Uint8Array ) {
46
+ return outputs . length > 0 ? bytesToHexString ( outputs ) : undefined ;
47
+ } else {
48
+ return outputs ;
49
+ }
50
+ }
51
+
52
+ function formatLog ( log : LogTrace ) : string [ ] {
53
+ const { parameters } = log ;
54
+ const lines = [ ] ;
55
+ if ( Array . isArray ( parameters ) ) {
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
+ }
66
+ } else {
67
+ lines . push (
68
+ `emit ${ parameters . name } (${ chalk . cyan ( parameters . arguments . join ( ", " ) ) } )` ,
69
+ ) ;
70
+ }
71
+ return lines ;
72
+ }
73
+
74
+ function formatKind ( kind : CallKind ) : string | undefined {
75
+ assertHardhatInvariant (
76
+ kind !== CallKind . Create ,
77
+ "Unexpected call kind 'Create'" ,
78
+ ) ;
79
+
80
+ switch ( kind ) {
81
+ case CallKind . Call :
82
+ return undefined ;
83
+ case CallKind . CallCode :
84
+ return "callcode" ;
85
+ case CallKind . DelegateCall :
86
+ return "delegatecall" ;
87
+ case CallKind . StaticCall :
88
+ return "staticcall" ;
89
+ }
90
+ }
91
+
92
+ function formatTrace ( trace : CallTrace ) : NestedArray < string > {
93
+ const {
94
+ success,
95
+ contract,
96
+ inputs,
97
+ gasUsed,
98
+ value,
99
+ kind,
100
+ isCheatcode,
101
+ outputs,
102
+ } = trace ;
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
+ // TODO: Uncomment this when the formattedInputs starts containing
120
+ // the address of where the contract was deployed instead of the code.
121
+ // if (formattedInputs !== undefined) {
122
+ // openingLine = `${openingLine}@${formattedInputs}`;
123
+ // }
124
+ } else {
125
+ const formattedKind = formatKind ( kind ) ;
126
+ openingLine = `[${ gasUsed } ] ${ color ( contract ) } ` ;
127
+ if ( formattedInputs !== undefined ) {
128
+ openingLine = `${ openingLine } ::${ formattedInputs } ` ;
129
+ }
130
+ if ( value !== BigInt ( 0 ) ) {
131
+ openingLine = `${ openingLine } {value: ${ value } }` ;
132
+ }
133
+ if ( formattedKind !== undefined ) {
134
+ openingLine = `${ openingLine } ${ chalk . yellow ( `[${ formattedKind } ]` ) } ` ;
135
+ }
136
+ }
137
+ if ( formattedOutputs !== undefined ) {
138
+ if (
139
+ formattedOutputs === "EvmError: Revert" ||
140
+ formattedOutputs . startsWith ( "revert:" )
141
+ ) {
142
+ closingLine = `${ color ( "←" ) } ${ color ( "[Revert]" ) } ${ formattedOutputs } ` ;
143
+ } else {
144
+ closingLine = `${ color ( "←" ) } ${ formattedOutputs } ` ;
145
+ }
146
+ }
147
+
148
+ const lines = [ ] ;
149
+ lines . push ( openingLine ) ;
150
+ for ( const child of trace . children ) {
151
+ if ( child . kind === LogKind . Log ) {
152
+ lines . push ( formatLog ( child ) ) ;
153
+ } else {
154
+ lines . push ( formatTrace ( child ) ) ;
155
+ }
156
+ }
157
+ if ( closingLine !== undefined ) {
158
+ lines . push ( [ closingLine ] ) ;
159
+ }
160
+ return lines ;
161
+ }
162
+
163
+ function formatNestedArray (
164
+ data : NestedArray < string > ,
165
+ prefix = "" ,
166
+ isTopLevel = true ,
167
+ ) : string {
168
+ let output = "" ;
169
+
170
+ for ( let i = 0 ; i < data . length ; i ++ ) {
171
+ const item = data [ i ] ;
172
+
173
+ if ( Array . isArray ( item ) && typeof item [ 0 ] === "string" ) {
174
+ const [ label , ...children ] = item ;
175
+
176
+ if ( isTopLevel ) {
177
+ output += `${ prefix } ${ label } \n` ;
178
+ output += formatNestedArray ( children , prefix , false ) ;
179
+ } else {
180
+ const isLast = i === data . length - 1 ;
181
+ const connector = isLast ? " └─ " : " ├─ " ;
182
+ const childPrefix = isLast ? " " : " | " ;
183
+ output += `${ prefix } ${ connector } ${ label } \n` ;
184
+ output += formatNestedArray ( children , prefix + childPrefix , false ) ;
185
+ }
186
+ } else if ( typeof item === "string" ) {
187
+ const isLast = i === data . length - 1 ;
188
+ const connector = isLast ? " └─ " : " ├─ " ;
189
+ output += `${ prefix } ${ connector } ${ item } \n` ;
190
+ }
191
+ }
192
+
193
+ return output ;
194
+ }
195
+
196
+ export function formatTraces ( traces : CallTrace [ ] , indent : number ) : string {
197
+ const lines = traces . map ( formatTrace ) ;
198
+ const formattedTraces = formatNestedArray ( lines , " " . repeat ( indent ) ) ;
199
+ // Remove the trailing newline
200
+ return formattedTraces . slice ( 0 , - 1 ) ;
201
+ }
0 commit comments