@nomicfoundation/edr 0.5.1 → 0.6.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.
@@ -0,0 +1,86 @@
1
+ //! Naive rewrite of `hardhat-network/provider/vm/exit.ts` from Hardhat.
2
+ //! Used together with `VmTracer`.
3
+
4
+ use std::fmt;
5
+
6
+ use edr_evm::HaltReason;
7
+ use napi_derive::napi;
8
+
9
+ #[napi]
10
+ pub struct Exit(pub(crate) ExitCode);
11
+
12
+ #[napi]
13
+ /// Represents the exit code of the EVM.
14
+ #[derive(Debug, PartialEq, Eq)]
15
+ #[allow(clippy::upper_case_acronyms, non_camel_case_types)] // These are exported and mapped 1:1 to existing JS enum
16
+ pub enum ExitCode {
17
+ /// Execution was successful.
18
+ SUCCESS = 0,
19
+ /// Execution was reverted.
20
+ REVERT,
21
+ /// Execution ran out of gas.
22
+ OUT_OF_GAS,
23
+ /// Execution encountered an internal error.
24
+ INTERNAL_ERROR,
25
+ /// Execution encountered an invalid opcode.
26
+ INVALID_OPCODE,
27
+ /// Execution encountered a stack underflow.
28
+ STACK_UNDERFLOW,
29
+ /// Create init code size exceeds limit (runtime).
30
+ CODESIZE_EXCEEDS_MAXIMUM,
31
+ /// Create collision.
32
+ CREATE_COLLISION,
33
+ }
34
+
35
+ impl fmt::Display for ExitCode {
36
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37
+ match self {
38
+ ExitCode::SUCCESS => write!(f, "Success"),
39
+ ExitCode::REVERT => write!(f, "Reverted"),
40
+ ExitCode::OUT_OF_GAS => write!(f, "Out of gas"),
41
+ ExitCode::INTERNAL_ERROR => write!(f, "Internal error"),
42
+ ExitCode::INVALID_OPCODE => write!(f, "Invalid opcode"),
43
+ ExitCode::STACK_UNDERFLOW => write!(f, "Stack underflow"),
44
+ ExitCode::CODESIZE_EXCEEDS_MAXIMUM => write!(f, "Codesize exceeds maximum"),
45
+ ExitCode::CREATE_COLLISION => write!(f, "Create collision"),
46
+ }
47
+ }
48
+ }
49
+
50
+ #[allow(clippy::fallible_impl_from)] // naively ported for now
51
+ impl From<edr_solidity::message_trace::ExitCode> for ExitCode {
52
+ fn from(code: edr_solidity::message_trace::ExitCode) -> Self {
53
+ use edr_solidity::message_trace::ExitCode;
54
+
55
+ match code {
56
+ ExitCode::Success => Self::SUCCESS,
57
+ ExitCode::Revert => Self::REVERT,
58
+ ExitCode::Halt(HaltReason::OutOfGas(_)) => Self::OUT_OF_GAS,
59
+ ExitCode::Halt(HaltReason::OpcodeNotFound | HaltReason::InvalidFEOpcode
60
+ // Returned when an opcode is not implemented for the hardfork
61
+ | HaltReason::NotActivated) => Self::INVALID_OPCODE,
62
+ ExitCode::Halt(HaltReason::StackUnderflow) => Self::STACK_UNDERFLOW,
63
+ ExitCode::Halt(HaltReason::CreateContractSizeLimit) => Self::CODESIZE_EXCEEDS_MAXIMUM,
64
+ ExitCode::Halt(HaltReason::CreateCollision) => Self::CREATE_COLLISION,
65
+ halt @ ExitCode::Halt(_) => panic!("Unmatched EDR exceptional halt: {halt:?}"),
66
+ }
67
+ }
68
+ }
69
+
70
+ #[napi]
71
+ impl Exit {
72
+ #[napi(getter)]
73
+ pub fn kind(&self) -> ExitCode {
74
+ self.0
75
+ }
76
+
77
+ #[napi]
78
+ pub fn is_error(&self) -> bool {
79
+ !matches!(self.0, ExitCode::SUCCESS)
80
+ }
81
+
82
+ #[napi]
83
+ pub fn get_reason(&self) -> String {
84
+ self.0.to_string()
85
+ }
86
+ }
@@ -0,0 +1,6 @@
1
+ use napi_derive::napi;
2
+
3
+ #[napi]
4
+ pub fn link_hex_string_bytecode(code: String, address: String, position: u32) -> String {
5
+ edr_solidity::library_utils::link_hex_string_bytecode(code, &address, position)
6
+ }
@@ -0,0 +1,180 @@
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
+ }
@@ -0,0 +1,179 @@
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
+ }
@@ -0,0 +1,59 @@
1
+ use std::rc::Rc;
2
+
3
+ use edr_solidity::build_model::Bytecode;
4
+ use napi_derive::napi;
5
+ use serde::Serialize;
6
+
7
+ /// Opaque handle to the `Bytecode` struct.
8
+ /// Only used on the JS side by the `VmTraceDecoder` class.
9
+ // NOTE: Needed, because we store the resolved `Bytecode` in the MessageTrace
10
+ // JS plain objects and those need a dedicated (class) type.
11
+ #[napi]
12
+ pub struct BytecodeWrapper(pub(crate) Rc<Bytecode>);
13
+
14
+ impl BytecodeWrapper {
15
+ pub fn new(bytecode: Rc<Bytecode>) -> Self {
16
+ Self(bytecode)
17
+ }
18
+
19
+ pub fn inner(&self) -> &Rc<Bytecode> {
20
+ &self.0
21
+ }
22
+ }
23
+
24
+ impl std::ops::Deref for BytecodeWrapper {
25
+ type Target = Bytecode;
26
+
27
+ fn deref(&self) -> &Self::Target {
28
+ &self.0
29
+ }
30
+ }
31
+
32
+ #[derive(Debug, PartialEq, Eq, Serialize)]
33
+ #[allow(non_camel_case_types)] // intentionally mimicks the original case in TS
34
+ #[allow(clippy::upper_case_acronyms)]
35
+ #[napi]
36
+ // Mimicks [`edr_solidity::build_model::ContractFunctionType`].
37
+ pub enum ContractFunctionType {
38
+ CONSTRUCTOR,
39
+ FUNCTION,
40
+ FALLBACK,
41
+ RECEIVE,
42
+ GETTER,
43
+ MODIFIER,
44
+ FREE_FUNCTION,
45
+ }
46
+
47
+ impl From<edr_solidity::build_model::ContractFunctionType> for ContractFunctionType {
48
+ fn from(value: edr_solidity::build_model::ContractFunctionType) -> Self {
49
+ match value {
50
+ edr_solidity::build_model::ContractFunctionType::Constructor => Self::CONSTRUCTOR,
51
+ edr_solidity::build_model::ContractFunctionType::Function => Self::FUNCTION,
52
+ edr_solidity::build_model::ContractFunctionType::Fallback => Self::FALLBACK,
53
+ edr_solidity::build_model::ContractFunctionType::Receive => Self::RECEIVE,
54
+ edr_solidity::build_model::ContractFunctionType::Getter => Self::GETTER,
55
+ edr_solidity::build_model::ContractFunctionType::Modifier => Self::MODIFIER,
56
+ edr_solidity::build_model::ContractFunctionType::FreeFunction => Self::FREE_FUNCTION,
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,84 @@
1
+ //! Rewrite of `hardhat-network/provider/return-data.ts` from Hardhat.
2
+
3
+ use alloy_sol_types::SolError;
4
+ use napi::bindgen_prelude::{BigInt, Uint8Array};
5
+ use napi_derive::napi;
6
+
7
+ // Built-in error types
8
+ // See <https://docs.soliditylang.org/en/v0.8.26/control-structures.html#error-handling-assert-require-revert-and-exceptions>
9
+ alloy_sol_types::sol! {
10
+ error Error(string);
11
+ error Panic(uint256);
12
+ }
13
+
14
+ #[napi]
15
+ pub struct ReturnData {
16
+ #[napi(readonly)]
17
+ pub value: Uint8Array,
18
+ selector: Option<[u8; 4]>,
19
+ }
20
+
21
+ #[napi]
22
+ impl ReturnData {
23
+ #[napi(constructor)]
24
+ pub fn new(value: Uint8Array) -> Self {
25
+ let selector = if value.len() >= 4 {
26
+ Some(value[0..4].try_into().unwrap())
27
+ } else {
28
+ None
29
+ };
30
+
31
+ Self { value, selector }
32
+ }
33
+
34
+ #[napi]
35
+ pub fn is_empty(&self) -> bool {
36
+ self.value.is_empty()
37
+ }
38
+
39
+ pub fn matches_selector(&self, selector: impl AsRef<[u8]>) -> bool {
40
+ self.selector
41
+ .map_or(false, |value| value == selector.as_ref())
42
+ }
43
+
44
+ #[napi]
45
+ pub fn is_error_return_data(&self) -> bool {
46
+ self.selector == Some(Error::SELECTOR)
47
+ }
48
+
49
+ #[napi]
50
+ pub fn is_panic_return_data(&self) -> bool {
51
+ self.selector == Some(Panic::SELECTOR)
52
+ }
53
+
54
+ #[napi]
55
+ pub fn decode_error(&self) -> napi::Result<String> {
56
+ if self.is_empty() {
57
+ return Ok(String::new());
58
+ }
59
+
60
+ let result = Error::abi_decode(&self.value[..], false).map_err(|_err| {
61
+ napi::Error::new(
62
+ napi::Status::InvalidArg,
63
+ "Expected return data to be a Error(string) and contain a valid string",
64
+ )
65
+ })?;
66
+
67
+ Ok(result._0)
68
+ }
69
+
70
+ #[napi]
71
+ pub fn decode_panic(&self) -> napi::Result<BigInt> {
72
+ let result = Panic::abi_decode(&self.value[..], false).map_err(|_err| {
73
+ napi::Error::new(
74
+ napi::Status::InvalidArg,
75
+ "Expected return data to be a Error(string) and contain a valid string",
76
+ )
77
+ })?;
78
+
79
+ Ok(BigInt {
80
+ sign_bit: false,
81
+ words: result._0.as_limbs().to_vec(),
82
+ })
83
+ }
84
+ }