@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.
- package/Cargo.toml +61 -27
- package/LICENSE +5 -1
- package/index.d.ts +875 -137
- package/index.js +61 -3
- package/package.json +20 -16
- package/src/account.rs +109 -32
- package/src/block.rs +2 -103
- package/src/call_override.rs +7 -7
- package/src/cast.rs +47 -17
- package/src/chains/generic.rs +51 -0
- package/src/chains/l1.rs +262 -0
- package/src/chains/op.rs +425 -0
- package/src/chains.rs +7 -0
- package/src/config.rs +537 -67
- package/src/context.rs +374 -17
- package/src/debug_trace.rs +2 -2
- package/src/instrument.rs +109 -0
- package/src/lib.rs +38 -14
- package/src/log.rs +12 -14
- package/src/logger.rs +77 -1177
- package/src/mock.rs +68 -0
- package/src/precompile.rs +50 -0
- package/src/provider/factory.rs +22 -0
- package/src/provider/response.rs +73 -0
- package/src/provider.rs +64 -325
- package/src/result.rs +60 -69
- package/src/scenarios.rs +11 -17
- package/src/serde.rs +57 -0
- package/src/solidity_tests/artifact.rs +184 -0
- package/src/solidity_tests/config.rs +725 -0
- package/src/solidity_tests/factory.rs +22 -0
- package/src/solidity_tests/l1.rs +68 -0
- package/src/solidity_tests/op.rs +69 -0
- package/src/solidity_tests/runner.rs +51 -0
- package/src/solidity_tests/test_results.rs +668 -0
- package/src/solidity_tests.rs +56 -0
- package/src/subscription.rs +32 -0
- package/src/trace/debug.rs +1 -1
- package/src/trace/exit.rs +12 -13
- package/src/trace/library_utils.rs +1 -1
- package/src/trace/return_data.rs +11 -11
- package/src/trace/solidity_stack_trace.rs +11 -8
- package/src/trace.rs +37 -44
- package/src/withdrawal.rs +4 -4
- package/src/provider/config.rs +0 -291
- package/src/subscribe.rs +0 -63
package/src/result.rs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use edr_evm::trace::AfterMessage;
|
|
2
2
|
use napi::{
|
|
3
|
-
bindgen_prelude::{BigInt,
|
|
4
|
-
Either,
|
|
3
|
+
bindgen_prelude::{BigInt, Either3, Uint8Array},
|
|
4
|
+
Either,
|
|
5
5
|
};
|
|
6
6
|
use napi_derive::napi;
|
|
7
7
|
|
|
@@ -19,18 +19,18 @@ pub enum SuccessReason {
|
|
|
19
19
|
EofReturnContract,
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
impl From<
|
|
23
|
-
fn from(eval:
|
|
22
|
+
impl From<edr_eth::result::SuccessReason> for SuccessReason {
|
|
23
|
+
fn from(eval: edr_eth::result::SuccessReason) -> Self {
|
|
24
24
|
match eval {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
edr_eth::result::SuccessReason::Stop => Self::Stop,
|
|
26
|
+
edr_eth::result::SuccessReason::Return => Self::Return,
|
|
27
|
+
edr_eth::result::SuccessReason::SelfDestruct => Self::SelfDestruct,
|
|
28
|
+
edr_eth::result::SuccessReason::EofReturnContract => Self::EofReturnContract,
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
impl From<SuccessReason> for
|
|
33
|
+
impl From<SuccessReason> for edr_eth::result::SuccessReason {
|
|
34
34
|
fn from(value: SuccessReason) -> Self {
|
|
35
35
|
match value {
|
|
36
36
|
SuccessReason::Stop => Self::Stop,
|
|
@@ -44,15 +44,15 @@ impl From<SuccessReason> for edr_evm::SuccessReason {
|
|
|
44
44
|
#[napi(object)]
|
|
45
45
|
pub struct CallOutput {
|
|
46
46
|
/// Return value
|
|
47
|
-
pub return_value:
|
|
47
|
+
pub return_value: Uint8Array,
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
#[napi(object)]
|
|
51
51
|
pub struct CreateOutput {
|
|
52
52
|
/// Return value
|
|
53
|
-
pub return_value:
|
|
53
|
+
pub return_value: Uint8Array,
|
|
54
54
|
/// Optionally, a 160-bit address
|
|
55
|
-
pub address: Option<
|
|
55
|
+
pub address: Option<Uint8Array>,
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/// The result when the EVM terminates successfully.
|
|
@@ -76,7 +76,7 @@ pub struct RevertResult {
|
|
|
76
76
|
/// The amount of gas used
|
|
77
77
|
pub gas_used: BigInt,
|
|
78
78
|
/// The transaction output
|
|
79
|
-
pub output:
|
|
79
|
+
pub output: Uint8Array,
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/// Indicates that the EVM has experienced an exceptional halt. This causes
|
|
@@ -105,55 +105,55 @@ pub enum ExceptionalHalt {
|
|
|
105
105
|
/// Aud data is smaller then already present data size.
|
|
106
106
|
EofAuxDataTooSmall,
|
|
107
107
|
/// EOF Subroutine stack overflow
|
|
108
|
-
|
|
108
|
+
SubRoutineStackOverflow,
|
|
109
109
|
/// Check for target address validity is only done inside subcall.
|
|
110
110
|
InvalidEXTCALLTarget,
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
impl From<
|
|
114
|
-
fn from(halt:
|
|
113
|
+
impl From<edr_eth::l1::HaltReason> for ExceptionalHalt {
|
|
114
|
+
fn from(halt: edr_eth::l1::HaltReason) -> Self {
|
|
115
115
|
match halt {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
116
|
+
edr_eth::l1::HaltReason::OutOfGas(..) => ExceptionalHalt::OutOfGas,
|
|
117
|
+
edr_eth::l1::HaltReason::OpcodeNotFound => ExceptionalHalt::OpcodeNotFound,
|
|
118
|
+
edr_eth::l1::HaltReason::InvalidFEOpcode => ExceptionalHalt::InvalidFEOpcode,
|
|
119
|
+
edr_eth::l1::HaltReason::InvalidJump => ExceptionalHalt::InvalidJump,
|
|
120
|
+
edr_eth::l1::HaltReason::NotActivated => ExceptionalHalt::NotActivated,
|
|
121
|
+
edr_eth::l1::HaltReason::StackUnderflow => ExceptionalHalt::StackUnderflow,
|
|
122
|
+
edr_eth::l1::HaltReason::StackOverflow => ExceptionalHalt::StackOverflow,
|
|
123
|
+
edr_eth::l1::HaltReason::OutOfOffset => ExceptionalHalt::OutOfOffset,
|
|
124
|
+
edr_eth::l1::HaltReason::CreateCollision => ExceptionalHalt::CreateCollision,
|
|
125
|
+
edr_eth::l1::HaltReason::PrecompileError => ExceptionalHalt::PrecompileError,
|
|
126
|
+
edr_eth::l1::HaltReason::NonceOverflow => ExceptionalHalt::NonceOverflow,
|
|
127
|
+
edr_eth::l1::HaltReason::CreateContractSizeLimit => {
|
|
128
128
|
ExceptionalHalt::CreateContractSizeLimit
|
|
129
129
|
}
|
|
130
|
-
|
|
130
|
+
edr_eth::l1::HaltReason::CreateContractStartingWithEF => {
|
|
131
131
|
ExceptionalHalt::CreateContractStartingWithEF
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
edr_eth::l1::HaltReason::CreateInitCodeSizeLimit => {
|
|
134
134
|
ExceptionalHalt::CreateInitCodeSizeLimit
|
|
135
135
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
ExceptionalHalt::
|
|
136
|
+
edr_eth::l1::HaltReason::EofAuxDataOverflow => ExceptionalHalt::EofAuxDataOverflow,
|
|
137
|
+
edr_eth::l1::HaltReason::EofAuxDataTooSmall => ExceptionalHalt::EofAuxDataTooSmall,
|
|
138
|
+
edr_eth::l1::HaltReason::SubRoutineStackOverflow => {
|
|
139
|
+
ExceptionalHalt::SubRoutineStackOverflow
|
|
140
140
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
|
144
|
-
|
|
|
145
|
-
|
|
|
146
|
-
|
|
|
141
|
+
edr_eth::l1::HaltReason::InvalidEXTCALLTarget => ExceptionalHalt::InvalidEXTCALLTarget,
|
|
142
|
+
edr_eth::l1::HaltReason::OverflowPayment
|
|
143
|
+
| edr_eth::l1::HaltReason::StateChangeDuringStaticCall
|
|
144
|
+
| edr_eth::l1::HaltReason::CallNotAllowedInsideStatic
|
|
145
|
+
| edr_eth::l1::HaltReason::OutOfFunds
|
|
146
|
+
| edr_eth::l1::HaltReason::CallTooDeep => {
|
|
147
147
|
unreachable!("Internal halts that can be only found inside Inspector: {halt:?}")
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
impl From<ExceptionalHalt> for
|
|
153
|
+
impl From<ExceptionalHalt> for edr_eth::l1::HaltReason {
|
|
154
154
|
fn from(value: ExceptionalHalt) -> Self {
|
|
155
155
|
match value {
|
|
156
|
-
ExceptionalHalt::OutOfGas => Self::OutOfGas(
|
|
156
|
+
ExceptionalHalt::OutOfGas => Self::OutOfGas(edr_eth::l1::OutOfGasError::Basic),
|
|
157
157
|
ExceptionalHalt::OpcodeNotFound => Self::OpcodeNotFound,
|
|
158
158
|
ExceptionalHalt::InvalidFEOpcode => Self::InvalidFEOpcode,
|
|
159
159
|
ExceptionalHalt::InvalidJump => Self::InvalidJump,
|
|
@@ -169,7 +169,7 @@ impl From<ExceptionalHalt> for edr_evm::HaltReason {
|
|
|
169
169
|
ExceptionalHalt::CreateInitCodeSizeLimit => Self::CreateInitCodeSizeLimit,
|
|
170
170
|
ExceptionalHalt::EofAuxDataOverflow => Self::EofAuxDataOverflow,
|
|
171
171
|
ExceptionalHalt::EofAuxDataTooSmall => Self::EofAuxDataTooSmall,
|
|
172
|
-
ExceptionalHalt::
|
|
172
|
+
ExceptionalHalt::SubRoutineStackOverflow => Self::SubRoutineStackOverflow,
|
|
173
173
|
ExceptionalHalt::InvalidEXTCALLTarget => Self::InvalidEXTCALLTarget,
|
|
174
174
|
}
|
|
175
175
|
}
|
|
@@ -191,28 +191,25 @@ pub struct ExecutionResult {
|
|
|
191
191
|
/// The transaction result
|
|
192
192
|
pub result: Either3<SuccessResult, RevertResult, HaltResult>,
|
|
193
193
|
/// Optional contract address if the transaction created a new contract.
|
|
194
|
-
pub contract_address: Option<
|
|
194
|
+
pub contract_address: Option<Uint8Array>,
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
impl ExecutionResult {
|
|
198
|
-
|
|
197
|
+
impl From<&AfterMessage<edr_eth::l1::HaltReason>> for ExecutionResult {
|
|
198
|
+
fn from(value: &AfterMessage<edr_eth::l1::HaltReason>) -> Self {
|
|
199
199
|
let AfterMessage {
|
|
200
200
|
execution_result,
|
|
201
201
|
contract_address,
|
|
202
|
-
} =
|
|
202
|
+
} = value;
|
|
203
203
|
|
|
204
204
|
let result = match execution_result {
|
|
205
|
-
|
|
205
|
+
edr_eth::result::ExecutionResult::Success {
|
|
206
206
|
reason,
|
|
207
207
|
gas_used,
|
|
208
208
|
gas_refunded,
|
|
209
209
|
logs,
|
|
210
210
|
output,
|
|
211
211
|
} => {
|
|
212
|
-
let logs = logs
|
|
213
|
-
.iter()
|
|
214
|
-
.map(|log| ExecutionLog::new(env, log))
|
|
215
|
-
.collect::<napi::Result<_>>()?;
|
|
212
|
+
let logs = logs.iter().map(ExecutionLog::from).collect();
|
|
216
213
|
|
|
217
214
|
Either3::A(SuccessResult {
|
|
218
215
|
reason: SuccessReason::from(*reason),
|
|
@@ -220,47 +217,41 @@ impl ExecutionResult {
|
|
|
220
217
|
gas_refunded: BigInt::from(*gas_refunded),
|
|
221
218
|
logs,
|
|
222
219
|
output: match output {
|
|
223
|
-
|
|
224
|
-
let return_value =
|
|
225
|
-
.create_buffer_with_data(return_value.to_vec())
|
|
226
|
-
.map(JsBufferValue::into_raw)?;
|
|
220
|
+
edr_eth::result::Output::Call(return_value) => {
|
|
221
|
+
let return_value = Uint8Array::with_data_copied(return_value);
|
|
227
222
|
|
|
228
223
|
Either::A(CallOutput { return_value })
|
|
229
224
|
}
|
|
230
|
-
|
|
231
|
-
let return_value =
|
|
232
|
-
.create_buffer_with_data(return_value.to_vec())
|
|
233
|
-
.map(JsBufferValue::into_raw)?;
|
|
225
|
+
edr_eth::result::Output::Create(return_value, address) => {
|
|
226
|
+
let return_value = Uint8Array::with_data_copied(return_value);
|
|
234
227
|
|
|
235
228
|
Either::B(CreateOutput {
|
|
236
229
|
return_value,
|
|
237
|
-
address: address.map(
|
|
230
|
+
address: address.as_ref().map(Uint8Array::with_data_copied),
|
|
238
231
|
})
|
|
239
232
|
}
|
|
240
233
|
},
|
|
241
234
|
})
|
|
242
235
|
}
|
|
243
|
-
|
|
244
|
-
let output =
|
|
245
|
-
.create_buffer_with_data(output.to_vec())
|
|
246
|
-
.map(JsBufferValue::into_raw)?;
|
|
236
|
+
edr_eth::result::ExecutionResult::Revert { gas_used, output } => {
|
|
237
|
+
let output = Uint8Array::with_data_copied(output);
|
|
247
238
|
|
|
248
239
|
Either3::B(RevertResult {
|
|
249
240
|
gas_used: BigInt::from(*gas_used),
|
|
250
241
|
output,
|
|
251
242
|
})
|
|
252
243
|
}
|
|
253
|
-
|
|
244
|
+
edr_eth::result::ExecutionResult::Halt { reason, gas_used } => Either3::C(HaltResult {
|
|
254
245
|
reason: ExceptionalHalt::from(*reason),
|
|
255
246
|
gas_used: BigInt::from(*gas_used),
|
|
256
247
|
}),
|
|
257
248
|
};
|
|
258
249
|
|
|
259
|
-
let contract_address = contract_address.map(
|
|
250
|
+
let contract_address = contract_address.as_ref().map(Uint8Array::with_data_copied);
|
|
260
251
|
|
|
261
|
-
|
|
252
|
+
Self {
|
|
262
253
|
result,
|
|
263
254
|
contract_address,
|
|
264
|
-
}
|
|
255
|
+
}
|
|
265
256
|
}
|
|
266
257
|
}
|
package/src/scenarios.rs
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
2
2
|
|
|
3
|
-
use
|
|
3
|
+
use edr_scenarios::ScenarioConfig;
|
|
4
4
|
use napi::tokio::{fs::File, io::AsyncWriteExt, sync::Mutex};
|
|
5
5
|
use rand::{distributions::Alphanumeric, Rng};
|
|
6
|
-
use serde::Serialize;
|
|
7
6
|
|
|
8
7
|
const SCENARIO_FILE_PREFIX: &str = "EDR_SCENARIO_PREFIX";
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/// Creates a scenario file with the provided configuration.
|
|
10
|
+
pub async fn scenario_file(
|
|
11
|
+
chain_type: String,
|
|
12
|
+
provider_config: edr_napi_core::provider::Config,
|
|
13
13
|
logger_enabled: bool,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
pub(crate) async fn scenario_file(
|
|
17
|
-
provider_config: &edr_provider::ProviderConfig,
|
|
18
|
-
logger_enabled: bool,
|
|
19
|
-
) -> Result<Option<Mutex<File>>, napi::Error> {
|
|
14
|
+
) -> napi::Result<Option<Mutex<File>>> {
|
|
20
15
|
if let Ok(scenario_prefix) = std::env::var(SCENARIO_FILE_PREFIX) {
|
|
21
16
|
let timestamp = SystemTime::now()
|
|
22
17
|
.duration_since(UNIX_EPOCH)
|
|
@@ -32,8 +27,9 @@ pub(crate) async fn scenario_file(
|
|
|
32
27
|
File::create(format!("{scenario_prefix}_{timestamp}_{suffix}.json")).await?;
|
|
33
28
|
|
|
34
29
|
let config = ScenarioConfig {
|
|
35
|
-
|
|
30
|
+
chain_type: Some(chain_type),
|
|
36
31
|
logger_enabled,
|
|
32
|
+
provider_config: provider_config.try_into()?,
|
|
37
33
|
};
|
|
38
34
|
let mut line = serde_json::to_string(&config)?;
|
|
39
35
|
line.push('\n');
|
|
@@ -45,11 +41,9 @@ pub(crate) async fn scenario_file(
|
|
|
45
41
|
}
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
request
|
|
51
|
-
) -> napi::Result<()> {
|
|
52
|
-
let mut line = serde_json::to_string(request)?;
|
|
44
|
+
/// Writes a JSON-RPC request to the scenario file.
|
|
45
|
+
pub async fn write_request(scenario_file: &Mutex<File>, request: &str) -> napi::Result<()> {
|
|
46
|
+
let mut line = request.to_string();
|
|
53
47
|
line.push('\n');
|
|
54
48
|
{
|
|
55
49
|
let mut scenario_file = scenario_file.lock().await;
|
package/src/serde.rs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
use edr_eth::hex;
|
|
2
|
+
use napi::bindgen_prelude::{BigInt, Uint8Array};
|
|
3
|
+
use serde::Serializer;
|
|
4
|
+
|
|
5
|
+
/// Serialize a `Uint8Array` as a 0x-prefixed hex string
|
|
6
|
+
pub fn serialize_uint8array_as_hex<S>(buffer: &Uint8Array, serializer: S) -> Result<S::Ok, S::Error>
|
|
7
|
+
where
|
|
8
|
+
S: Serializer,
|
|
9
|
+
{
|
|
10
|
+
let hex_string = format!("0x{}", hex::encode(buffer));
|
|
11
|
+
serializer.serialize_str(&hex_string)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// Serialize an Option<Uint8Array> as a 0x-prefixed hex string or None
|
|
15
|
+
pub fn serialize_optional_uint8array_as_hex<S>(
|
|
16
|
+
buffer: &Option<Uint8Array>,
|
|
17
|
+
serializer: S,
|
|
18
|
+
) -> Result<S::Ok, S::Error>
|
|
19
|
+
where
|
|
20
|
+
S: Serializer,
|
|
21
|
+
{
|
|
22
|
+
match buffer {
|
|
23
|
+
Some(buf) => {
|
|
24
|
+
let hex_string = format!("0x{}", hex::encode(buf));
|
|
25
|
+
serializer.serialize_str(&hex_string)
|
|
26
|
+
}
|
|
27
|
+
None => serializer.serialize_none(),
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// Serialize a `BigInt` as a struct with `sign_bit` and `words` fields
|
|
32
|
+
pub fn serialize_bigint_as_struct<S>(value: &BigInt, serializer: S) -> Result<S::Ok, S::Error>
|
|
33
|
+
where
|
|
34
|
+
S: Serializer,
|
|
35
|
+
{
|
|
36
|
+
use serde::ser::SerializeStruct;
|
|
37
|
+
|
|
38
|
+
let mut state = serializer.serialize_struct("BigInt", 2)?;
|
|
39
|
+
state.serialize_field("sign_bit", &value.sign_bit)?;
|
|
40
|
+
state.serialize_field("words", &value.words)?;
|
|
41
|
+
state.end()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Serialize an Option<BigInt> as a struct with `sign_bit` and `words` fields
|
|
45
|
+
/// or None
|
|
46
|
+
pub fn serialize_optional_bigint_as_struct<S>(
|
|
47
|
+
value: &Option<BigInt>,
|
|
48
|
+
serializer: S,
|
|
49
|
+
) -> Result<S::Ok, S::Error>
|
|
50
|
+
where
|
|
51
|
+
S: Serializer,
|
|
52
|
+
{
|
|
53
|
+
match value {
|
|
54
|
+
Some(val) => serialize_bigint_as_struct(val, serializer),
|
|
55
|
+
None => serializer.serialize_none(),
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
use std::{
|
|
2
|
+
borrow::Cow,
|
|
3
|
+
collections::{BTreeMap, HashMap},
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
use napi_derive::napi;
|
|
7
|
+
|
|
8
|
+
/// A compilation artifact.
|
|
9
|
+
#[derive(Clone, Debug)]
|
|
10
|
+
#[napi(object)]
|
|
11
|
+
pub struct Artifact {
|
|
12
|
+
/// The identifier of the artifact.
|
|
13
|
+
pub id: ArtifactId,
|
|
14
|
+
/// The test contract.
|
|
15
|
+
pub contract: ContractData,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// The identifier of a Solidity contract.
|
|
19
|
+
#[derive(Clone, Debug)]
|
|
20
|
+
#[napi(object)]
|
|
21
|
+
pub struct ArtifactId {
|
|
22
|
+
/// The name of the contract.
|
|
23
|
+
pub name: String,
|
|
24
|
+
/// Original source file path.
|
|
25
|
+
pub source: String,
|
|
26
|
+
/// The solc semver string.
|
|
27
|
+
pub solc_version: String,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
impl From<edr_solidity::artifacts::ArtifactId> for ArtifactId {
|
|
31
|
+
fn from(value: edr_solidity::artifacts::ArtifactId) -> Self {
|
|
32
|
+
Self {
|
|
33
|
+
name: value.name,
|
|
34
|
+
source: value.source.to_string_lossy().to_string(),
|
|
35
|
+
solc_version: value.version.to_string(),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
impl TryFrom<ArtifactId> for edr_solidity::artifacts::ArtifactId {
|
|
41
|
+
type Error = napi::Error;
|
|
42
|
+
|
|
43
|
+
fn try_from(value: ArtifactId) -> napi::Result<Self> {
|
|
44
|
+
Ok(edr_solidity::artifacts::ArtifactId {
|
|
45
|
+
name: value.name,
|
|
46
|
+
source: value.source.parse().map_err(|_err| {
|
|
47
|
+
napi::Error::new(napi::Status::GenericFailure, "Invalid source path")
|
|
48
|
+
})?,
|
|
49
|
+
version: value.solc_version.parse().map_err(|_err| {
|
|
50
|
+
napi::Error::new(napi::Status::GenericFailure, "Invalid solc semver string")
|
|
51
|
+
})?,
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/// A test contract to execute.
|
|
57
|
+
#[derive(Clone, Debug)]
|
|
58
|
+
#[napi(object)]
|
|
59
|
+
pub struct ContractData {
|
|
60
|
+
/// Contract ABI as json string.
|
|
61
|
+
pub abi: String,
|
|
62
|
+
/// Contract creation code as hex string. It can be missing if the contract
|
|
63
|
+
/// is ABI only.
|
|
64
|
+
pub bytecode: Option<String>,
|
|
65
|
+
/// The link references of the deployment bytecode.
|
|
66
|
+
pub link_references: Option<HashMap<String, HashMap<String, Vec<LinkReference>>>>,
|
|
67
|
+
/// Contract runtime code as hex string. It can be missing if the contract
|
|
68
|
+
/// is ABI only.
|
|
69
|
+
pub deployed_bytecode: Option<String>,
|
|
70
|
+
/// The link references of the deployed bytecode.
|
|
71
|
+
pub deployed_link_references: Option<HashMap<String, HashMap<String, Vec<LinkReference>>>>,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
impl TryFrom<ContractData> for foundry_compilers::artifacts::CompactContractBytecode {
|
|
75
|
+
type Error = napi::Error;
|
|
76
|
+
|
|
77
|
+
fn try_from(contract: ContractData) -> napi::Result<Self> {
|
|
78
|
+
Ok(foundry_compilers::artifacts::CompactContractBytecode {
|
|
79
|
+
abi: Some(serde_json::from_str(&contract.abi).map_err(|_err| {
|
|
80
|
+
napi::Error::new(napi::Status::GenericFailure, "Invalid JSON ABI")
|
|
81
|
+
})?),
|
|
82
|
+
bytecode: contract
|
|
83
|
+
.bytecode
|
|
84
|
+
.map(|bytecode| {
|
|
85
|
+
let link_references =
|
|
86
|
+
convert_link_references(contract.link_references.unwrap_or_default());
|
|
87
|
+
let object = convert_bytecode(bytecode, !link_references.is_empty())?;
|
|
88
|
+
Ok::<_, napi::Error>(foundry_compilers::artifacts::CompactBytecode {
|
|
89
|
+
object,
|
|
90
|
+
source_map: None,
|
|
91
|
+
link_references,
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
.transpose()?,
|
|
95
|
+
deployed_bytecode: contract
|
|
96
|
+
.deployed_bytecode
|
|
97
|
+
.map(|deployed_bytecode| {
|
|
98
|
+
let link_references = convert_link_references(
|
|
99
|
+
contract.deployed_link_references.unwrap_or_default(),
|
|
100
|
+
);
|
|
101
|
+
let object = convert_bytecode(deployed_bytecode, !link_references.is_empty())?;
|
|
102
|
+
let compact_bytecode = foundry_compilers::artifacts::CompactBytecode {
|
|
103
|
+
object,
|
|
104
|
+
source_map: None,
|
|
105
|
+
link_references,
|
|
106
|
+
};
|
|
107
|
+
Ok::<_, napi::Error>(foundry_compilers::artifacts::CompactDeployedBytecode {
|
|
108
|
+
bytecode: Some(compact_bytecode),
|
|
109
|
+
immutable_references: BTreeMap::default(),
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
.transpose()?,
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
impl TryFrom<ContractData> for foundry_compilers::artifacts::CompactContractBytecodeCow<'static> {
|
|
118
|
+
type Error = napi::Error;
|
|
119
|
+
|
|
120
|
+
fn try_from(contract: ContractData) -> napi::Result<Self> {
|
|
121
|
+
let c: foundry_compilers::artifacts::CompactContractBytecode = contract.try_into()?;
|
|
122
|
+
Ok(foundry_compilers::artifacts::CompactContractBytecodeCow {
|
|
123
|
+
abi: c.abi.map(Cow::Owned),
|
|
124
|
+
bytecode: c.bytecode.map(Cow::Owned),
|
|
125
|
+
deployed_bytecode: c.deployed_bytecode.map(Cow::Owned),
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// The order of link references as supplied through the NAPI interface doesn't
|
|
131
|
+
// matter, but the order can matter downstream for deterministic address
|
|
132
|
+
// generation.
|
|
133
|
+
fn convert_link_references(
|
|
134
|
+
link_references: HashMap<String, HashMap<String, Vec<LinkReference>>>,
|
|
135
|
+
) -> BTreeMap<String, BTreeMap<String, Vec<foundry_compilers::artifacts::Offsets>>> {
|
|
136
|
+
link_references
|
|
137
|
+
.into_iter()
|
|
138
|
+
.map(|(file, libraries)| {
|
|
139
|
+
let lib_map = libraries
|
|
140
|
+
.into_iter()
|
|
141
|
+
.map(|(library_name, references)| {
|
|
142
|
+
let offsets = references.into_iter().map(Into::into).collect();
|
|
143
|
+
(library_name, offsets)
|
|
144
|
+
})
|
|
145
|
+
.collect();
|
|
146
|
+
(file, lib_map)
|
|
147
|
+
})
|
|
148
|
+
.collect()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn convert_bytecode(
|
|
152
|
+
bytecode: String,
|
|
153
|
+
needs_linking: bool,
|
|
154
|
+
) -> napi::Result<foundry_compilers::artifacts::BytecodeObject> {
|
|
155
|
+
if needs_linking {
|
|
156
|
+
Ok(foundry_compilers::artifacts::BytecodeObject::Unlinked(
|
|
157
|
+
bytecode,
|
|
158
|
+
))
|
|
159
|
+
} else {
|
|
160
|
+
let bytes = bytecode.parse().map_err(|err| {
|
|
161
|
+
let message = format!("Hex decoding error while parsing bytecode: '{err}'. Maybe forgot to pass link references for a contract that needs linking?");
|
|
162
|
+
napi::Error::from_reason(message)
|
|
163
|
+
})?;
|
|
164
|
+
Ok(foundry_compilers::artifacts::BytecodeObject::Bytecode(
|
|
165
|
+
bytes,
|
|
166
|
+
))
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#[derive(Clone, Debug)]
|
|
171
|
+
#[napi(object)]
|
|
172
|
+
pub struct LinkReference {
|
|
173
|
+
pub start: u32,
|
|
174
|
+
pub length: u32,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
impl From<LinkReference> for foundry_compilers::artifacts::Offsets {
|
|
178
|
+
fn from(value: LinkReference) -> Self {
|
|
179
|
+
Self {
|
|
180
|
+
start: value.start,
|
|
181
|
+
length: value.length,
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|