@nomicfoundation/edr 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,180 +0,0 @@
1
- //! This file includes Solidity tracing heuristics for solc starting with
2
- //! version 0.6.9.
3
- //!
4
- //! This solc version introduced a significant change to how sourcemaps are
5
- //! handled for inline yul/internal functions. These were mapped to the
6
- //! unmapped/-1 file before, which lead to many unmapped reverts. Now, they are
7
- //! mapped to the part of the Solidity source that lead to their inlining.
8
- //!
9
- //! This change is a very positive change, as errors would point to the correct
10
- //! line by default. The only problem is that we used to rely very heavily on
11
- //! unmapped reverts to decide when our error detection heuristics were to be
12
- //! run. In fact, these heuristics were first introduced because of unmapped
13
- //! reverts.
14
- //!
15
- //! Instead of synthetically completing stack traces when unmapped reverts
16
- //! occur, we now start from complete stack traces and adjust them if we can
17
- //! provide more meaningful errors.
18
-
19
- use edr_evm::interpreter::OpCode;
20
- use napi::{
21
- bindgen_prelude::{Either24, Either4},
22
- Either,
23
- };
24
- use semver::Version;
25
-
26
- use super::{
27
- message_trace::{CallMessageTrace, CreateMessageTrace, EvmStep},
28
- solidity_stack_trace::{
29
- InvalidParamsErrorStackTraceEntry, NonContractAccountCalledErrorStackTraceEntry,
30
- RevertErrorStackTraceEntry, SolidityStackTrace, StackTraceEntryTypeConst,
31
- },
32
- };
33
-
34
- const FIRST_SOLC_VERSION_WITH_MAPPED_SMALL_INTERNAL_FUNCTIONS: Version = Version::new(0, 6, 9);
35
-
36
- pub fn stack_trace_may_require_adjustments(
37
- stacktrace: &SolidityStackTrace,
38
- decoded_trace: Either<&CallMessageTrace, &CreateMessageTrace>,
39
- ) -> bool {
40
- let bytecode = match &decoded_trace {
41
- Either::A(create) => &create.bytecode,
42
- Either::B(call) => &call.bytecode,
43
- };
44
- let bytecode = bytecode.as_ref().expect("JS code asserts");
45
-
46
- let Some(last_frame) = stacktrace.last() else {
47
- return false;
48
- };
49
-
50
- if let Either24::E(last_frame @ RevertErrorStackTraceEntry { .. }) = last_frame {
51
- return !last_frame.is_invalid_opcode_error
52
- && last_frame.return_data.is_empty()
53
- && Version::parse(&bytecode.compiler_version)
54
- .map(|version| version >= FIRST_SOLC_VERSION_WITH_MAPPED_SMALL_INTERNAL_FUNCTIONS)
55
- .unwrap_or(false);
56
- }
57
-
58
- false
59
- }
60
-
61
- pub fn adjust_stack_trace(
62
- mut stacktrace: SolidityStackTrace,
63
- decoded_trace: Either<&CallMessageTrace, &CreateMessageTrace>,
64
- ) -> napi::Result<SolidityStackTrace> {
65
- let Some(Either24::E(revert @ RevertErrorStackTraceEntry { .. })) = stacktrace.last() else {
66
- unreachable!("JS code asserts that; it's only used immediately after we check with `stack_trace_may_require_adjustments` that the last frame is a revert frame");
67
- };
68
-
69
- // Replace the last revert frame with an adjusted frame if needed
70
- if is_non_contract_account_called_error(decoded_trace)? {
71
- let last_revert_frame_source_reference = revert.source_reference.clone();
72
- stacktrace.pop();
73
- stacktrace.push(
74
- NonContractAccountCalledErrorStackTraceEntry {
75
- type_: StackTraceEntryTypeConst,
76
- source_reference: last_revert_frame_source_reference,
77
- }
78
- .into(),
79
- );
80
- return Ok(stacktrace);
81
- }
82
-
83
- if is_constructor_invalid_params_error(decoded_trace)? {
84
- let last_revert_frame_source_reference = revert.source_reference.clone();
85
- stacktrace.pop();
86
- stacktrace.push(
87
- InvalidParamsErrorStackTraceEntry {
88
- type_: StackTraceEntryTypeConst,
89
- source_reference: last_revert_frame_source_reference,
90
- }
91
- .into(),
92
- );
93
- return Ok(stacktrace);
94
- }
95
-
96
- if is_call_invalid_params_error(decoded_trace)? {
97
- let last_revert_frame_source_reference = revert.source_reference.clone();
98
- stacktrace.pop();
99
- stacktrace.push(
100
- InvalidParamsErrorStackTraceEntry {
101
- type_: StackTraceEntryTypeConst,
102
- source_reference: last_revert_frame_source_reference,
103
- }
104
- .into(),
105
- );
106
-
107
- return Ok(stacktrace);
108
- }
109
-
110
- Ok(stacktrace)
111
- }
112
-
113
- fn is_non_contract_account_called_error(
114
- decoded_trace: Either<&CallMessageTrace, &CreateMessageTrace>,
115
- ) -> napi::Result<bool> {
116
- match_opcodes(
117
- decoded_trace,
118
- -9,
119
- &[
120
- OpCode::EXTCODESIZE,
121
- OpCode::ISZERO,
122
- OpCode::DUP1,
123
- OpCode::ISZERO,
124
- ],
125
- )
126
- }
127
-
128
- fn is_constructor_invalid_params_error(
129
- decoded_trace: Either<&CallMessageTrace, &CreateMessageTrace>,
130
- ) -> napi::Result<bool> {
131
- Ok(match_opcodes(decoded_trace, -20, &[OpCode::CODESIZE])?
132
- && match_opcodes(decoded_trace, -15, &[OpCode::CODECOPY])?
133
- && match_opcodes(decoded_trace, -7, &[OpCode::LT, OpCode::ISZERO])?)
134
- }
135
-
136
- fn is_call_invalid_params_error(
137
- decoded_trace: Either<&CallMessageTrace, &CreateMessageTrace>,
138
- ) -> napi::Result<bool> {
139
- Ok(match_opcodes(decoded_trace, -11, &[OpCode::CALLDATASIZE])?
140
- && match_opcodes(decoded_trace, -7, &[OpCode::LT, OpCode::ISZERO])?)
141
- }
142
-
143
- fn match_opcodes(
144
- decoded_trace: Either<&CallMessageTrace, &CreateMessageTrace>,
145
- first_step_index: i32,
146
- opcodes: &[OpCode],
147
- ) -> napi::Result<bool> {
148
- let (bytecode, steps) = match &decoded_trace {
149
- Either::A(call) => (&call.bytecode, &call.steps),
150
- Either::B(create) => (&create.bytecode, &create.steps),
151
- };
152
- let bytecode = bytecode.as_ref().expect("JS code asserts");
153
-
154
- // If the index is negative, we start from the end of the trace,
155
- // just like in the original JS code
156
- let mut index = match first_step_index {
157
- 0.. => first_step_index as usize,
158
- ..=-1 if first_step_index.abs() < steps.len() as i32 => {
159
- (steps.len() as i32 + first_step_index) as usize
160
- }
161
- // Out of bounds
162
- _ => return Ok(false),
163
- };
164
-
165
- for opcode in opcodes {
166
- let Some(Either4::A(EvmStep { pc })) = steps.get(index) else {
167
- return Ok(false);
168
- };
169
-
170
- let instruction = bytecode.get_instruction(*pc)?;
171
-
172
- if instruction.opcode != *opcode {
173
- return Ok(false);
174
- }
175
-
176
- index += 1;
177
- }
178
-
179
- Ok(true)
180
- }
@@ -1,179 +0,0 @@
1
- //! Bridging type for the existing `MessageTrace` interface in Hardhat.
2
-
3
- use napi::{
4
- bindgen_prelude::{BigInt, ClassInstance, Either3, Either4, Uint8Array, Undefined},
5
- Either, Env,
6
- };
7
- use napi_derive::napi;
8
-
9
- use super::{exit::Exit, model::BytecodeWrapper};
10
-
11
- #[napi(object)]
12
- pub struct EvmStep {
13
- pub pc: u32,
14
- }
15
-
16
- #[napi(object)]
17
- pub struct PrecompileMessageTrace {
18
- // `BaseMessageTrace`
19
- pub value: BigInt,
20
- pub return_data: Uint8Array,
21
- pub exit: ClassInstance<Exit>,
22
- pub gas_used: BigInt,
23
- pub depth: u32,
24
- // `PrecompileMessageTrace`
25
- pub precompile: u32,
26
- pub calldata: Uint8Array,
27
- }
28
-
29
- // NOTE: Because of the hack below for `deployed_contract`, now the
30
- // `CallMessageTrace` is a strict superset of `CreateMessageTrace`, so we need
31
- // to take care to keep the order consistent from most-specific to
32
- // least-specific in the `Either{3,4}` type when converting to or from N-API.
33
- #[napi(object)]
34
- pub struct CreateMessageTrace {
35
- // `BaseMessageTrace`
36
- pub value: BigInt,
37
- pub return_data: Uint8Array,
38
- pub exit: ClassInstance<Exit>,
39
- pub gas_used: BigInt,
40
- pub depth: u32,
41
- // `BaseEvmMessageTrace`
42
- pub code: Uint8Array,
43
- pub steps: Vec<Either4<EvmStep, PrecompileMessageTrace, CallMessageTrace, CreateMessageTrace>>,
44
- /// Reference to the resolved `Bytecode` EDR data.
45
- /// Only used on the JS side by the `VmTraceDecoder` class.
46
- pub bytecode: Option<ClassInstance<BytecodeWrapper>>,
47
- pub number_of_subtraces: u32,
48
- // `CreateMessageTrace`
49
- // HACK: It seems that `Either<Uint8Array, Undefined>` means exactly what we
50
- // want (a required property but can be explicitly `undefined`) but internally
51
- // the napi-rs treats an encountered `Undefined` like a missing property
52
- // and it throws a validation error. While not 100% backwards compatible, we
53
- // work around using an optional type.
54
- // See https://github.com/napi-rs/napi-rs/issues/1986 for context on the PR
55
- // that introduced this behavior.
56
- pub deployed_contract: Option<Either<Uint8Array, Undefined>>,
57
- }
58
-
59
- #[napi(object)]
60
- pub struct CallMessageTrace {
61
- // `BaseMessageTrace`
62
- pub value: BigInt,
63
- pub return_data: Uint8Array,
64
- pub exit: ClassInstance<Exit>,
65
- pub gas_used: BigInt,
66
- pub depth: u32,
67
- // `BaseEvmMessageTrace`
68
- pub code: Uint8Array,
69
- pub steps: Vec<Either4<EvmStep, PrecompileMessageTrace, CallMessageTrace, CreateMessageTrace>>,
70
- /// Reference to the resolved `Bytecode` EDR data.
71
- /// Only used on the JS side by the `VmTraceDecoder` class.
72
- pub bytecode: Option<ClassInstance<BytecodeWrapper>>,
73
- pub number_of_subtraces: u32,
74
- // `CallMessageTrace`
75
- pub calldata: Uint8Array,
76
- pub address: Uint8Array,
77
- pub code_address: Uint8Array,
78
- }
79
-
80
- /// Converts [`edr_solidity::message_trace::MessageTraceStep`] to the N-API
81
- /// representation.
82
- ///
83
- /// # Panics
84
- /// This function will panic if the value is mutably borrowed.
85
- pub fn message_trace_step_to_napi(
86
- value: edr_solidity::message_trace::MessageTraceStep,
87
- env: Env,
88
- ) -> napi::Result<Either4<EvmStep, PrecompileMessageTrace, CallMessageTrace, CreateMessageTrace>> {
89
- Ok(match value {
90
- edr_solidity::message_trace::MessageTraceStep::Evm(step) => {
91
- Either4::A(EvmStep { pc: step.pc as u32 })
92
- }
93
- edr_solidity::message_trace::MessageTraceStep::Message(msg) => {
94
- // Immediately drop the borrow lock to err on the safe side as we
95
- // may be recursing.
96
- let owned = msg.borrow().clone();
97
- match message_trace_to_napi(owned, env)? {
98
- Either3::A(precompile) => Either4::B(precompile),
99
- Either3::B(call) => Either4::C(call),
100
- Either3::C(create) => Either4::D(create),
101
- }
102
- }
103
- })
104
- }
105
-
106
- /// Converts the Rust representation of a `MessageTrace` to the N-API
107
- /// representation.
108
- pub fn message_trace_to_napi(
109
- value: edr_solidity::message_trace::MessageTrace,
110
- env: Env,
111
- ) -> napi::Result<Either3<PrecompileMessageTrace, CallMessageTrace, CreateMessageTrace>> {
112
- Ok(match value {
113
- edr_solidity::message_trace::MessageTrace::Precompile(precompile) => {
114
- Either3::A(PrecompileMessageTrace {
115
- value: BigInt {
116
- sign_bit: false,
117
- words: precompile.base.value.as_limbs().to_vec(),
118
- },
119
- return_data: Uint8Array::from(precompile.base.return_data.as_ref()),
120
- exit: Exit(precompile.base.exit.into()).into_instance(env)?,
121
- gas_used: BigInt::from(precompile.base.gas_used),
122
- depth: precompile.base.depth as u32,
123
-
124
- precompile: precompile.precompile,
125
- calldata: Uint8Array::from(precompile.calldata.as_ref()),
126
- })
127
- }
128
- edr_solidity::message_trace::MessageTrace::Call(call) => Either3::B(CallMessageTrace {
129
- value: BigInt {
130
- sign_bit: false,
131
- words: call.base.base.value.as_limbs().to_vec(),
132
- },
133
- return_data: Uint8Array::from(call.base.base.return_data.as_ref()),
134
- exit: Exit(call.base.base.exit.into()).into_instance(env)?,
135
- gas_used: BigInt::from(call.base.base.gas_used),
136
- depth: call.base.base.depth as u32,
137
- code: Uint8Array::from(call.base.code.as_ref()),
138
- steps: call
139
- .base
140
- .steps
141
- .into_iter()
142
- .map(|step| message_trace_step_to_napi(step, env))
143
- .collect::<napi::Result<Vec<_>>>()?,
144
- // NOTE: We specifically use None as that will be later filled on the JS side
145
- bytecode: None,
146
- number_of_subtraces: call.base.number_of_subtraces,
147
-
148
- address: Uint8Array::from(call.address.as_slice()),
149
- calldata: Uint8Array::from(call.calldata.as_ref()),
150
- code_address: Uint8Array::from(call.code_address.as_slice()),
151
- }),
152
- edr_solidity::message_trace::MessageTrace::Create(create) => {
153
- Either3::C(CreateMessageTrace {
154
- value: BigInt {
155
- sign_bit: false,
156
- words: create.base.base.value.as_limbs().to_vec(),
157
- },
158
- return_data: Uint8Array::from(create.base.base.return_data.as_ref()),
159
- exit: Exit(create.base.base.exit.into()).into_instance(env)?,
160
- gas_used: BigInt::from(create.base.base.gas_used),
161
- depth: create.base.base.depth as u32,
162
- code: Uint8Array::from(create.base.code.as_ref()),
163
- steps: create
164
- .base
165
- .steps
166
- .into_iter()
167
- .map(|step| message_trace_step_to_napi(step, env))
168
- .collect::<napi::Result<Vec<_>>>()?,
169
- // NOTE: We specifically use None as that will be later filled on the JS side
170
- bytecode: None,
171
-
172
- number_of_subtraces: create.base.number_of_subtraces,
173
- deployed_contract: create
174
- .deployed_contract
175
- .map(|contract| Either::A(Uint8Array::from(contract.as_ref()))),
176
- })
177
- }
178
- })
179
- }
@@ -1,315 +0,0 @@
1
- use edr_evm::interpreter::OpCode;
2
- use edr_solidity::build_model::{Instruction, JumpType};
3
- use napi::{
4
- bindgen_prelude::{Either3, Either4},
5
- Either,
6
- };
7
- use napi_derive::napi;
8
-
9
- use super::{
10
- error_inferrer::{
11
- instruction_to_callstack_stack_trace_entry, ErrorInferrer, SubmessageDataRef,
12
- },
13
- mapped_inlined_internal_functions_heuristics::{
14
- adjust_stack_trace, stack_trace_may_require_adjustments,
15
- },
16
- message_trace::{CallMessageTrace, CreateMessageTrace, EvmStep, PrecompileMessageTrace},
17
- solidity_stack_trace::{PrecompileErrorStackTraceEntry, SolidityStackTrace},
18
- };
19
- use crate::trace::{
20
- exit::ExitCode,
21
- solidity_stack_trace::{
22
- ContractTooLargeErrorStackTraceEntry, SolidityStackTraceEntry, StackTraceEntryTypeConst,
23
- UnrecognizedContractCallstackEntryStackTraceEntry,
24
- UnrecognizedContractErrorStackTraceEntry, UnrecognizedCreateCallstackEntryStackTraceEntry,
25
- UnrecognizedCreateErrorStackTraceEntry,
26
- },
27
- };
28
-
29
- #[napi(constructor)]
30
- pub struct SolidityTracer;
31
-
32
- #[allow(clippy::unused_self)] // we allow this for convenience for now
33
- #[napi]
34
- impl SolidityTracer {
35
- #[napi(catch_unwind)]
36
- pub fn get_stack_trace(
37
- &self,
38
- trace: Either3<PrecompileMessageTrace, CallMessageTrace, CreateMessageTrace>,
39
- ) -> napi::Result<SolidityStackTrace> {
40
- let trace = match &trace {
41
- Either3::A(precompile) => Either3::A(precompile),
42
- Either3::B(call) => Either3::B(call),
43
- Either3::C(create) => Either3::C(create),
44
- };
45
-
46
- self.get_stack_trace_inner(trace)
47
- }
48
-
49
- pub fn get_stack_trace_inner(
50
- &self,
51
- trace: Either3<&PrecompileMessageTrace, &CallMessageTrace, &CreateMessageTrace>,
52
- ) -> napi::Result<SolidityStackTrace> {
53
- let exit = match &trace {
54
- Either3::A(precompile) => &precompile.exit,
55
- Either3::B(call) => &call.exit,
56
- Either3::C(create) => &create.exit,
57
- };
58
-
59
- if !exit.is_error() {
60
- return Ok(vec![]);
61
- }
62
-
63
- match trace {
64
- Either3::A(precompile) => Ok(self.get_precompile_message_stack_trace(precompile)?),
65
- Either3::B(call) if call.bytecode.is_some() => {
66
- Ok(self.get_call_message_stack_trace(call)?)
67
- }
68
- Either3::C(create) if create.bytecode.is_some() => {
69
- Ok(self.get_create_message_stack_trace(create)?)
70
- }
71
- // No bytecode is present
72
- Either3::B(call) => Ok(self.get_unrecognized_message_stack_trace(Either::A(call))?),
73
- Either3::C(create) => Ok(self.get_unrecognized_message_stack_trace(Either::B(create))?),
74
- }
75
- }
76
-
77
- fn get_last_subtrace<'a>(
78
- &self,
79
- trace: Either<&'a CallMessageTrace, &'a CreateMessageTrace>,
80
- ) -> Option<Either3<&'a PrecompileMessageTrace, &'a CallMessageTrace, &'a CreateMessageTrace>>
81
- {
82
- let (number_of_subtraces, steps) = match trace {
83
- Either::A(create) => (create.number_of_subtraces, &create.steps),
84
- Either::B(call) => (call.number_of_subtraces, &call.steps),
85
- };
86
-
87
- if number_of_subtraces == 0 {
88
- return None;
89
- }
90
-
91
- steps.iter().rev().find_map(|step| match step {
92
- Either4::A(EvmStep { .. }) => None,
93
- Either4::B(precompile) => Some(Either3::A(precompile)),
94
- Either4::C(call) => Some(Either3::B(call)),
95
- Either4::D(create) => Some(Either3::C(create)),
96
- })
97
- }
98
-
99
- fn get_precompile_message_stack_trace(
100
- &self,
101
- trace: &PrecompileMessageTrace,
102
- ) -> napi::Result<SolidityStackTrace> {
103
- Ok(vec![PrecompileErrorStackTraceEntry {
104
- type_: StackTraceEntryTypeConst,
105
- precompile: trace.precompile,
106
- source_reference: None,
107
- }
108
- .into()])
109
- }
110
-
111
- fn get_create_message_stack_trace(
112
- &self,
113
- trace: &CreateMessageTrace,
114
- ) -> napi::Result<SolidityStackTrace> {
115
- let inferred_error = ErrorInferrer::infer_before_tracing_create_message(trace)?;
116
-
117
- if let Some(inferred_error) = inferred_error {
118
- return Ok(inferred_error);
119
- }
120
-
121
- self.trace_evm_execution(Either::B(trace))
122
- }
123
-
124
- fn get_call_message_stack_trace(
125
- &self,
126
- trace: &CallMessageTrace,
127
- ) -> napi::Result<SolidityStackTrace> {
128
- let inferred_error = ErrorInferrer::infer_before_tracing_call_message(trace)?;
129
-
130
- if let Some(inferred_error) = inferred_error {
131
- return Ok(inferred_error);
132
- }
133
-
134
- self.trace_evm_execution(Either::A(trace))
135
- }
136
-
137
- fn get_unrecognized_message_stack_trace(
138
- &self,
139
- trace: Either<&CallMessageTrace, &CreateMessageTrace>,
140
- ) -> napi::Result<SolidityStackTrace> {
141
- let (trace_exit_kind, trace_return_data) = match &trace {
142
- Either::A(call) => (call.exit.kind(), &call.return_data),
143
- Either::B(create) => (create.exit.kind(), &create.return_data),
144
- };
145
-
146
- let subtrace = self.get_last_subtrace(trace);
147
-
148
- if let Some(subtrace) = subtrace {
149
- let (is_error, return_data) = match subtrace {
150
- Either3::A(precompile) => {
151
- (precompile.exit.is_error(), precompile.return_data.clone())
152
- }
153
- Either3::B(call) => (call.exit.is_error(), call.return_data.clone()),
154
- Either3::C(create) => (create.exit.is_error(), create.return_data.clone()),
155
- };
156
-
157
- // This is not a very exact heuristic, but most of the time it will be right, as
158
- // solidity reverts if a call fails, and most contracts are in
159
- // solidity
160
- if is_error && trace_return_data.as_ref() == return_data.as_ref() {
161
- let unrecognized_entry: SolidityStackTraceEntry = match trace {
162
- Either::A(CallMessageTrace { address, .. }) => {
163
- UnrecognizedContractCallstackEntryStackTraceEntry {
164
- type_: StackTraceEntryTypeConst,
165
- address: address.clone(),
166
- source_reference: None,
167
- }
168
- .into()
169
- }
170
- Either::B(CreateMessageTrace { .. }) => {
171
- UnrecognizedCreateCallstackEntryStackTraceEntry {
172
- type_: StackTraceEntryTypeConst,
173
- source_reference: None,
174
- }
175
- .into()
176
- }
177
- };
178
-
179
- let mut stacktrace = vec![unrecognized_entry];
180
- stacktrace.extend(self.get_stack_trace_inner(subtrace)?);
181
-
182
- return Ok(stacktrace);
183
- }
184
- }
185
-
186
- if trace_exit_kind == ExitCode::CODESIZE_EXCEEDS_MAXIMUM {
187
- return Ok(vec![ContractTooLargeErrorStackTraceEntry {
188
- type_: StackTraceEntryTypeConst,
189
- source_reference: None,
190
- }
191
- .into()]);
192
- }
193
-
194
- let is_invalid_opcode_error = trace_exit_kind == ExitCode::INVALID_OPCODE;
195
-
196
- match trace {
197
- Either::A(trace @ CallMessageTrace { .. }) => {
198
- Ok(vec![UnrecognizedContractErrorStackTraceEntry {
199
- type_: StackTraceEntryTypeConst,
200
- address: trace.address.clone(),
201
- return_data: trace.return_data.clone(),
202
- is_invalid_opcode_error,
203
- source_reference: None,
204
- }
205
- .into()])
206
- }
207
- Either::B(trace @ CreateMessageTrace { .. }) => {
208
- Ok(vec![UnrecognizedCreateErrorStackTraceEntry {
209
- type_: StackTraceEntryTypeConst,
210
- return_data: trace.return_data.clone(),
211
- is_invalid_opcode_error,
212
- source_reference: None,
213
- }
214
- .into()])
215
- }
216
- }
217
- }
218
-
219
- fn trace_evm_execution(
220
- &self,
221
- trace: Either<&CallMessageTrace, &CreateMessageTrace>,
222
- ) -> napi::Result<SolidityStackTrace> {
223
- let stack_trace = self.raw_trace_evm_execution(trace)?;
224
-
225
- if stack_trace_may_require_adjustments(&stack_trace, trace) {
226
- return adjust_stack_trace(stack_trace, trace);
227
- }
228
-
229
- Ok(stack_trace)
230
- }
231
-
232
- fn raw_trace_evm_execution(
233
- &self,
234
- trace: Either<&CallMessageTrace, &CreateMessageTrace>,
235
- ) -> napi::Result<SolidityStackTrace> {
236
- let (bytecode, steps, number_of_subtraces) = match &trace {
237
- Either::A(call) => (&call.bytecode, &call.steps, call.number_of_subtraces),
238
- Either::B(create) => (&create.bytecode, &create.steps, create.number_of_subtraces),
239
- };
240
- let bytecode = bytecode.as_ref().expect("JS code asserts");
241
-
242
- let mut stacktrace: SolidityStackTrace = vec![];
243
-
244
- let mut subtraces_seen = 0;
245
-
246
- // There was a jump into a function according to the sourcemaps
247
- let mut jumped_into_function = false;
248
-
249
- let mut function_jumpdests: Vec<&Instruction> = vec![];
250
-
251
- let mut last_submessage_data: Option<SubmessageDataRef<'_>> = None;
252
-
253
- let mut iter = steps.iter().enumerate().peekable();
254
- while let Some((step_index, step)) = iter.next() {
255
- if let Either4::A(EvmStep { pc }) = step {
256
- let inst = bytecode.get_instruction(*pc)?;
257
-
258
- if inst.jump_type == JumpType::IntoFunction && iter.peek().is_some() {
259
- let (_, next_step) = iter.peek().unwrap();
260
- let Either4::A(next_evm_step) = next_step else {
261
- unreachable!("JS code asserted that");
262
- };
263
- let next_inst = bytecode.get_instruction(next_evm_step.pc)?;
264
-
265
- if next_inst.opcode == OpCode::JUMPDEST {
266
- let frame = instruction_to_callstack_stack_trace_entry(bytecode, inst)?;
267
- stacktrace.push(match frame {
268
- Either::A(frame) => frame.into(),
269
- Either::B(frame) => frame.into(),
270
- });
271
- if next_inst.location.is_some() {
272
- jumped_into_function = true;
273
- }
274
- function_jumpdests.push(next_inst);
275
- }
276
- } else if inst.jump_type == JumpType::OutofFunction {
277
- stacktrace.pop();
278
- function_jumpdests.pop();
279
- }
280
- } else {
281
- let message_trace = match step {
282
- Either4::A(_) => unreachable!("branch is taken above"),
283
- Either4::B(precompile) => Either3::A(precompile),
284
- Either4::C(call) => Either3::B(call),
285
- Either4::D(create) => Either3::C(create),
286
- };
287
-
288
- subtraces_seen += 1;
289
-
290
- // If there are more subtraces, this one didn't terminate the execution
291
- if subtraces_seen < number_of_subtraces {
292
- continue;
293
- }
294
-
295
- let submessage_trace = self.get_stack_trace_inner(message_trace)?;
296
-
297
- last_submessage_data = Some(SubmessageDataRef {
298
- message_trace,
299
- step_index: step_index as u32,
300
- stacktrace: submessage_trace,
301
- });
302
- }
303
- }
304
-
305
- let stacktrace_with_inferred_error = ErrorInferrer::infer_after_tracing(
306
- trace,
307
- stacktrace,
308
- &function_jumpdests,
309
- jumped_into_function,
310
- last_submessage_data,
311
- )?;
312
-
313
- ErrorInferrer::filter_redundant_frames(stacktrace_with_inferred_error)
314
- }
315
- }