@nomicfoundation/edr 0.12.0-alpha.0 → 0.12.0-next.1
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 +47 -16
- package/LICENSE +5 -1
- package/index.d.ts +794 -104
- package/index.js +31 -1
- package/package.json +20 -17
- package/src/account.rs +102 -55
- package/src/block.rs +2 -103
- package/src/call_override.rs +8 -8
- package/src/cast.rs +7 -29
- package/src/chains/generic.rs +1 -1
- package/src/chains/l1.rs +20 -18
- package/src/chains/op.rs +95 -38
- package/src/config.rs +305 -161
- package/src/context.rs +260 -21
- package/src/debug_trace.rs +2 -2
- package/src/instrument.rs +109 -0
- package/src/lib.rs +10 -0
- package/src/log.rs +12 -14
- package/src/logger.rs +28 -30
- package/src/mock.rs +68 -0
- package/src/precompile.rs +50 -0
- package/src/provider/response.rs +8 -5
- package/src/provider.rs +14 -8
- package/src/result.rs +18 -27
- package/src/scenarios.rs +2 -2
- 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 +707 -0
- package/src/solidity_tests.rs +56 -0
- package/src/subscription.rs +1 -1
- package/src/trace/debug.rs +1 -1
- package/src/trace/exit.rs +4 -4
- package/src/trace/library_utils.rs +7 -2
- package/src/trace/return_data.rs +10 -10
- package/src/trace/solidity_stack_trace.rs +7 -5
- package/src/trace.rs +29 -38
- package/src/withdrawal.rs +3 -3
package/src/mock.rs
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
use std::sync::Arc;
|
|
2
|
+
|
|
3
|
+
use edr_napi_core::provider::SyncProvider;
|
|
4
|
+
use edr_rpc_client::jsonrpc;
|
|
5
|
+
use edr_solidity::contract_decoder::ContractDecoder;
|
|
6
|
+
use napi::tokio::runtime;
|
|
7
|
+
use napi_derive::napi;
|
|
8
|
+
|
|
9
|
+
use crate::{context::EdrContext, provider::Provider};
|
|
10
|
+
|
|
11
|
+
/// A mock provider that always returns the given mocked response.
|
|
12
|
+
pub struct MockProvider {
|
|
13
|
+
mocked_response: serde_json::Value,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl MockProvider {
|
|
17
|
+
pub fn new(mocked_response: serde_json::Value) -> Self {
|
|
18
|
+
Self { mocked_response }
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
impl SyncProvider for MockProvider {
|
|
23
|
+
fn handle_request(
|
|
24
|
+
&self,
|
|
25
|
+
_request: String,
|
|
26
|
+
_contract_decoder: Arc<ContractDecoder>,
|
|
27
|
+
) -> napi::Result<edr_napi_core::spec::Response<edr_eth::l1::HaltReason>> {
|
|
28
|
+
let response = jsonrpc::ResponseData::Success {
|
|
29
|
+
result: self.mocked_response.clone(),
|
|
30
|
+
};
|
|
31
|
+
edr_napi_core::spec::marshal_response_data(response)
|
|
32
|
+
.map(|data| edr_napi_core::spec::Response {
|
|
33
|
+
solidity_trace: None,
|
|
34
|
+
data,
|
|
35
|
+
traces: Vec::new(),
|
|
36
|
+
})
|
|
37
|
+
.map_err(|error| napi::Error::new(napi::Status::GenericFailure, error.to_string()))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fn set_call_override_callback(
|
|
41
|
+
&self,
|
|
42
|
+
_call_override_callback: Arc<dyn edr_provider::SyncCallOverride>,
|
|
43
|
+
) {
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
fn set_verbose_tracing(&self, _enabled: bool) {}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
#[napi]
|
|
50
|
+
impl EdrContext {
|
|
51
|
+
#[doc = "Creates a mock provider, which always returns the given response."]
|
|
52
|
+
#[doc = "For testing purposes."]
|
|
53
|
+
#[napi]
|
|
54
|
+
pub fn create_mock_provider(
|
|
55
|
+
&self,
|
|
56
|
+
mocked_response: serde_json::Value,
|
|
57
|
+
) -> napi::Result<Provider> {
|
|
58
|
+
let provider = Provider::new(
|
|
59
|
+
Arc::new(MockProvider::new(mocked_response)),
|
|
60
|
+
runtime::Handle::current(),
|
|
61
|
+
Arc::new(ContractDecoder::default()),
|
|
62
|
+
#[cfg(feature = "scenarios")]
|
|
63
|
+
None,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
Ok(provider)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
use edr_eth::Address;
|
|
2
|
+
use edr_evm::precompile::{self, PrecompileFn, PrecompileWithAddress};
|
|
3
|
+
use napi::bindgen_prelude::Uint8Array;
|
|
4
|
+
use napi_derive::napi;
|
|
5
|
+
|
|
6
|
+
#[napi]
|
|
7
|
+
#[derive(Clone)]
|
|
8
|
+
pub struct Precompile {
|
|
9
|
+
address: Address,
|
|
10
|
+
precompile_fn: PrecompileFn,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
impl Precompile {
|
|
14
|
+
pub fn new(address: Address, precompile_fn: PrecompileFn) -> Self {
|
|
15
|
+
Self {
|
|
16
|
+
address,
|
|
17
|
+
precompile_fn,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// Returns the address and precompile function as a tuple.
|
|
22
|
+
pub fn to_tuple(&self) -> (Address, PrecompileFn) {
|
|
23
|
+
(self.address, self.precompile_fn)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl From<PrecompileWithAddress> for Precompile {
|
|
28
|
+
fn from(value: PrecompileWithAddress) -> Self {
|
|
29
|
+
Self {
|
|
30
|
+
address: value.0,
|
|
31
|
+
precompile_fn: value.1,
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[napi]
|
|
37
|
+
impl Precompile {
|
|
38
|
+
/// Returns the address of the precompile.
|
|
39
|
+
#[napi(catch_unwind, getter)]
|
|
40
|
+
pub fn address(&self) -> Uint8Array {
|
|
41
|
+
Uint8Array::with_data_copied(self.address)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/// [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md#specification)
|
|
46
|
+
/// secp256r1 precompile.
|
|
47
|
+
#[napi(catch_unwind)]
|
|
48
|
+
pub fn precompile_p256_verify() -> Precompile {
|
|
49
|
+
Precompile::from(precompile::secp256r1::P256VERIFY)
|
|
50
|
+
}
|
package/src/provider/response.rs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
use edr_eth::l1;
|
|
2
2
|
use edr_napi_core::spec::SolidityTraceData;
|
|
3
|
+
use edr_solidity::contract_decoder::NestedTraceDecoder as _;
|
|
3
4
|
use napi::Either;
|
|
4
5
|
use napi_derive::napi;
|
|
5
6
|
|
|
6
7
|
use crate::{
|
|
7
8
|
cast::TryCast,
|
|
8
|
-
trace::{
|
|
9
|
+
trace::{solidity_stack_trace::SolidityStackTrace, RawTrace},
|
|
9
10
|
};
|
|
10
11
|
|
|
11
12
|
#[napi]
|
|
@@ -22,14 +23,14 @@ impl From<edr_napi_core::spec::Response<l1::HaltReason>> for Response {
|
|
|
22
23
|
#[napi]
|
|
23
24
|
impl Response {
|
|
24
25
|
#[doc = "Returns the response data as a JSON string or a JSON object."]
|
|
25
|
-
#[napi(getter)]
|
|
26
|
+
#[napi(catch_unwind, getter)]
|
|
26
27
|
pub fn data(&self) -> Either<String, serde_json::Value> {
|
|
27
28
|
self.inner.data.clone()
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
// Rust port of https://github.com/NomicFoundation/hardhat/blob/c20bf195a6efdc2d74e778b7a4a7799aac224841/packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts#L590
|
|
31
32
|
#[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."]
|
|
32
|
-
#[napi]
|
|
33
|
+
#[napi(catch_unwind)]
|
|
33
34
|
pub fn stack_trace(&self) -> napi::Result<Option<SolidityStackTrace>> {
|
|
34
35
|
let Some(SolidityTraceData {
|
|
35
36
|
trace,
|
|
@@ -44,7 +45,9 @@ impl Response {
|
|
|
44
45
|
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
45
46
|
|
|
46
47
|
if let Some(vm_trace) = nested_trace {
|
|
47
|
-
let decoded_trace = contract_decoder
|
|
48
|
+
let decoded_trace = contract_decoder
|
|
49
|
+
.try_to_decode_nested_trace(vm_trace)
|
|
50
|
+
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
48
51
|
let stack_trace = edr_solidity::solidity_tracer::get_stack_trace(decoded_trace)
|
|
49
52
|
.map_err(|err| napi::Error::from_reason(err.to_string()))?;
|
|
50
53
|
let stack_trace = stack_trace
|
|
@@ -59,7 +62,7 @@ impl Response {
|
|
|
59
62
|
}
|
|
60
63
|
|
|
61
64
|
#[doc = "Returns the raw traces of executed contracts. This maybe contain zero or more traces."]
|
|
62
|
-
#[napi(getter)]
|
|
65
|
+
#[napi(catch_unwind, getter)]
|
|
63
66
|
pub fn traces(&self) -> Vec<RawTrace> {
|
|
64
67
|
self.inner
|
|
65
68
|
.traces
|
package/src/provider.rs
CHANGED
|
@@ -6,7 +6,7 @@ use std::sync::Arc;
|
|
|
6
6
|
|
|
7
7
|
use edr_napi_core::provider::SyncProvider;
|
|
8
8
|
use edr_solidity::contract_decoder::ContractDecoder;
|
|
9
|
-
use napi::{Env, JsFunction, JsObject, Status
|
|
9
|
+
use napi::{tokio::runtime, Env, JsFunction, JsObject, Status};
|
|
10
10
|
use napi_derive::napi;
|
|
11
11
|
|
|
12
12
|
pub use self::factory::ProviderFactory;
|
|
@@ -46,7 +46,7 @@ impl Provider {
|
|
|
46
46
|
#[napi]
|
|
47
47
|
impl Provider {
|
|
48
48
|
#[doc = "Handles a JSON-RPC request and returns a JSON-RPC response."]
|
|
49
|
-
#[napi]
|
|
49
|
+
#[napi(catch_unwind)]
|
|
50
50
|
pub async fn handle_request(&self, request: String) -> napi::Result<Response> {
|
|
51
51
|
let provider = self.provider.clone();
|
|
52
52
|
|
|
@@ -64,24 +64,30 @@ impl Provider {
|
|
|
64
64
|
.map(Response::from)
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
#[napi(ts_return_type = "Promise<void>")]
|
|
67
|
+
#[napi(catch_unwind, ts_return_type = "Promise<void>")]
|
|
68
68
|
pub fn set_call_override_callback(
|
|
69
69
|
&self,
|
|
70
70
|
env: Env,
|
|
71
71
|
#[napi(
|
|
72
|
-
ts_arg_type = "(contract_address:
|
|
72
|
+
ts_arg_type = "(contract_address: ArrayBuffer, data: ArrayBuffer) => Promise<CallOverrideResult | undefined>"
|
|
73
73
|
)]
|
|
74
74
|
call_override_callback: JsFunction,
|
|
75
75
|
) -> napi::Result<JsObject> {
|
|
76
|
+
let (deferred, promise) = env.create_deferred()?;
|
|
77
|
+
|
|
76
78
|
let call_override_callback =
|
|
77
|
-
CallOverrideCallback::new(&env, call_override_callback, self.runtime.clone())
|
|
79
|
+
match CallOverrideCallback::new(&env, call_override_callback, self.runtime.clone()) {
|
|
80
|
+
Ok(callback) => callback,
|
|
81
|
+
Err(error) => {
|
|
82
|
+
deferred.reject(error);
|
|
83
|
+
return Ok(promise);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
78
86
|
|
|
79
87
|
let call_override_callback =
|
|
80
88
|
Arc::new(move |address, data| call_override_callback.call_override(address, data));
|
|
81
89
|
|
|
82
90
|
let provider = self.provider.clone();
|
|
83
|
-
|
|
84
|
-
let (deferred, promise) = env.create_deferred()?;
|
|
85
91
|
self.runtime.spawn_blocking(move || {
|
|
86
92
|
provider.set_call_override_callback(call_override_callback);
|
|
87
93
|
|
|
@@ -95,7 +101,7 @@ impl Provider {
|
|
|
95
101
|
/// `eth_estimateGas`, `eth_sendRawTransaction`, `eth_sendTransaction`,
|
|
96
102
|
/// `evm_mine`, `hardhat_mine` include the full stack and memory. Set to
|
|
97
103
|
/// `false` to disable this.
|
|
98
|
-
#[napi]
|
|
104
|
+
#[napi(catch_unwind)]
|
|
99
105
|
pub async fn set_verbose_tracing(&self, verbose_tracing: bool) -> napi::Result<()> {
|
|
100
106
|
let provider = self.provider.clone();
|
|
101
107
|
|
package/src/result.rs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use edr_evm::trace::AfterMessage;
|
|
2
2
|
use napi::{
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
bindgen_prelude::{BigInt, Either3, Uint8Array},
|
|
4
|
+
Either,
|
|
5
5
|
};
|
|
6
6
|
use napi_derive::napi;
|
|
7
7
|
|
|
@@ -44,15 +44,15 @@ impl From<SuccessReason> for edr_eth::result::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
|
|
@@ -191,15 +191,15 @@ 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 {
|
|
@@ -209,10 +209,7 @@ impl ExecutionResult {
|
|
|
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),
|
|
@@ -221,29 +218,23 @@ impl ExecutionResult {
|
|
|
221
218
|
logs,
|
|
222
219
|
output: match output {
|
|
223
220
|
edr_eth::result::Output::Call(return_value) => {
|
|
224
|
-
let return_value =
|
|
225
|
-
.create_buffer_with_data(return_value.to_vec())
|
|
226
|
-
.map(JsBufferValue::into_raw)?;
|
|
221
|
+
let return_value = Uint8Array::with_data_copied(return_value);
|
|
227
222
|
|
|
228
223
|
Either::A(CallOutput { return_value })
|
|
229
224
|
}
|
|
230
225
|
edr_eth::result::Output::Create(return_value, address) => {
|
|
231
|
-
let return_value =
|
|
232
|
-
.create_buffer_with_data(return_value.to_vec())
|
|
233
|
-
.map(JsBufferValue::into_raw)?;
|
|
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
236
|
edr_eth::result::ExecutionResult::Revert { gas_used, output } => {
|
|
244
|
-
let output =
|
|
245
|
-
.create_buffer_with_data(output.to_vec())
|
|
246
|
-
.map(JsBufferValue::into_raw)?;
|
|
237
|
+
let output = Uint8Array::with_data_copied(output);
|
|
247
238
|
|
|
248
239
|
Either3::B(RevertResult {
|
|
249
240
|
gas_used: BigInt::from(*gas_used),
|
|
@@ -256,11 +247,11 @@ impl ExecutionResult {
|
|
|
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
|
@@ -2,7 +2,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
2
2
|
|
|
3
3
|
use edr_scenarios::ScenarioConfig;
|
|
4
4
|
use napi::tokio::{fs::File, io::AsyncWriteExt, sync::Mutex};
|
|
5
|
-
use rand::{
|
|
5
|
+
use rand::{distributions::Alphanumeric, Rng};
|
|
6
6
|
|
|
7
7
|
const SCENARIO_FILE_PREFIX: &str = "EDR_SCENARIO_PREFIX";
|
|
8
8
|
|
|
@@ -29,7 +29,7 @@ pub async fn scenario_file(
|
|
|
29
29
|
let config = ScenarioConfig {
|
|
30
30
|
chain_type: Some(chain_type),
|
|
31
31
|
logger_enabled,
|
|
32
|
-
provider_config: provider_config.
|
|
32
|
+
provider_config: provider_config.try_into()?,
|
|
33
33
|
};
|
|
34
34
|
let mut line = serde_json::to_string(&config)?;
|
|
35
35
|
line.push('\n');
|
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
|
+
}
|