@nomicfoundation/edr 0.6.5 → 0.8.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.
- package/index.d.ts +42 -90
- package/index.js +4 -9
- package/package.json +8 -8
- package/src/logger.rs +34 -67
- package/src/provider.rs +132 -16
- package/src/trace/debug.rs +2 -262
- package/src/trace/exit.rs +3 -3
- package/src/trace/model.rs +5 -5
- package/src/trace/solidity_stack_trace.rs +220 -76
- package/src/trace.rs +11 -11
- package/src/trace/compiler.rs +0 -27
- package/src/trace/error_inferrer.rs +0 -2255
- package/src/trace/mapped_inlined_internal_functions_heuristics.rs +0 -180
- package/src/trace/message_trace.rs +0 -179
- package/src/trace/solidity_tracer.rs +0 -315
- package/src/trace/vm_trace_decoder.rs +0 -234
- package/src/trace/vm_tracer.rs +0 -71
package/index.d.ts
CHANGED
|
@@ -149,8 +149,6 @@ export interface LoggerConfig {
|
|
|
149
149
|
/** Whether to enable the logger. */
|
|
150
150
|
enable: boolean
|
|
151
151
|
decodeConsoleLogInputsCallback: (inputs: Buffer[]) => string[]
|
|
152
|
-
/** Used to resolve the contract and function name when logging. */
|
|
153
|
-
getContractAndFunctionNameCallback: (code: Buffer, calldata?: Buffer) => ContractAndFunctionName
|
|
154
152
|
printLineCallback: (message: string, replace: boolean) => void
|
|
155
153
|
}
|
|
156
154
|
/** Configuration for a chain */
|
|
@@ -256,6 +254,27 @@ export interface ProviderConfig {
|
|
|
256
254
|
/** The network ID of the blockchain */
|
|
257
255
|
networkId: bigint
|
|
258
256
|
}
|
|
257
|
+
/** Tracing config for Solidity stack trace generation. */
|
|
258
|
+
export interface TracingConfigWithBuffers {
|
|
259
|
+
/**
|
|
260
|
+
* Build information to use for decoding contracts. Either a Hardhat v2
|
|
261
|
+
* build info file that contains both input and output or a Hardhat v3
|
|
262
|
+
* build info file that doesn't contain output and a separate output file.
|
|
263
|
+
*/
|
|
264
|
+
buildInfos?: Array<Uint8Array> | Array<BuildInfoAndOutput>
|
|
265
|
+
/** Whether to ignore contracts whose name starts with "Ignored". */
|
|
266
|
+
ignoreContracts?: boolean
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Hardhat V3 build info where the compiler output is not part of the build
|
|
270
|
+
* info file.
|
|
271
|
+
*/
|
|
272
|
+
export interface BuildInfoAndOutput {
|
|
273
|
+
/** The build info input file */
|
|
274
|
+
buildInfo: Uint8Array
|
|
275
|
+
/** The build info output file */
|
|
276
|
+
output: Uint8Array
|
|
277
|
+
}
|
|
259
278
|
/** The possible reasons for successful termination of the EVM. */
|
|
260
279
|
export const enum SuccessReason {
|
|
261
280
|
/** The opcode `STOP` was called */
|
|
@@ -346,18 +365,7 @@ export interface SubscriptionEvent {
|
|
|
346
365
|
filterId: bigint
|
|
347
366
|
result: any
|
|
348
367
|
}
|
|
349
|
-
export declare function createModelsAndDecodeBytecodes(solcVersion: string, compilerInput: any, compilerOutput: any): Array<BytecodeWrapper>
|
|
350
368
|
export declare function linkHexStringBytecode(code: string, address: string, position: number): string
|
|
351
|
-
export const enum ContractFunctionType {
|
|
352
|
-
CONSTRUCTOR = 0,
|
|
353
|
-
FUNCTION = 1,
|
|
354
|
-
FALLBACK = 2,
|
|
355
|
-
RECEIVE = 3,
|
|
356
|
-
GETTER = 4,
|
|
357
|
-
MODIFIER = 5,
|
|
358
|
-
FREE_FUNCTION = 6
|
|
359
|
-
}
|
|
360
|
-
export declare function printMessageTrace(trace: PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace, depth?: number | undefined | null): void
|
|
361
369
|
export declare function printStackTrace(trace: SolidityStackTrace): void
|
|
362
370
|
/** Represents the exit code of the EVM. */
|
|
363
371
|
export const enum ExitCode {
|
|
@@ -380,51 +388,14 @@ export const enum ExitCode {
|
|
|
380
388
|
/** Unknown halt reason. */
|
|
381
389
|
UNKNOWN_HALT_REASON = 8
|
|
382
390
|
}
|
|
383
|
-
export
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
depth: number
|
|
392
|
-
precompile: number
|
|
393
|
-
calldata: Uint8Array
|
|
394
|
-
}
|
|
395
|
-
export interface CreateMessageTrace {
|
|
396
|
-
value: bigint
|
|
397
|
-
returnData: Uint8Array
|
|
398
|
-
exit: Exit
|
|
399
|
-
gasUsed: bigint
|
|
400
|
-
depth: number
|
|
401
|
-
code: Uint8Array
|
|
402
|
-
steps: Array<EvmStep | PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace>
|
|
403
|
-
/**
|
|
404
|
-
* Reference to the resolved `Bytecode` EDR data.
|
|
405
|
-
* Only used on the JS side by the `VmTraceDecoder` class.
|
|
406
|
-
*/
|
|
407
|
-
bytecode?: BytecodeWrapper
|
|
408
|
-
numberOfSubtraces: number
|
|
409
|
-
deployedContract?: Uint8Array | undefined
|
|
410
|
-
}
|
|
411
|
-
export interface CallMessageTrace {
|
|
412
|
-
value: bigint
|
|
413
|
-
returnData: Uint8Array
|
|
414
|
-
exit: Exit
|
|
415
|
-
gasUsed: bigint
|
|
416
|
-
depth: number
|
|
417
|
-
code: Uint8Array
|
|
418
|
-
steps: Array<EvmStep | PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace>
|
|
419
|
-
/**
|
|
420
|
-
* Reference to the resolved `Bytecode` EDR data.
|
|
421
|
-
* Only used on the JS side by the `VmTraceDecoder` class.
|
|
422
|
-
*/
|
|
423
|
-
bytecode?: BytecodeWrapper
|
|
424
|
-
numberOfSubtraces: number
|
|
425
|
-
calldata: Uint8Array
|
|
426
|
-
address: Uint8Array
|
|
427
|
-
codeAddress: Uint8Array
|
|
391
|
+
export const enum ContractFunctionType {
|
|
392
|
+
CONSTRUCTOR = 0,
|
|
393
|
+
FUNCTION = 1,
|
|
394
|
+
FALLBACK = 2,
|
|
395
|
+
RECEIVE = 3,
|
|
396
|
+
GETTER = 4,
|
|
397
|
+
MODIFIER = 5,
|
|
398
|
+
FREE_FUNCTION = 6
|
|
428
399
|
}
|
|
429
400
|
export const enum StackTraceEntryType {
|
|
430
401
|
CALLSTACK_ENTRY = 0,
|
|
@@ -580,11 +551,6 @@ export interface ContractCallRunOutOfGasError {
|
|
|
580
551
|
type: StackTraceEntryType.CONTRACT_CALL_RUN_OUT_OF_GAS_ERROR
|
|
581
552
|
sourceReference?: SourceReference
|
|
582
553
|
}
|
|
583
|
-
export interface ContractAndFunctionName {
|
|
584
|
-
contractName: string
|
|
585
|
-
functionName: string | undefined
|
|
586
|
-
}
|
|
587
|
-
export declare function initializeVmTraceDecoder(vmTraceDecoder: VmTraceDecoder, tracingConfig: any): void
|
|
588
554
|
export interface TracingMessage {
|
|
589
555
|
/** Sender address */
|
|
590
556
|
readonly caller: Buffer
|
|
@@ -628,6 +594,11 @@ export interface TracingMessageResult {
|
|
|
628
594
|
/** Execution result */
|
|
629
595
|
readonly executionResult: ExecutionResult
|
|
630
596
|
}
|
|
597
|
+
/**
|
|
598
|
+
* Returns the latest version of solc that EDR officially
|
|
599
|
+
* supports and is tested against.
|
|
600
|
+
*/
|
|
601
|
+
export declare function getLatestSupportedSolcVersion(): string
|
|
631
602
|
export interface Withdrawal {
|
|
632
603
|
/** The index of withdrawal */
|
|
633
604
|
index: bigint
|
|
@@ -645,7 +616,7 @@ export declare class EdrContext {
|
|
|
645
616
|
/** A JSON-RPC provider for Ethereum. */
|
|
646
617
|
export declare class Provider {
|
|
647
618
|
/**Constructs a new provider with the provided configuration. */
|
|
648
|
-
static withConfig(context: EdrContext, config: ProviderConfig, loggerConfig: LoggerConfig, subscriberCallback: (event: SubscriptionEvent) => void): Promise<Provider>
|
|
619
|
+
static withConfig(context: EdrContext, config: ProviderConfig, loggerConfig: LoggerConfig, tracingConfig: TracingConfigWithBuffers, subscriberCallback: (event: SubscriptionEvent) => void): Promise<Provider>
|
|
649
620
|
/**Handles a JSON-RPC request and returns a JSON-RPC response. */
|
|
650
621
|
handleRequest(jsonRequest: string): Promise<Response>
|
|
651
622
|
setCallOverrideCallback(callOverrideCallback: (contract_address: Buffer, data: Buffer) => Promise<CallOverrideResult | undefined>): void
|
|
@@ -660,19 +631,20 @@ export declare class Provider {
|
|
|
660
631
|
export declare class Response {
|
|
661
632
|
/** Returns the response data as a JSON string or a JSON object. */
|
|
662
633
|
get data(): string | any
|
|
663
|
-
get solidityTrace(): RawTrace | null
|
|
664
634
|
get traces(): Array<RawTrace>
|
|
635
|
+
/**Compute the error stack trace. Return the stack trace if it can be decoded, otherwise returns none. Throws if there was an error computing the stack trace. */
|
|
636
|
+
stackTrace(): SolidityStackTrace | null
|
|
665
637
|
}
|
|
666
|
-
/**
|
|
667
|
-
* Opaque handle to the `Bytecode` struct.
|
|
668
|
-
* Only used on the JS side by the `VmTraceDecoder` class.
|
|
669
|
-
*/
|
|
670
|
-
export declare class BytecodeWrapper { }
|
|
671
638
|
export declare class Exit {
|
|
672
639
|
get kind(): ExitCode
|
|
673
640
|
isError(): boolean
|
|
674
641
|
getReason(): string
|
|
675
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* Opaque handle to the `Bytecode` struct.
|
|
645
|
+
* Only used on the JS side by the `VmTraceDecoder` class.
|
|
646
|
+
*/
|
|
647
|
+
export declare class BytecodeWrapper { }
|
|
676
648
|
export declare class ReturnData {
|
|
677
649
|
readonly value: Uint8Array
|
|
678
650
|
constructor(value: Uint8Array)
|
|
@@ -682,26 +654,6 @@ export declare class ReturnData {
|
|
|
682
654
|
decodeError(): string
|
|
683
655
|
decodePanic(): bigint
|
|
684
656
|
}
|
|
685
|
-
export declare class SolidityTracer {
|
|
686
|
-
|
|
687
|
-
constructor()
|
|
688
|
-
getStackTrace(trace: PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace): SolidityStackTrace
|
|
689
|
-
}
|
|
690
|
-
export declare class VmTraceDecoder {
|
|
691
|
-
constructor()
|
|
692
|
-
addBytecode(bytecode: BytecodeWrapper): void
|
|
693
|
-
tryToDecodeMessageTrace(messageTrace: PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace): PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace
|
|
694
|
-
getContractAndFunctionNamesForCall(code: Uint8Array, calldata: Uint8Array | undefined): ContractAndFunctionName
|
|
695
|
-
}
|
|
696
|
-
export type VMTracer = VmTracer
|
|
697
|
-
/** N-API bindings for the Rust port of `VMTracer` from Hardhat. */
|
|
698
|
-
export declare class VmTracer {
|
|
699
|
-
constructor()
|
|
700
|
-
/** Observes a trace, collecting information about the execution of the EVM. */
|
|
701
|
-
observe(trace: RawTrace): void
|
|
702
|
-
getLastTopLevelMessageTrace(): PrecompileMessageTrace | CallMessageTrace | CreateMessageTrace | undefined
|
|
703
|
-
getLastError(): Error | undefined
|
|
704
|
-
}
|
|
705
657
|
export declare class RawTrace {
|
|
706
658
|
trace(): Array<TracingMessage | TracingStep | TracingMessageResult>
|
|
707
659
|
}
|
package/index.js
CHANGED
|
@@ -310,7 +310,7 @@ if (!nativeBinding) {
|
|
|
310
310
|
throw new Error(`Failed to load native binding`)
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
const { SpecId, EdrContext, MineOrdering, Provider, Response, SuccessReason, ExceptionalHalt,
|
|
313
|
+
const { SpecId, EdrContext, MineOrdering, Provider, Response, SuccessReason, ExceptionalHalt, linkHexStringBytecode, printStackTrace, Exit, ExitCode, BytecodeWrapper, ContractFunctionType, ReturnData, StackTraceEntryType, stackTraceEntryTypeToString, FALLBACK_FUNCTION_NAME, RECEIVE_FUNCTION_NAME, CONSTRUCTOR_FUNCTION_NAME, UNRECOGNIZED_FUNCTION_NAME, UNKNOWN_FUNCTION_NAME, PRECOMPILE_FUNCTION_NAME, UNRECOGNIZED_CONTRACT_NAME, RawTrace, getLatestSupportedSolcVersion } = nativeBinding
|
|
314
314
|
|
|
315
315
|
module.exports.SpecId = SpecId
|
|
316
316
|
module.exports.EdrContext = EdrContext
|
|
@@ -319,14 +319,12 @@ module.exports.Provider = Provider
|
|
|
319
319
|
module.exports.Response = Response
|
|
320
320
|
module.exports.SuccessReason = SuccessReason
|
|
321
321
|
module.exports.ExceptionalHalt = ExceptionalHalt
|
|
322
|
-
module.exports.createModelsAndDecodeBytecodes = createModelsAndDecodeBytecodes
|
|
323
322
|
module.exports.linkHexStringBytecode = linkHexStringBytecode
|
|
324
|
-
module.exports.BytecodeWrapper = BytecodeWrapper
|
|
325
|
-
module.exports.ContractFunctionType = ContractFunctionType
|
|
326
|
-
module.exports.printMessageTrace = printMessageTrace
|
|
327
323
|
module.exports.printStackTrace = printStackTrace
|
|
328
324
|
module.exports.Exit = Exit
|
|
329
325
|
module.exports.ExitCode = ExitCode
|
|
326
|
+
module.exports.BytecodeWrapper = BytecodeWrapper
|
|
327
|
+
module.exports.ContractFunctionType = ContractFunctionType
|
|
330
328
|
module.exports.ReturnData = ReturnData
|
|
331
329
|
module.exports.StackTraceEntryType = StackTraceEntryType
|
|
332
330
|
module.exports.stackTraceEntryTypeToString = stackTraceEntryTypeToString
|
|
@@ -337,8 +335,5 @@ module.exports.UNRECOGNIZED_FUNCTION_NAME = UNRECOGNIZED_FUNCTION_NAME
|
|
|
337
335
|
module.exports.UNKNOWN_FUNCTION_NAME = UNKNOWN_FUNCTION_NAME
|
|
338
336
|
module.exports.PRECOMPILE_FUNCTION_NAME = PRECOMPILE_FUNCTION_NAME
|
|
339
337
|
module.exports.UNRECOGNIZED_CONTRACT_NAME = UNRECOGNIZED_CONTRACT_NAME
|
|
340
|
-
module.exports.SolidityTracer = SolidityTracer
|
|
341
|
-
module.exports.VmTraceDecoder = VmTraceDecoder
|
|
342
|
-
module.exports.initializeVmTraceDecoder = initializeVmTraceDecoder
|
|
343
|
-
module.exports.VmTracer = VmTracer
|
|
344
338
|
module.exports.RawTrace = RawTrace
|
|
339
|
+
module.exports.getLatestSupportedSolcVersion = getLatestSupportedSolcVersion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nomicfoundation/edr",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"devDependencies": {
|
|
5
5
|
"@napi-rs/cli": "^2.18.4",
|
|
6
6
|
"@types/chai": "^4.2.0",
|
|
@@ -52,13 +52,13 @@
|
|
|
52
52
|
"repository": "NomicFoundation/edr.git",
|
|
53
53
|
"types": "index.d.ts",
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@nomicfoundation/edr-darwin-arm64": "0.
|
|
56
|
-
"@nomicfoundation/edr-darwin-x64": "0.
|
|
57
|
-
"@nomicfoundation/edr-linux-arm64-gnu": "0.
|
|
58
|
-
"@nomicfoundation/edr-linux-arm64-musl": "0.
|
|
59
|
-
"@nomicfoundation/edr-linux-x64-gnu": "0.
|
|
60
|
-
"@nomicfoundation/edr-linux-x64-musl": "0.
|
|
61
|
-
"@nomicfoundation/edr-win32-x64-msvc": "0.
|
|
55
|
+
"@nomicfoundation/edr-darwin-arm64": "0.8.0",
|
|
56
|
+
"@nomicfoundation/edr-darwin-x64": "0.8.0",
|
|
57
|
+
"@nomicfoundation/edr-linux-arm64-gnu": "0.8.0",
|
|
58
|
+
"@nomicfoundation/edr-linux-arm64-musl": "0.8.0",
|
|
59
|
+
"@nomicfoundation/edr-linux-x64-gnu": "0.8.0",
|
|
60
|
+
"@nomicfoundation/edr-linux-x64-musl": "0.8.0",
|
|
61
|
+
"@nomicfoundation/edr-win32-x64-msvc": "0.8.0"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"artifacts": "napi artifacts",
|
package/src/logger.rs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
use std::{
|
|
1
|
+
use std::{
|
|
2
|
+
fmt::Display,
|
|
3
|
+
sync::{mpsc::channel, Arc},
|
|
4
|
+
};
|
|
2
5
|
|
|
3
6
|
use ansi_term::{Color, Style};
|
|
4
7
|
use edr_eth::{
|
|
@@ -14,6 +17,7 @@ use edr_evm::{
|
|
|
14
17
|
ExecutionResult, SyncBlock,
|
|
15
18
|
};
|
|
16
19
|
use edr_provider::{ProviderError, TransactionFailure};
|
|
20
|
+
use edr_solidity::contract_decoder::ContractDecoder;
|
|
17
21
|
use itertools::izip;
|
|
18
22
|
use napi::{
|
|
19
23
|
threadsafe_function::{
|
|
@@ -41,21 +45,12 @@ impl TryCast<(String, Option<String>)> for ContractAndFunctionName {
|
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
struct ContractAndFunctionNameCall {
|
|
45
|
-
code: Bytes,
|
|
46
|
-
/// Only present for calls.
|
|
47
|
-
calldata: Option<Bytes>,
|
|
48
|
-
}
|
|
49
|
-
|
|
50
48
|
#[napi(object)]
|
|
51
49
|
pub struct LoggerConfig {
|
|
52
50
|
/// Whether to enable the logger.
|
|
53
51
|
pub enable: bool,
|
|
54
52
|
#[napi(ts_type = "(inputs: Buffer[]) => string[]")]
|
|
55
53
|
pub decode_console_log_inputs_callback: JsFunction,
|
|
56
|
-
#[napi(ts_type = "(code: Buffer, calldata?: Buffer) => ContractAndFunctionName")]
|
|
57
|
-
/// Used to resolve the contract and function name when logging.
|
|
58
|
-
pub get_contract_and_function_name_callback: JsFunction,
|
|
59
54
|
#[napi(ts_type = "(message: string, replace: boolean) => void")]
|
|
60
55
|
pub print_line_callback: JsFunction,
|
|
61
56
|
}
|
|
@@ -118,9 +113,13 @@ pub struct Logger {
|
|
|
118
113
|
}
|
|
119
114
|
|
|
120
115
|
impl Logger {
|
|
121
|
-
pub fn new(
|
|
116
|
+
pub fn new(
|
|
117
|
+
env: &Env,
|
|
118
|
+
config: LoggerConfig,
|
|
119
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
120
|
+
) -> napi::Result<Self> {
|
|
122
121
|
Ok(Self {
|
|
123
|
-
collector: LogCollector::new(env, config)?,
|
|
122
|
+
collector: LogCollector::new(env, config, contract_decoder)?,
|
|
124
123
|
})
|
|
125
124
|
}
|
|
126
125
|
}
|
|
@@ -235,6 +234,15 @@ impl edr_provider::Logger for Logger {
|
|
|
235
234
|
|
|
236
235
|
Ok(())
|
|
237
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
|
+
}
|
|
238
246
|
}
|
|
239
247
|
|
|
240
248
|
#[derive(Clone)]
|
|
@@ -245,9 +253,8 @@ pub struct CollapsedMethod {
|
|
|
245
253
|
|
|
246
254
|
#[derive(Clone)]
|
|
247
255
|
struct LogCollector {
|
|
256
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
248
257
|
decode_console_log_inputs_fn: ThreadsafeFunction<Vec<Bytes>, ErrorStrategy::Fatal>,
|
|
249
|
-
get_contract_and_function_name_fn:
|
|
250
|
-
ThreadsafeFunction<ContractAndFunctionNameCall, ErrorStrategy::Fatal>,
|
|
251
258
|
indentation: usize,
|
|
252
259
|
is_enabled: bool,
|
|
253
260
|
logs: Vec<LogLine>,
|
|
@@ -257,7 +264,11 @@ struct LogCollector {
|
|
|
257
264
|
}
|
|
258
265
|
|
|
259
266
|
impl LogCollector {
|
|
260
|
-
pub fn new(
|
|
267
|
+
pub fn new(
|
|
268
|
+
env: &Env,
|
|
269
|
+
config: LoggerConfig,
|
|
270
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
271
|
+
) -> napi::Result<Self> {
|
|
261
272
|
let mut decode_console_log_inputs_fn = config
|
|
262
273
|
.decode_console_log_inputs_callback
|
|
263
274
|
.create_threadsafe_function(0, |ctx: ThreadSafeCallContext<Vec<Bytes>>| {
|
|
@@ -281,34 +292,6 @@ impl LogCollector {
|
|
|
281
292
|
// exiting.
|
|
282
293
|
decode_console_log_inputs_fn.unref(env)?;
|
|
283
294
|
|
|
284
|
-
let mut get_contract_and_function_name_fn = config
|
|
285
|
-
.get_contract_and_function_name_callback
|
|
286
|
-
.create_threadsafe_function(
|
|
287
|
-
0,
|
|
288
|
-
|ctx: ThreadSafeCallContext<ContractAndFunctionNameCall>| {
|
|
289
|
-
// Buffer
|
|
290
|
-
let code = ctx
|
|
291
|
-
.env
|
|
292
|
-
.create_buffer_with_data(ctx.value.code.to_vec())?
|
|
293
|
-
.into_unknown();
|
|
294
|
-
|
|
295
|
-
// Option<Buffer>
|
|
296
|
-
let calldata = if let Some(calldata) = ctx.value.calldata {
|
|
297
|
-
ctx.env
|
|
298
|
-
.create_buffer_with_data(calldata.to_vec())?
|
|
299
|
-
.into_unknown()
|
|
300
|
-
} else {
|
|
301
|
-
ctx.env.get_undefined()?.into_unknown()
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
Ok(vec![code, calldata])
|
|
305
|
-
},
|
|
306
|
-
)?;
|
|
307
|
-
|
|
308
|
-
// Maintain a weak reference to the function to avoid the event loop from
|
|
309
|
-
// exiting.
|
|
310
|
-
get_contract_and_function_name_fn.unref(env)?;
|
|
311
|
-
|
|
312
295
|
let mut print_line_fn = config.print_line_callback.create_threadsafe_function(
|
|
313
296
|
0,
|
|
314
297
|
|ctx: ThreadSafeCallContext<(String, bool)>| {
|
|
@@ -327,8 +310,8 @@ impl LogCollector {
|
|
|
327
310
|
print_line_fn.unref(env)?;
|
|
328
311
|
|
|
329
312
|
Ok(Self {
|
|
313
|
+
contract_decoder,
|
|
330
314
|
decode_console_log_inputs_fn,
|
|
331
|
-
get_contract_and_function_name_fn,
|
|
332
315
|
indentation: 0,
|
|
333
316
|
is_enabled: config.enable,
|
|
334
317
|
logs: Vec::new(),
|
|
@@ -560,29 +543,13 @@ impl LogCollector {
|
|
|
560
543
|
code: Bytes,
|
|
561
544
|
calldata: Option<Bytes>,
|
|
562
545
|
) -> (String, Option<String>) {
|
|
563
|
-
let
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
move |result: ContractAndFunctionName| {
|
|
571
|
-
let contract_and_function_name = result.try_cast();
|
|
572
|
-
sender.send(contract_and_function_name).map_err(|_error| {
|
|
573
|
-
napi::Error::new(
|
|
574
|
-
Status::GenericFailure,
|
|
575
|
-
"Failed to send result from get_contract_and_function_name",
|
|
576
|
-
)
|
|
577
|
-
})
|
|
578
|
-
},
|
|
579
|
-
);
|
|
580
|
-
assert_eq!(status, Status::Ok);
|
|
581
|
-
|
|
582
|
-
receiver
|
|
583
|
-
.recv()
|
|
584
|
-
.unwrap()
|
|
585
|
-
.expect("Failed call to get_contract_and_function_name")
|
|
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)
|
|
586
553
|
}
|
|
587
554
|
|
|
588
555
|
fn format(&self, message: impl ToString) -> String {
|
package/src/provider.rs
CHANGED
|
@@ -4,7 +4,10 @@ use std::sync::Arc;
|
|
|
4
4
|
|
|
5
5
|
use edr_provider::{time::CurrentTime, InvalidRequestReason};
|
|
6
6
|
use edr_rpc_eth::jsonrpc;
|
|
7
|
-
use
|
|
7
|
+
use edr_solidity::contract_decoder::ContractDecoder;
|
|
8
|
+
use napi::{
|
|
9
|
+
bindgen_prelude::Uint8Array, tokio::runtime, Either, Env, JsFunction, JsObject, Status,
|
|
10
|
+
};
|
|
8
11
|
use napi_derive::napi;
|
|
9
12
|
|
|
10
13
|
use self::config::ProviderConfig;
|
|
@@ -13,7 +16,7 @@ use crate::{
|
|
|
13
16
|
context::EdrContext,
|
|
14
17
|
logger::{Logger, LoggerConfig, LoggerError},
|
|
15
18
|
subscribe::SubscriberCallback,
|
|
16
|
-
trace::RawTrace,
|
|
19
|
+
trace::{solidity_stack_trace::SolidityStackTrace, RawTrace},
|
|
17
20
|
};
|
|
18
21
|
|
|
19
22
|
/// A JSON-RPC provider for Ethereum.
|
|
@@ -21,6 +24,7 @@ use crate::{
|
|
|
21
24
|
pub struct Provider {
|
|
22
25
|
provider: Arc<edr_provider::Provider<LoggerError>>,
|
|
23
26
|
runtime: runtime::Handle,
|
|
27
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
24
28
|
#[cfg(feature = "scenarios")]
|
|
25
29
|
scenario_file: Option<napi::tokio::sync::Mutex<napi::tokio::fs::File>>,
|
|
26
30
|
}
|
|
@@ -35,12 +39,26 @@ impl Provider {
|
|
|
35
39
|
_context: &EdrContext,
|
|
36
40
|
config: ProviderConfig,
|
|
37
41
|
logger_config: LoggerConfig,
|
|
42
|
+
tracing_config: TracingConfigWithBuffers,
|
|
38
43
|
#[napi(ts_arg_type = "(event: SubscriptionEvent) => void")] subscriber_callback: JsFunction,
|
|
39
44
|
) -> napi::Result<JsObject> {
|
|
40
|
-
let config = edr_provider::ProviderConfig::try_from(config)?;
|
|
41
45
|
let runtime = runtime::Handle::current();
|
|
42
46
|
|
|
43
|
-
let
|
|
47
|
+
let config = edr_provider::ProviderConfig::try_from(config)?;
|
|
48
|
+
|
|
49
|
+
// TODO https://github.com/NomicFoundation/edr/issues/760
|
|
50
|
+
let build_info_config =
|
|
51
|
+
edr_solidity::artifacts::BuildInfoConfig::parse_from_buffers((&tracing_config).into())
|
|
52
|
+
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
53
|
+
let contract_decoder = ContractDecoder::new(&build_info_config)
|
|
54
|
+
.map_err(|error| napi::Error::from_reason(error.to_string()))?;
|
|
55
|
+
let contract_decoder = Arc::new(contract_decoder);
|
|
56
|
+
|
|
57
|
+
let logger = Box::new(Logger::new(
|
|
58
|
+
&env,
|
|
59
|
+
logger_config,
|
|
60
|
+
Arc::clone(&contract_decoder),
|
|
61
|
+
)?);
|
|
44
62
|
let subscriber_callback = SubscriberCallback::new(&env, subscriber_callback)?;
|
|
45
63
|
let subscriber_callback = Box::new(move |event| subscriber_callback.call(event));
|
|
46
64
|
|
|
@@ -58,6 +76,7 @@ impl Provider {
|
|
|
58
76
|
logger,
|
|
59
77
|
subscriber_callback,
|
|
60
78
|
config,
|
|
79
|
+
Arc::clone(&contract_decoder),
|
|
61
80
|
CurrentTime,
|
|
62
81
|
)
|
|
63
82
|
.map_or_else(
|
|
@@ -66,6 +85,7 @@ impl Provider {
|
|
|
66
85
|
Ok(Provider {
|
|
67
86
|
provider: Arc::new(provider),
|
|
68
87
|
runtime,
|
|
88
|
+
contract_decoder,
|
|
69
89
|
#[cfg(feature = "scenarios")]
|
|
70
90
|
scenario_file,
|
|
71
91
|
})
|
|
@@ -184,10 +204,16 @@ impl Provider {
|
|
|
184
204
|
}
|
|
185
205
|
})
|
|
186
206
|
.map_err(|error| napi::Error::new(Status::GenericFailure, error.to_string()))
|
|
187
|
-
.map(|data|
|
|
188
|
-
solidity_trace
|
|
189
|
-
|
|
190
|
-
|
|
207
|
+
.map(|data| {
|
|
208
|
+
let solidity_trace = solidity_trace.map(|trace| SolidityTraceData {
|
|
209
|
+
trace,
|
|
210
|
+
contract_decoder: Arc::clone(&self.contract_decoder),
|
|
211
|
+
});
|
|
212
|
+
Response {
|
|
213
|
+
solidity_trace,
|
|
214
|
+
data,
|
|
215
|
+
traces: traces.into_iter().map(Arc::new).collect(),
|
|
216
|
+
}
|
|
191
217
|
})
|
|
192
218
|
}
|
|
193
219
|
|
|
@@ -222,6 +248,72 @@ impl Provider {
|
|
|
222
248
|
}
|
|
223
249
|
}
|
|
224
250
|
|
|
251
|
+
/// Tracing config for Solidity stack trace generation.
|
|
252
|
+
#[napi(object)]
|
|
253
|
+
pub struct TracingConfigWithBuffers {
|
|
254
|
+
/// Build information to use for decoding contracts. Either a Hardhat v2
|
|
255
|
+
/// build info file that contains both input and output or a Hardhat v3
|
|
256
|
+
/// build info file that doesn't contain output and a separate output file.
|
|
257
|
+
pub build_infos: Option<Either<Vec<Uint8Array>, Vec<BuildInfoAndOutput>>>,
|
|
258
|
+
/// Whether to ignore contracts whose name starts with "Ignored".
|
|
259
|
+
pub ignore_contracts: Option<bool>,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/// Hardhat V3 build info where the compiler output is not part of the build
|
|
263
|
+
/// info file.
|
|
264
|
+
#[napi(object)]
|
|
265
|
+
pub struct BuildInfoAndOutput {
|
|
266
|
+
/// The build info input file
|
|
267
|
+
pub build_info: Uint8Array,
|
|
268
|
+
/// The build info output file
|
|
269
|
+
pub output: Uint8Array,
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
impl<'a> From<&'a BuildInfoAndOutput>
|
|
273
|
+
for edr_solidity::artifacts::BuildInfoBufferSeparateOutput<'a>
|
|
274
|
+
{
|
|
275
|
+
fn from(value: &'a BuildInfoAndOutput) -> Self {
|
|
276
|
+
Self {
|
|
277
|
+
build_info: value.build_info.as_ref(),
|
|
278
|
+
output: value.output.as_ref(),
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
impl<'a> From<&'a TracingConfigWithBuffers>
|
|
284
|
+
for edr_solidity::artifacts::BuildInfoConfigWithBuffers<'a>
|
|
285
|
+
{
|
|
286
|
+
fn from(value: &'a TracingConfigWithBuffers) -> Self {
|
|
287
|
+
use edr_solidity::artifacts::{BuildInfoBufferSeparateOutput, BuildInfoBuffers};
|
|
288
|
+
|
|
289
|
+
let build_infos = value.build_infos.as_ref().map(|infos| match infos {
|
|
290
|
+
Either::A(with_output) => BuildInfoBuffers::WithOutput(
|
|
291
|
+
with_output
|
|
292
|
+
.iter()
|
|
293
|
+
.map(std::convert::AsRef::as_ref)
|
|
294
|
+
.collect(),
|
|
295
|
+
),
|
|
296
|
+
Either::B(separate_output) => BuildInfoBuffers::SeparateInputOutput(
|
|
297
|
+
separate_output
|
|
298
|
+
.iter()
|
|
299
|
+
.map(BuildInfoBufferSeparateOutput::from)
|
|
300
|
+
.collect(),
|
|
301
|
+
),
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
Self {
|
|
305
|
+
build_infos,
|
|
306
|
+
ignore_contracts: value.ignore_contracts,
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
#[derive(Debug)]
|
|
312
|
+
struct SolidityTraceData {
|
|
313
|
+
trace: Arc<edr_evm::trace::Trace>,
|
|
314
|
+
contract_decoder: Arc<ContractDecoder>,
|
|
315
|
+
}
|
|
316
|
+
|
|
225
317
|
#[napi]
|
|
226
318
|
pub struct Response {
|
|
227
319
|
// N-API is known to be slow when marshalling `serde_json::Value`s, so we try to return a
|
|
@@ -230,7 +322,7 @@ pub struct Response {
|
|
|
230
322
|
data: Either<String, serde_json::Value>,
|
|
231
323
|
/// When a transaction fails to execute, the provider returns a trace of the
|
|
232
324
|
/// transaction.
|
|
233
|
-
solidity_trace: Option<
|
|
325
|
+
solidity_trace: Option<SolidityTraceData>,
|
|
234
326
|
/// This may contain zero or more traces, depending on the (batch) request
|
|
235
327
|
traces: Vec<Arc<edr_evm::trace::Trace>>,
|
|
236
328
|
}
|
|
@@ -243,13 +335,6 @@ impl Response {
|
|
|
243
335
|
self.data.clone()
|
|
244
336
|
}
|
|
245
337
|
|
|
246
|
-
#[napi(getter)]
|
|
247
|
-
pub fn solidity_trace(&self) -> Option<RawTrace> {
|
|
248
|
-
self.solidity_trace
|
|
249
|
-
.as_ref()
|
|
250
|
-
.map(|trace| RawTrace::new(trace.clone()))
|
|
251
|
-
}
|
|
252
|
-
|
|
253
338
|
#[napi(getter)]
|
|
254
339
|
pub fn traces(&self) -> Vec<RawTrace> {
|
|
255
340
|
self.traces
|
|
@@ -257,4 +342,35 @@ impl Response {
|
|
|
257
342
|
.map(|trace| RawTrace::new(trace.clone()))
|
|
258
343
|
.collect()
|
|
259
344
|
}
|
|
345
|
+
|
|
346
|
+
// Rust port of https://github.com/NomicFoundation/hardhat/blob/c20bf195a6efdc2d74e778b7a4a7799aac224841/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts#L590
|
|
347
|
+
#[doc = "Compute the error stack trace. Return the stack trace if it can be decoded, otherwise returns none. Throws if there was an error computing the stack trace."]
|
|
348
|
+
#[napi]
|
|
349
|
+
pub fn stack_trace(&self) -> napi::Result<Option<SolidityStackTrace>> {
|
|
350
|
+
let Some(SolidityTraceData {
|
|
351
|
+
trace,
|
|
352
|
+
contract_decoder,
|
|
353
|
+
}) = &self.solidity_trace
|
|
354
|
+
else {
|
|
355
|
+
return Ok(None);
|
|
356
|
+
};
|
|
357
|
+
let nested_trace = edr_solidity::nested_tracer::convert_trace_messages_to_nested_trace(
|
|
358
|
+
trace.as_ref().clone(),
|
|
359
|
+
)
|
|
360
|
+
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
361
|
+
|
|
362
|
+
if let Some(vm_trace) = nested_trace {
|
|
363
|
+
let decoded_trace = contract_decoder.try_to_decode_message_trace(vm_trace);
|
|
364
|
+
let stack_trace = edr_solidity::solidity_tracer::get_stack_trace(decoded_trace)
|
|
365
|
+
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
366
|
+
let stack_trace = stack_trace
|
|
367
|
+
.into_iter()
|
|
368
|
+
.map(super::cast::TryCast::try_cast)
|
|
369
|
+
.collect::<Result<Vec<_>, _>>()?;
|
|
370
|
+
|
|
371
|
+
Ok(Some(stack_trace))
|
|
372
|
+
} else {
|
|
373
|
+
Ok(None)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
260
376
|
}
|