@nomicfoundation/edr 0.11.3 → 0.12.0-next.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.
Files changed (46) hide show
  1. package/Cargo.toml +61 -27
  2. package/LICENSE +5 -1
  3. package/index.d.ts +875 -137
  4. package/index.js +61 -3
  5. package/package.json +20 -16
  6. package/src/account.rs +109 -32
  7. package/src/block.rs +2 -103
  8. package/src/call_override.rs +7 -7
  9. package/src/cast.rs +47 -17
  10. package/src/chains/generic.rs +51 -0
  11. package/src/chains/l1.rs +262 -0
  12. package/src/chains/op.rs +425 -0
  13. package/src/chains.rs +7 -0
  14. package/src/config.rs +537 -67
  15. package/src/context.rs +374 -17
  16. package/src/debug_trace.rs +2 -2
  17. package/src/instrument.rs +109 -0
  18. package/src/lib.rs +38 -14
  19. package/src/log.rs +12 -14
  20. package/src/logger.rs +77 -1177
  21. package/src/mock.rs +68 -0
  22. package/src/precompile.rs +50 -0
  23. package/src/provider/factory.rs +22 -0
  24. package/src/provider/response.rs +73 -0
  25. package/src/provider.rs +64 -325
  26. package/src/result.rs +60 -69
  27. package/src/scenarios.rs +11 -17
  28. package/src/serde.rs +57 -0
  29. package/src/solidity_tests/artifact.rs +184 -0
  30. package/src/solidity_tests/config.rs +725 -0
  31. package/src/solidity_tests/factory.rs +22 -0
  32. package/src/solidity_tests/l1.rs +68 -0
  33. package/src/solidity_tests/op.rs +69 -0
  34. package/src/solidity_tests/runner.rs +51 -0
  35. package/src/solidity_tests/test_results.rs +668 -0
  36. package/src/solidity_tests.rs +56 -0
  37. package/src/subscription.rs +32 -0
  38. package/src/trace/debug.rs +1 -1
  39. package/src/trace/exit.rs +12 -13
  40. package/src/trace/library_utils.rs +1 -1
  41. package/src/trace/return_data.rs +11 -11
  42. package/src/trace/solidity_stack_trace.rs +11 -8
  43. package/src/trace.rs +37 -44
  44. package/src/withdrawal.rs +4 -4
  45. package/src/provider/config.rs +0 -291
  46. package/src/subscribe.rs +0 -63
package/src/logger.rs CHANGED
@@ -1,300 +1,76 @@
1
- use std::{
2
- fmt::Display,
3
- sync::{mpsc::channel, Arc},
4
- };
1
+ use std::sync::{mpsc::channel, Arc};
5
2
 
6
- use ansi_term::{Color, Style};
7
- use edr_eth::{
8
- transaction::{self, Transaction},
9
- Bytes, B256, U256,
10
- };
11
- use edr_evm::{
12
- blockchain::BlockchainError,
13
- chain_spec::L1ChainSpec,
14
- precompile::{self, Precompiles},
15
- trace::{AfterMessage, TraceMessage},
16
- transaction::SignedTransaction as _,
17
- ExecutionResult, SyncBlock,
18
- };
19
- use edr_provider::{ProviderError, TransactionFailure};
20
- use edr_solidity::contract_decoder::ContractDecoder;
21
- use itertools::izip;
3
+ use edr_eth::Bytes;
4
+ use edr_napi_core::logger::LoggerError;
22
5
  use napi::{
23
6
  threadsafe_function::{
24
7
  ErrorStrategy, ThreadSafeCallContext, ThreadsafeFunction, ThreadsafeFunctionCallMode,
25
8
  },
26
- Env, JsFunction, Status,
9
+ JsFunction, Status,
27
10
  };
28
11
  use napi_derive::napi;
29
12
 
30
- use crate::cast::TryCast;
31
-
32
- #[napi(object)]
33
- pub struct ContractAndFunctionName {
34
- /// The contract name.
35
- pub contract_name: String,
36
- /// The function name. Only present for calls.
37
- pub function_name: Option<String>,
38
- }
39
-
40
- impl TryCast<(String, Option<String>)> for ContractAndFunctionName {
41
- type Error = napi::Error;
42
-
43
- fn try_cast(self) -> std::result::Result<(String, Option<String>), Self::Error> {
44
- Ok((self.contract_name, self.function_name))
45
- }
46
- }
47
-
48
13
  #[napi(object)]
49
14
  pub struct LoggerConfig {
50
15
  /// Whether to enable the logger.
51
16
  pub enable: bool,
52
- #[napi(ts_type = "(inputs: Buffer[]) => string[]")]
17
+ #[napi(ts_type = "(inputs: ArrayBuffer[]) => string[]")]
53
18
  pub decode_console_log_inputs_callback: JsFunction,
54
19
  #[napi(ts_type = "(message: string, replace: boolean) => void")]
55
20
  pub print_line_callback: JsFunction,
56
21
  }
57
22
 
58
- #[derive(Clone)]
59
- pub enum LoggingState {
60
- CollapsingMethod(CollapsedMethod),
61
- HardhatMinining {
62
- empty_blocks_range_start: Option<u64>,
63
- },
64
- IntervalMining {
65
- empty_blocks_range_start: Option<u64>,
66
- },
67
- Empty,
68
- }
69
-
70
- impl LoggingState {
71
- /// Converts the state into a hardhat mining state.
72
- pub fn into_hardhat_mining(self) -> Option<u64> {
73
- match self {
74
- Self::HardhatMinining {
75
- empty_blocks_range_start,
76
- } => empty_blocks_range_start,
77
- _ => None,
78
- }
79
- }
80
-
81
- /// Converts the state into an interval mining state.
82
- pub fn into_interval_mining(self) -> Option<u64> {
83
- match self {
84
- Self::IntervalMining {
85
- empty_blocks_range_start,
86
- } => empty_blocks_range_start,
87
- _ => None,
88
- }
89
- }
90
- }
91
-
92
- impl Default for LoggingState {
93
- fn default() -> Self {
94
- Self::Empty
95
- }
96
- }
97
-
98
- #[derive(Clone)]
99
- enum LogLine {
100
- Single(String),
101
- WithTitle(String, String),
102
- }
103
-
104
- #[derive(Debug, thiserror::Error)]
105
- pub enum LoggerError {
106
- #[error("Failed to print line")]
107
- PrintLine,
108
- }
109
-
110
- #[derive(Clone)]
111
- pub struct Logger {
112
- collector: LogCollector,
113
- }
114
-
115
- impl Logger {
116
- pub fn new(
117
- env: &Env,
118
- config: LoggerConfig,
119
- contract_decoder: Arc<ContractDecoder>,
120
- ) -> napi::Result<Self> {
121
- Ok(Self {
122
- collector: LogCollector::new(env, config, contract_decoder)?,
123
- })
124
- }
125
- }
126
-
127
- impl edr_provider::Logger for Logger {
128
- type BlockchainError = BlockchainError;
129
-
130
- type LoggerError = LoggerError;
131
-
132
- fn is_enabled(&self) -> bool {
133
- self.collector.is_enabled
134
- }
135
-
136
- fn set_is_enabled(&mut self, is_enabled: bool) {
137
- self.collector.is_enabled = is_enabled;
138
- }
139
-
140
- fn log_call(
141
- &mut self,
142
- spec_id: edr_eth::SpecId,
143
- transaction: &transaction::Signed,
144
- result: &edr_provider::CallResult,
145
- ) -> Result<(), Self::LoggerError> {
146
- self.collector.log_call(spec_id, transaction, result);
147
-
148
- Ok(())
149
- }
150
-
151
- fn log_estimate_gas_failure(
152
- &mut self,
153
- spec_id: edr_eth::SpecId,
154
- transaction: &transaction::Signed,
155
- failure: &edr_provider::EstimateGasFailure,
156
- ) -> Result<(), Self::LoggerError> {
157
- self.collector
158
- .log_estimate_gas(spec_id, transaction, failure);
159
-
160
- Ok(())
161
- }
162
-
163
- fn log_interval_mined(
164
- &mut self,
165
- spec_id: edr_eth::SpecId,
166
- mining_result: &edr_provider::DebugMineBlockResult<Self::BlockchainError>,
167
- ) -> Result<(), Self::LoggerError> {
168
- self.collector.log_interval_mined(spec_id, mining_result)
169
- }
170
-
171
- fn log_mined_block(
172
- &mut self,
173
- spec_id: edr_eth::SpecId,
174
- mining_results: &[edr_provider::DebugMineBlockResult<Self::BlockchainError>],
175
- ) -> Result<(), Self::LoggerError> {
176
- self.collector.log_mined_blocks(spec_id, mining_results);
177
-
178
- Ok(())
179
- }
180
-
181
- fn log_send_transaction(
182
- &mut self,
183
- spec_id: edr_eth::SpecId,
184
- transaction: &edr_evm::transaction::Signed,
185
- mining_results: &[edr_provider::DebugMineBlockResult<Self::BlockchainError>],
186
- ) -> Result<(), Self::LoggerError> {
187
- self.collector
188
- .log_send_transaction(spec_id, transaction, mining_results);
189
-
190
- Ok(())
191
- }
192
-
193
- fn print_method_logs(
194
- &mut self,
195
- method: &str,
196
- error: Option<&ProviderError<LoggerError>>,
197
- ) -> Result<(), Self::LoggerError> {
198
- if let Some(error) = error {
199
- self.collector.state = LoggingState::Empty;
200
-
201
- if matches!(error, ProviderError::UnsupportedMethod { .. }) {
202
- self.collector
203
- .print::<false>(Color::Red.paint(error.to_string()))?;
204
- } else {
205
- self.collector.print::<false>(Color::Red.paint(method))?;
206
- self.collector.print_logs()?;
207
-
208
- if !matches!(error, ProviderError::TransactionFailed(_)) {
209
- self.collector.print_empty_line()?;
210
-
211
- let error_message = error.to_string();
212
- self.collector
213
- .try_indented(|logger| logger.print::<false>(&error_message))?;
214
-
215
- if matches!(error, ProviderError::InvalidEip155TransactionChainId) {
216
- self.collector.try_indented(|logger| {
217
- logger.print::<false>(Color::Yellow.paint(
218
- "If you are using MetaMask, you can learn how to fix this error here: https://hardhat.org/metamask-issue"
219
- ))
220
- })?;
221
- }
222
- }
223
-
224
- self.collector.print_empty_line()?;
225
- }
226
- } else {
227
- self.collector.print_method(method)?;
228
-
229
- let printed = self.collector.print_logs()?;
230
- if printed {
231
- self.collector.print_empty_line()?;
232
- }
233
- }
234
-
235
- Ok(())
236
- }
237
-
238
- fn print_contract_decoding_error(&mut self, error: &str) -> Result<(), Self::LoggerError> {
239
- self.collector.log(
240
- "Contract decoder failed to be updated. Please report this to help us improve Hardhat.",
241
- );
242
- self.collector.print_empty_line()?;
243
- self.collector.log(error);
244
- Ok(())
245
- }
246
- }
247
-
248
- #[derive(Clone)]
249
- pub struct CollapsedMethod {
250
- count: usize,
251
- method: String,
252
- }
253
-
254
- #[derive(Clone)]
255
- struct LogCollector {
256
- contract_decoder: Arc<ContractDecoder>,
257
- decode_console_log_inputs_fn: ThreadsafeFunction<Vec<Bytes>, ErrorStrategy::Fatal>,
258
- indentation: usize,
259
- is_enabled: bool,
260
- logs: Vec<LogLine>,
261
- print_line_fn: ThreadsafeFunction<(String, bool), ErrorStrategy::Fatal>,
262
- state: LoggingState,
263
- title_length: usize,
264
- }
265
-
266
- impl LogCollector {
267
- pub fn new(
268
- env: &Env,
269
- config: LoggerConfig,
270
- contract_decoder: Arc<ContractDecoder>,
271
- ) -> napi::Result<Self> {
272
- let mut decode_console_log_inputs_fn = config
273
- .decode_console_log_inputs_callback
274
- .create_threadsafe_function(0, |ctx: ThreadSafeCallContext<Vec<Bytes>>| {
275
- let inputs =
276
- ctx.env
277
- .create_array_with_length(ctx.value.len())
278
- .and_then(|mut inputs| {
23
+ impl LoggerConfig {
24
+ /// Resolves the logger config, converting it to a
25
+ /// `edr_napi_core::logger::Config`.
26
+ pub fn resolve(self, env: &napi::Env) -> napi::Result<edr_napi_core::logger::Config> {
27
+ let mut decode_console_log_inputs_callback: ThreadsafeFunction<_, ErrorStrategy::Fatal> =
28
+ self.decode_console_log_inputs_callback
29
+ .create_threadsafe_function(0, |ctx: ThreadSafeCallContext<Vec<Bytes>>| {
30
+ let inputs = ctx.env.create_array_with_length(ctx.value.len()).and_then(
31
+ |mut inputs| {
279
32
  for (idx, input) in ctx.value.into_iter().enumerate() {
280
- ctx.env.create_buffer_with_data(input.to_vec()).and_then(
281
- |input| inputs.set_element(idx as u32, input.into_raw()),
282
- )?;
33
+ ctx.env
34
+ .create_arraybuffer_with_data(input.to_vec())
35
+ .and_then(|input| {
36
+ inputs.set_element(idx as u32, input.into_raw())
37
+ })?;
283
38
  }
284
39
 
285
40
  Ok(inputs)
286
- })?;
287
-
288
- Ok(vec![inputs])
289
- })?;
41
+ },
42
+ )?;
43
+
44
+ Ok(vec![inputs])
45
+ })?;
46
+
47
+ // Maintain a weak reference to the function to avoid blocking the event loop
48
+ // from exiting.
49
+ decode_console_log_inputs_callback.unref(env)?;
50
+
51
+ let decode_console_log_inputs_fn = Arc::new(move |console_log_inputs| {
52
+ let (sender, receiver) = channel();
53
+
54
+ let status = decode_console_log_inputs_callback.call_with_return_value(
55
+ console_log_inputs,
56
+ ThreadsafeFunctionCallMode::Blocking,
57
+ move |decoded_inputs: Vec<String>| {
58
+ sender.send(decoded_inputs).map_err(|_error| {
59
+ napi::Error::new(
60
+ Status::GenericFailure,
61
+ "Failed to send result from decode_console_log_inputs",
62
+ )
63
+ })
64
+ },
65
+ );
66
+ assert_eq!(status, Status::Ok);
290
67
 
291
- // Maintain a weak reference to the function to avoid the event loop from
292
- // exiting.
293
- decode_console_log_inputs_fn.unref(env)?;
68
+ receiver.recv().unwrap()
69
+ });
294
70
 
295
- let mut print_line_fn = config.print_line_callback.create_threadsafe_function(
296
- 0,
297
- |ctx: ThreadSafeCallContext<(String, bool)>| {
71
+ let mut print_line_callback: ThreadsafeFunction<_, ErrorStrategy::Fatal> = self
72
+ .print_line_callback
73
+ .create_threadsafe_function(0, |ctx: ThreadSafeCallContext<(String, bool)>| {
298
74
  // String
299
75
  let message = ctx.env.create_string_from_std(ctx.value.0)?;
300
76
 
@@ -302,917 +78,41 @@ impl LogCollector {
302
78
  let replace = ctx.env.get_boolean(ctx.value.1)?;
303
79
 
304
80
  Ok(vec![message.into_unknown(), replace.into_unknown()])
305
- },
306
- )?;
307
-
308
- // Maintain a weak reference to the function to avoid the event loop from
309
- // exiting.
310
- print_line_fn.unref(env)?;
311
-
312
- Ok(Self {
313
- contract_decoder,
314
- decode_console_log_inputs_fn,
315
- indentation: 0,
316
- is_enabled: config.enable,
317
- logs: Vec::new(),
318
- print_line_fn,
319
- state: LoggingState::default(),
320
- title_length: 0,
321
- })
322
- }
323
-
324
- pub fn log_call(
325
- &mut self,
326
- spec_id: edr_eth::SpecId,
327
- transaction: &transaction::Signed,
328
- result: &edr_provider::CallResult,
329
- ) {
330
- let edr_provider::CallResult {
331
- console_log_inputs,
332
- execution_result,
333
- trace,
334
- } = result;
335
-
336
- self.state = LoggingState::Empty;
337
-
338
- self.indented(|logger| {
339
- logger.log_contract_and_function_name::<true>(spec_id, trace);
340
-
341
- logger.log_with_title("From", format!("0x{:x}", transaction.caller()));
342
- if let Some(to) = transaction.kind().to() {
343
- logger.log_with_title("To", format!("0x{to:x}"));
344
- }
345
- if transaction.value() > U256::ZERO {
346
- logger.log_with_title("Value", wei_to_human_readable(transaction.value()));
347
- }
348
-
349
- logger.log_console_log_messages(console_log_inputs);
350
-
351
- if let Some(transaction_failure) =
352
- TransactionFailure::from_execution_result(execution_result, None, trace)
353
- {
354
- logger.log_transaction_failure(&transaction_failure);
355
- }
356
- });
357
- }
358
-
359
- pub fn log_estimate_gas(
360
- &mut self,
361
- spec_id: edr_eth::SpecId,
362
- transaction: &transaction::Signed,
363
- result: &edr_provider::EstimateGasFailure,
364
- ) {
365
- let edr_provider::EstimateGasFailure {
366
- console_log_inputs,
367
- transaction_failure,
368
- } = result;
369
-
370
- self.state = LoggingState::Empty;
371
-
372
- self.indented(|logger| {
373
- logger.log_contract_and_function_name::<true>(
374
- spec_id,
375
- &transaction_failure.failure.solidity_trace,
376
- );
377
-
378
- logger.log_with_title("From", format!("0x{:x}", transaction.caller()));
379
- if let Some(to) = transaction.kind().to() {
380
- logger.log_with_title("To", format!("0x{to:x}"));
381
- }
382
- logger.log_with_title("Value", wei_to_human_readable(transaction.value()));
383
-
384
- logger.log_console_log_messages(console_log_inputs);
385
-
386
- logger.log_transaction_failure(&transaction_failure.failure);
387
- });
388
- }
389
-
390
- fn log_transaction_failure(&mut self, failure: &edr_provider::TransactionFailure) {
391
- let is_revert_error = matches!(
392
- failure.reason,
393
- edr_provider::TransactionFailureReason::Revert(_)
394
- );
395
-
396
- let error_type = if is_revert_error {
397
- "Error"
398
- } else {
399
- "TransactionExecutionError"
400
- };
401
-
402
- self.log_empty_line();
403
- self.log(format!("{error_type}: {failure}"));
404
- }
405
-
406
- pub fn log_mined_blocks(
407
- &mut self,
408
- spec_id: edr_eth::SpecId,
409
- mining_results: &[edr_provider::DebugMineBlockResult<BlockchainError>],
410
- ) {
411
- let num_results = mining_results.len();
412
- for (idx, mining_result) in mining_results.iter().enumerate() {
413
- let state = std::mem::take(&mut self.state);
414
- let empty_blocks_range_start = state.into_hardhat_mining();
415
-
416
- if mining_result.block.transactions().is_empty() {
417
- self.log_hardhat_mined_empty_block(&mining_result.block, empty_blocks_range_start);
418
-
419
- let block_number = mining_result.block.header().number;
420
- self.state = LoggingState::HardhatMinining {
421
- empty_blocks_range_start: Some(
422
- empty_blocks_range_start.unwrap_or(block_number),
423
- ),
424
- };
425
- } else {
426
- self.log_hardhat_mined_block(spec_id, mining_result);
427
-
428
- if idx < num_results - 1 {
429
- self.log_empty_line();
430
- }
431
- }
432
- }
433
- }
434
-
435
- pub fn log_interval_mined(
436
- &mut self,
437
- spec_id: edr_eth::SpecId,
438
- mining_result: &edr_provider::DebugMineBlockResult<BlockchainError>,
439
- ) -> Result<(), LoggerError> {
440
- let block_header = mining_result.block.header();
441
- let block_number = block_header.number;
442
-
443
- if mining_result.block.transactions().is_empty() {
444
- let state = std::mem::take(&mut self.state);
445
- let empty_blocks_range_start = state.into_interval_mining();
446
-
447
- if let Some(empty_blocks_range_start) = empty_blocks_range_start {
448
- self.print::<true>(format!(
449
- "Mined empty block range #{empty_blocks_range_start} to #{block_number}"
450
- ))?;
451
- } else {
452
- let base_fee = if let Some(base_fee) = block_header.base_fee_per_gas.as_ref() {
453
- format!(" with base fee {base_fee}")
454
- } else {
455
- String::new()
456
- };
457
-
458
- self.print::<false>(format!("Mined empty block #{block_number}{base_fee}"))?;
459
- }
460
-
461
- self.state = LoggingState::IntervalMining {
462
- empty_blocks_range_start: Some(
463
- empty_blocks_range_start.unwrap_or(block_header.number),
464
- ),
465
- };
466
- } else {
467
- self.log_interval_mined_block(spec_id, mining_result);
468
-
469
- self.print::<false>(format!("Mined block #{block_number}"))?;
470
-
471
- let printed = self.print_logs()?;
472
- if printed {
473
- self.print_empty_line()?;
474
- }
475
- }
476
-
477
- Ok(())
478
- }
479
-
480
- pub fn log_send_transaction(
481
- &mut self,
482
- spec_id: edr_eth::SpecId,
483
- transaction: &edr_evm::transaction::Signed,
484
- mining_results: &[edr_provider::DebugMineBlockResult<BlockchainError>],
485
- ) {
486
- if !mining_results.is_empty() {
487
- self.state = LoggingState::Empty;
81
+ })?;
488
82
 
489
- let (sent_block_result, sent_transaction_result, sent_trace) = mining_results
490
- .iter()
491
- .find_map(|result| {
492
- izip!(
493
- result.block.transactions(),
494
- result.transaction_results.iter(),
495
- result.transaction_traces.iter()
496
- )
497
- .find(|(block_transaction, _, _)| {
498
- *block_transaction.transaction_hash() == *transaction.transaction_hash()
83
+ // Maintain a weak reference to the function to avoid blocking the event loop
84
+ // from exiting.
85
+ print_line_callback.unref(env)?;
86
+
87
+ let print_line_fn = Arc::new(move |message, replace| {
88
+ let (sender, receiver) = channel();
89
+
90
+ let status = print_line_callback.call_with_return_value(
91
+ (message, replace),
92
+ ThreadsafeFunctionCallMode::Blocking,
93
+ move |()| {
94
+ sender.send(()).map_err(|_error| {
95
+ napi::Error::new(
96
+ Status::GenericFailure,
97
+ "Failed to send result from decode_console_log_inputs",
98
+ )
499
99
  })
500
- .map(|(_, transaction_result, trace)| (result, transaction_result, trace))
501
- })
502
- .expect("Transaction result not found");
503
-
504
- if mining_results.len() > 1 {
505
- self.log_multiple_blocks_warning();
506
- self.log_auto_mined_block_results(
507
- spec_id,
508
- mining_results,
509
- transaction.transaction_hash(),
510
- );
511
- self.log_currently_sent_transaction(
512
- spec_id,
513
- sent_block_result,
514
- transaction,
515
- sent_transaction_result,
516
- sent_trace,
517
- );
518
- } else if let Some(result) = mining_results.first() {
519
- let transactions = result.block.transactions();
520
- if transactions.len() > 1 {
521
- self.log_multiple_transactions_warning();
522
- self.log_auto_mined_block_results(
523
- spec_id,
524
- mining_results,
525
- transaction.transaction_hash(),
526
- );
527
- self.log_currently_sent_transaction(
528
- spec_id,
529
- sent_block_result,
530
- transaction,
531
- sent_transaction_result,
532
- sent_trace,
533
- );
534
- } else if let Some(transaction) = transactions.first() {
535
- self.log_single_transaction_mining_result(spec_id, result, transaction);
536
- }
537
- }
538
- }
539
- }
540
-
541
- fn contract_and_function_name(
542
- &self,
543
- code: Bytes,
544
- calldata: Option<Bytes>,
545
- ) -> (String, Option<String>) {
546
- let edr_solidity::contract_decoder::ContractAndFunctionName {
547
- contract_name,
548
- function_name,
549
- } = self
550
- .contract_decoder
551
- .get_contract_and_function_names_for_call(&code, calldata.as_ref());
552
- (contract_name, function_name)
553
- }
554
-
555
- fn format(&self, message: impl ToString) -> String {
556
- let message = message.to_string();
557
-
558
- if message.is_empty() {
559
- message
560
- } else {
561
- message
562
- .split('\n')
563
- .map(|line| format!("{:indent$}{line}", "", indent = self.indentation))
564
- .collect::<Vec<_>>()
565
- .join("\n")
566
- }
567
- }
568
-
569
- fn indented(&mut self, display_fn: impl FnOnce(&mut Self)) {
570
- self.indentation += 2;
571
- display_fn(self);
572
- self.indentation -= 2;
573
- }
574
-
575
- fn try_indented(
576
- &mut self,
577
- display_fn: impl FnOnce(&mut Self) -> Result<(), LoggerError>,
578
- ) -> Result<(), LoggerError> {
579
- self.indentation += 2;
580
- let result = display_fn(self);
581
- self.indentation -= 2;
582
-
583
- result
584
- }
585
-
586
- fn log(&mut self, message: impl ToString) {
587
- let formatted = self.format(message);
588
-
589
- self.logs.push(LogLine::Single(formatted));
590
- }
591
-
592
- fn log_auto_mined_block_results(
593
- &mut self,
594
- spec_id: edr_eth::SpecId,
595
- results: &[edr_provider::DebugMineBlockResult<BlockchainError>],
596
- sent_transaction_hash: &B256,
597
- ) {
598
- for result in results {
599
- self.log_block_from_auto_mine(spec_id, result, sent_transaction_hash);
600
- }
601
- }
602
-
603
- fn log_base_fee(&mut self, base_fee: Option<&U256>) {
604
- if let Some(base_fee) = base_fee {
605
- self.log(format!("Base fee: {base_fee}"));
606
- }
607
- }
608
-
609
- fn log_block_from_auto_mine(
610
- &mut self,
611
- spec_id: edr_eth::SpecId,
612
- result: &edr_provider::DebugMineBlockResult<BlockchainError>,
613
- transaction_hash_to_highlight: &edr_eth::B256,
614
- ) {
615
- let edr_provider::DebugMineBlockResult {
616
- block,
617
- transaction_results,
618
- transaction_traces,
619
- console_log_inputs,
620
- } = result;
621
-
622
- let transactions = block.transactions();
623
- let num_transactions = transactions.len();
624
-
625
- debug_assert_eq!(num_transactions, transaction_results.len());
626
- debug_assert_eq!(num_transactions, transaction_traces.len());
627
-
628
- let block_header = block.header();
629
-
630
- self.indented(|logger| {
631
- logger.log_block_id(block);
632
-
633
- logger.indented(|logger| {
634
- logger.log_base_fee(block_header.base_fee_per_gas.as_ref());
635
-
636
- for (idx, transaction, result, trace) in izip!(
637
- 0..num_transactions,
638
- transactions,
639
- transaction_results,
640
- transaction_traces
641
- ) {
642
- let should_highlight_hash =
643
- *transaction.transaction_hash() == *transaction_hash_to_highlight;
644
- logger.log_block_transaction(
645
- spec_id,
646
- transaction,
647
- result,
648
- trace,
649
- console_log_inputs,
650
- should_highlight_hash,
651
- );
652
-
653
- logger.log_empty_line_between_transactions(idx, num_transactions);
654
- }
655
- });
656
- });
657
-
658
- self.log_empty_line();
659
- }
660
-
661
- fn log_block_hash(&mut self, block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>) {
662
- let block_hash = block.hash();
663
-
664
- self.log(format!("Block: {block_hash}"));
665
- }
666
-
667
- fn log_block_id(&mut self, block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>) {
668
- let block_number = block.header().number;
669
- let block_hash = block.hash();
670
-
671
- self.log(format!("Block #{block_number}: {block_hash}"));
672
- }
673
-
674
- fn log_block_number(&mut self, block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>) {
675
- let block_number = block.header().number;
676
-
677
- self.log(format!("Mined block #{block_number}"));
678
- }
679
-
680
- /// Logs a transaction that's part of a block.
681
- fn log_block_transaction(
682
- &mut self,
683
- spec_id: edr_eth::SpecId,
684
- transaction: &edr_evm::transaction::Signed,
685
- result: &edr_evm::ExecutionResult,
686
- trace: &edr_evm::trace::Trace,
687
- console_log_inputs: &[Bytes],
688
- should_highlight_hash: bool,
689
- ) {
690
- let transaction_hash = transaction.transaction_hash();
691
- if should_highlight_hash {
692
- self.log_with_title(
693
- "Transaction",
694
- Style::new().bold().paint(transaction_hash.to_string()),
695
- );
696
- } else {
697
- self.log_with_title("Transaction", transaction_hash.to_string());
698
- }
699
-
700
- self.indented(|logger| {
701
- logger.log_contract_and_function_name::<false>(spec_id, trace);
702
- logger.log_with_title("From", format!("0x{:x}", transaction.caller()));
703
- if let Some(to) = transaction.kind().to() {
704
- logger.log_with_title("To", format!("0x{to:x}"));
705
- }
706
- logger.log_with_title("Value", wei_to_human_readable(transaction.value()));
707
- logger.log_with_title(
708
- "Gas used",
709
- format!(
710
- "{gas_used} of {gas_limit}",
711
- gas_used = result.gas_used(),
712
- gas_limit = transaction.gas_limit()
713
- ),
100
+ },
714
101
  );
715
102
 
716
- logger.log_console_log_messages(console_log_inputs);
717
-
718
- let transaction_failure = edr_provider::TransactionFailure::from_execution_result(
719
- result,
720
- Some(transaction_hash),
721
- trace,
722
- );
723
-
724
- if let Some(transaction_failure) = transaction_failure {
725
- logger.log_transaction_failure(&transaction_failure);
726
- }
727
- });
728
- }
729
-
730
- fn log_console_log_messages(&mut self, console_log_inputs: &[Bytes]) {
731
- let (sender, receiver) = channel();
732
-
733
- let status = self.decode_console_log_inputs_fn.call_with_return_value(
734
- console_log_inputs.to_vec(),
735
- ThreadsafeFunctionCallMode::Blocking,
736
- move |decoded_inputs: Vec<String>| {
737
- sender.send(decoded_inputs).map_err(|_error| {
738
- napi::Error::new(
739
- Status::GenericFailure,
740
- "Failed to send result from decode_console_log_inputs",
741
- )
742
- })
743
- },
744
- );
745
- assert_eq!(status, Status::Ok);
746
-
747
- let console_log_inputs = receiver.recv().unwrap();
748
- // This is a special case, as we always want to print the console.log messages.
749
- // The difference is how. If we have a logger, we should use that, so that logs
750
- // are printed in order. If we don't, we just print the messages here.
751
- if self.is_enabled {
752
- if !console_log_inputs.is_empty() {
753
- self.log_empty_line();
754
- self.log("console.log:");
103
+ let () = receiver.recv().unwrap();
755
104
 
756
- self.indented(|logger| {
757
- for input in console_log_inputs {
758
- logger.log(input);
759
- }
760
- });
761
- }
762
- } else {
763
- for input in console_log_inputs {
764
- let status = self
765
- .print_line_fn
766
- .call((input, false), ThreadsafeFunctionCallMode::Blocking);
767
-
768
- assert_eq!(status, napi::Status::Ok);
769
- }
770
- }
771
- }
772
-
773
- fn log_contract_and_function_name<const PRINT_INVALID_CONTRACT_WARNING: bool>(
774
- &mut self,
775
- spec_id: edr_eth::SpecId,
776
- trace: &edr_evm::trace::Trace,
777
- ) {
778
- if let Some(TraceMessage::Before(before_message)) = trace.messages.first() {
779
- if let Some(to) = before_message.to {
780
- // Call
781
- let is_precompile = {
782
- let precompiles =
783
- Precompiles::new(precompile::PrecompileSpecId::from_spec_id(spec_id));
784
- precompiles.contains(&to)
785
- };
786
-
787
- if is_precompile {
788
- let precompile = u16::from_be_bytes([to[18], to[19]]);
789
- self.log_with_title(
790
- "Precompile call",
791
- format!("<PrecompileContract {precompile}>"),
792
- );
793
- } else {
794
- let is_code_empty = before_message
795
- .code
796
- .as_ref()
797
- .map_or(true, edr_evm::Bytecode::is_empty);
798
-
799
- if is_code_empty {
800
- if PRINT_INVALID_CONTRACT_WARNING {
801
- self.log("WARNING: Calling an account which is not a contract");
802
- }
803
- } else {
804
- let (contract_name, function_name) = self.contract_and_function_name(
805
- before_message
806
- .code
807
- .as_ref()
808
- .map(edr_evm::Bytecode::original_bytes)
809
- .expect("Call must be defined"),
810
- Some(before_message.data.clone()),
811
- );
812
-
813
- let function_name = function_name.expect("Function name must be defined");
814
- self.log_with_title(
815
- "Contract call",
816
- if function_name.is_empty() {
817
- contract_name
818
- } else {
819
- format!("{contract_name}#{function_name}")
820
- },
821
- );
822
- }
823
- }
824
- } else {
825
- let result = if let Some(TraceMessage::After(AfterMessage {
826
- execution_result,
827
- ..
828
- })) = trace.messages.last()
829
- {
830
- execution_result
831
- } else {
832
- unreachable!("Before messages must have an after message")
833
- };
834
-
835
- // Create
836
- let (contract_name, _) =
837
- self.contract_and_function_name(before_message.data.clone(), None);
838
-
839
- self.log_with_title("Contract deployment", contract_name);
840
-
841
- if let ExecutionResult::Success { output, .. } = result {
842
- if let edr_evm::Output::Create(_, address) = output {
843
- if let Some(deployed_address) = address {
844
- self.log_with_title(
845
- "Contract address",
846
- format!("0x{deployed_address:x}"),
847
- );
848
- }
849
- } else {
850
- unreachable!("Create calls must return a Create output")
851
- }
852
- }
853
- }
854
- }
855
- }
856
-
857
- fn log_empty_block(&mut self, block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>) {
858
- let block_header = block.header();
859
- let block_number = block_header.number;
860
-
861
- let base_fee = if let Some(base_fee) = block_header.base_fee_per_gas.as_ref() {
862
- format!(" with base fee {base_fee}")
863
- } else {
864
- String::new()
865
- };
866
-
867
- self.log(format!("Mined empty block #{block_number}{base_fee}",));
868
- }
869
-
870
- fn log_empty_line(&mut self) {
871
- self.log("");
872
- }
873
-
874
- fn log_empty_line_between_transactions(&mut self, idx: usize, num_transactions: usize) {
875
- if num_transactions > 1 && idx < num_transactions - 1 {
876
- self.log_empty_line();
877
- }
878
- }
879
-
880
- fn log_hardhat_mined_empty_block(
881
- &mut self,
882
- block: &dyn SyncBlock<L1ChainSpec, Error = BlockchainError>,
883
- empty_blocks_range_start: Option<u64>,
884
- ) {
885
- self.indented(|logger| {
886
- if let Some(empty_blocks_range_start) = empty_blocks_range_start {
887
- logger.replace_last_log_line(format!(
888
- "Mined empty block range #{empty_blocks_range_start} to #{block_number}",
889
- block_number = block.header().number
890
- ));
105
+ if status == napi::Status::Ok {
106
+ Ok(())
891
107
  } else {
892
- logger.log_empty_block(block);
108
+ Err(LoggerError::PrintLine)
893
109
  }
894
110
  });
895
- }
896
-
897
- /// Logs the result of interval mining a block.
898
- fn log_interval_mined_block(
899
- &mut self,
900
- spec_id: edr_eth::SpecId,
901
- result: &edr_provider::DebugMineBlockResult<BlockchainError>,
902
- ) {
903
- let edr_provider::DebugMineBlockResult {
904
- block,
905
- transaction_results,
906
- transaction_traces,
907
- console_log_inputs,
908
- } = result;
909
-
910
- let transactions = block.transactions();
911
- let num_transactions = transactions.len();
912
-
913
- debug_assert_eq!(num_transactions, transaction_results.len());
914
- debug_assert_eq!(num_transactions, transaction_traces.len());
915
-
916
- let block_header = block.header();
917
-
918
- self.indented(|logger| {
919
- logger.log_block_hash(block);
920
-
921
- logger.indented(|logger| {
922
- logger.log_base_fee(block_header.base_fee_per_gas.as_ref());
923
-
924
- for (idx, transaction, result, trace) in izip!(
925
- 0..num_transactions,
926
- transactions,
927
- transaction_results,
928
- transaction_traces
929
- ) {
930
- logger.log_block_transaction(
931
- spec_id,
932
- transaction,
933
- result,
934
- trace,
935
- console_log_inputs,
936
- false,
937
- );
938
-
939
- logger.log_empty_line_between_transactions(idx, num_transactions);
940
- }
941
- });
942
- });
943
- }
944
-
945
- fn log_hardhat_mined_block(
946
- &mut self,
947
- spec_id: edr_eth::SpecId,
948
- result: &edr_provider::DebugMineBlockResult<BlockchainError>,
949
- ) {
950
- let edr_provider::DebugMineBlockResult {
951
- block,
952
- transaction_results,
953
- transaction_traces,
954
- console_log_inputs,
955
- } = result;
956
-
957
- let transactions = block.transactions();
958
- let num_transactions = transactions.len();
959
111
 
960
- debug_assert_eq!(num_transactions, transaction_results.len());
961
- debug_assert_eq!(num_transactions, transaction_traces.len());
962
-
963
- self.indented(|logger| {
964
- if transactions.is_empty() {
965
- logger.log_empty_block(block);
966
- } else {
967
- logger.log_block_number(block);
968
-
969
- logger.indented(|logger| {
970
- logger.log_block_hash(block);
971
-
972
- logger.indented(|logger| {
973
- logger.log_base_fee(block.header().base_fee_per_gas.as_ref());
974
-
975
- for (idx, transaction, result, trace) in izip!(
976
- 0..num_transactions,
977
- transactions,
978
- transaction_results,
979
- transaction_traces
980
- ) {
981
- logger.log_block_transaction(
982
- spec_id,
983
- transaction,
984
- result,
985
- trace,
986
- console_log_inputs,
987
- false,
988
- );
989
-
990
- logger.log_empty_line_between_transactions(idx, num_transactions);
991
- }
992
- });
993
- });
994
- }
995
- });
996
- }
997
-
998
- /// Logs a warning about multiple blocks being mined.
999
- fn log_multiple_blocks_warning(&mut self) {
1000
- self.indented(|logger| {
1001
- logger
1002
- .log("There were other pending transactions. More than one block had to be mined:");
1003
- });
1004
- self.log_empty_line();
1005
- }
1006
-
1007
- /// Logs a warning about multiple transactions being mined.
1008
- fn log_multiple_transactions_warning(&mut self) {
1009
- self.indented(|logger| {
1010
- logger.log("There were other pending transactions mined in the same block:");
1011
- });
1012
- self.log_empty_line();
1013
- }
1014
-
1015
- fn log_with_title(&mut self, title: impl Into<String>, message: impl Display) {
1016
- // repeat whitespace self.indentation times and concatenate with title
1017
- let title = format!("{:indent$}{}", "", title.into(), indent = self.indentation);
1018
- if title.len() > self.title_length {
1019
- self.title_length = title.len();
1020
- }
1021
-
1022
- let message = format!("{message}");
1023
- self.logs.push(LogLine::WithTitle(title, message));
1024
- }
1025
-
1026
- fn log_currently_sent_transaction(
1027
- &mut self,
1028
- spec_id: edr_eth::SpecId,
1029
- block_result: &edr_provider::DebugMineBlockResult<BlockchainError>,
1030
- transaction: &transaction::Signed,
1031
- transaction_result: &edr_evm::ExecutionResult,
1032
- trace: &edr_evm::trace::Trace,
1033
- ) {
1034
- self.indented(|logger| {
1035
- logger.log("Currently sent transaction:");
1036
- logger.log("");
1037
- });
1038
-
1039
- self.log_transaction(
1040
- spec_id,
1041
- block_result,
1042
- transaction,
1043
- transaction_result,
1044
- trace,
1045
- );
1046
- }
1047
-
1048
- fn log_single_transaction_mining_result(
1049
- &mut self,
1050
- spec_id: edr_eth::SpecId,
1051
- result: &edr_provider::DebugMineBlockResult<BlockchainError>,
1052
- transaction: &transaction::Signed,
1053
- ) {
1054
- let trace = result
1055
- .transaction_traces
1056
- .first()
1057
- .expect("A transaction exists, so the trace must exist as well.");
1058
-
1059
- let transaction_result = result
1060
- .transaction_results
1061
- .first()
1062
- .expect("A transaction exists, so the result must exist as well.");
1063
-
1064
- self.log_transaction(spec_id, result, transaction, transaction_result, trace);
1065
- }
1066
-
1067
- fn log_transaction(
1068
- &mut self,
1069
- spec_id: edr_eth::SpecId,
1070
- block_result: &edr_provider::DebugMineBlockResult<BlockchainError>,
1071
- transaction: &transaction::Signed,
1072
- transaction_result: &edr_evm::ExecutionResult,
1073
- trace: &edr_evm::trace::Trace,
1074
- ) {
1075
- self.indented(|logger| {
1076
- logger.log_contract_and_function_name::<false>(spec_id, trace);
1077
-
1078
- let transaction_hash = transaction.transaction_hash();
1079
- logger.log_with_title("Transaction", transaction_hash);
1080
-
1081
- logger.log_with_title("From", format!("0x{:x}", transaction.caller()));
1082
- if let Some(to) = transaction.kind().to() {
1083
- logger.log_with_title("To", format!("0x{to:x}"));
1084
- }
1085
- logger.log_with_title("Value", wei_to_human_readable(transaction.value()));
1086
- logger.log_with_title(
1087
- "Gas used",
1088
- format!(
1089
- "{gas_used} of {gas_limit}",
1090
- gas_used = transaction_result.gas_used(),
1091
- gas_limit = transaction.gas_limit()
1092
- ),
1093
- );
1094
-
1095
- let block_number = block_result.block.header().number;
1096
- logger.log_with_title(format!("Block #{block_number}"), block_result.block.hash());
1097
-
1098
- logger.log_console_log_messages(&block_result.console_log_inputs);
1099
-
1100
- let transaction_failure = edr_provider::TransactionFailure::from_execution_result(
1101
- transaction_result,
1102
- Some(transaction_hash),
1103
- trace,
1104
- );
1105
-
1106
- if let Some(transaction_failure) = transaction_failure {
1107
- logger.log_transaction_failure(&transaction_failure);
1108
- }
1109
- });
1110
- }
1111
-
1112
- fn print<const REPLACE: bool>(&mut self, message: impl ToString) -> Result<(), LoggerError> {
1113
- if !self.is_enabled {
1114
- return Ok(());
1115
- }
1116
-
1117
- let formatted = self.format(message);
1118
-
1119
- let status = self
1120
- .print_line_fn
1121
- .call((formatted, REPLACE), ThreadsafeFunctionCallMode::Blocking);
1122
-
1123
- if status == napi::Status::Ok {
1124
- Ok(())
1125
- } else {
1126
- Err(LoggerError::PrintLine)
1127
- }
1128
- }
1129
-
1130
- fn print_empty_line(&mut self) -> Result<(), LoggerError> {
1131
- self.print::<false>("")
1132
- }
1133
-
1134
- fn print_logs(&mut self) -> Result<bool, LoggerError> {
1135
- let logs = std::mem::take(&mut self.logs);
1136
- if logs.is_empty() {
1137
- return Ok(false);
1138
- }
1139
-
1140
- for log in logs {
1141
- let line = match log {
1142
- LogLine::Single(message) => message,
1143
- LogLine::WithTitle(title, message) => {
1144
- let title = format!("{title}:");
1145
- format!("{title:indent$} {message}", indent = self.title_length + 1)
1146
- }
1147
- };
1148
-
1149
- self.print::<false>(line)?;
1150
- }
1151
-
1152
- Ok(true)
1153
- }
1154
-
1155
- fn print_method(&mut self, method: &str) -> Result<(), LoggerError> {
1156
- if let Some(collapsed_method) = self.collapsed_method(method) {
1157
- collapsed_method.count += 1;
1158
-
1159
- let line = format!("{method} ({count})", count = collapsed_method.count);
1160
- self.print::<true>(Color::Green.paint(line))
1161
- } else {
1162
- self.state = LoggingState::CollapsingMethod(CollapsedMethod {
1163
- count: 1,
1164
- method: method.to_string(),
1165
- });
1166
-
1167
- self.print::<false>(Color::Green.paint(method))
1168
- }
1169
- }
1170
-
1171
- /// Retrieves the collapsed method with the provided name, if it exists.
1172
- fn collapsed_method(&mut self, method: &str) -> Option<&mut CollapsedMethod> {
1173
- if let LoggingState::CollapsingMethod(collapsed_method) = &mut self.state {
1174
- if collapsed_method.method == method {
1175
- return Some(collapsed_method);
1176
- }
1177
- }
1178
-
1179
- None
1180
- }
1181
-
1182
- fn replace_last_log_line(&mut self, message: impl ToString) {
1183
- let formatted = self.format(message);
1184
-
1185
- *self.logs.last_mut().expect("There must be a log line") = LogLine::Single(formatted);
1186
- }
1187
- }
1188
-
1189
- fn wei_to_human_readable(wei: U256) -> String {
1190
- if wei == U256::ZERO {
1191
- "0 ETH".to_string()
1192
- } else if wei < U256::from(100_000u64) {
1193
- format!("{wei} wei")
1194
- } else if wei < U256::from(100_000_000_000_000u64) {
1195
- let mut decimal = to_decimal_string(wei, 9);
1196
- decimal.push_str(" gwei");
1197
- decimal
1198
- } else {
1199
- let mut decimal = to_decimal_string(wei, 18);
1200
- decimal.push_str(" ETH");
1201
- decimal
112
+ Ok(edr_napi_core::logger::Config {
113
+ enable: self.enable,
114
+ decode_console_log_inputs_fn,
115
+ print_line_fn,
116
+ })
1202
117
  }
1203
118
  }
1204
-
1205
- /// Converts the provided `value` to a decimal string after dividing it by
1206
- /// `10^exponent`. The returned string will have at most `MAX_DECIMALS`
1207
- /// decimals.
1208
- fn to_decimal_string(value: U256, exponent: u8) -> String {
1209
- const MAX_DECIMALS: u8 = 4;
1210
-
1211
- let (integer, remainder) = value.div_rem(U256::from(10).pow(U256::from(exponent)));
1212
- let decimal = remainder / U256::from(10).pow(U256::from(exponent - MAX_DECIMALS));
1213
-
1214
- // Remove trailing zeros
1215
- let decimal = decimal.to_string().trim_end_matches('0').to_string();
1216
-
1217
- format!("{integer}.{decimal}")
1218
- }