@nomicfoundation/edr 0.11.3 → 0.12.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/Cargo.toml +61 -27
  2. package/LICENSE +5 -1
  3. package/index.d.ts +875 -137
  4. package/index.js +61 -3
  5. package/package.json +20 -16
  6. package/src/account.rs +109 -32
  7. package/src/block.rs +2 -103
  8. package/src/call_override.rs +7 -7
  9. package/src/cast.rs +47 -17
  10. package/src/chains/generic.rs +51 -0
  11. package/src/chains/l1.rs +262 -0
  12. package/src/chains/op.rs +425 -0
  13. package/src/chains.rs +7 -0
  14. package/src/config.rs +537 -67
  15. package/src/context.rs +374 -17
  16. package/src/debug_trace.rs +2 -2
  17. package/src/instrument.rs +109 -0
  18. package/src/lib.rs +38 -14
  19. package/src/log.rs +12 -14
  20. package/src/logger.rs +77 -1177
  21. package/src/mock.rs +68 -0
  22. package/src/precompile.rs +50 -0
  23. package/src/provider/factory.rs +22 -0
  24. package/src/provider/response.rs +73 -0
  25. package/src/provider.rs +64 -325
  26. package/src/result.rs +60 -69
  27. package/src/scenarios.rs +11 -17
  28. package/src/serde.rs +57 -0
  29. package/src/solidity_tests/artifact.rs +184 -0
  30. package/src/solidity_tests/config.rs +725 -0
  31. package/src/solidity_tests/factory.rs +22 -0
  32. package/src/solidity_tests/l1.rs +68 -0
  33. package/src/solidity_tests/op.rs +69 -0
  34. package/src/solidity_tests/runner.rs +51 -0
  35. package/src/solidity_tests/test_results.rs +668 -0
  36. package/src/solidity_tests.rs +56 -0
  37. package/src/subscription.rs +32 -0
  38. package/src/trace/debug.rs +1 -1
  39. package/src/trace/exit.rs +12 -13
  40. package/src/trace/library_utils.rs +1 -1
  41. package/src/trace/return_data.rs +11 -11
  42. package/src/trace/solidity_stack_trace.rs +11 -8
  43. package/src/trace.rs +37 -44
  44. package/src/withdrawal.rs +4 -4
  45. package/src/provider/config.rs +0 -291
  46. package/src/subscribe.rs +0 -63
package/src/result.rs CHANGED
@@ -1,7 +1,7 @@
1
1
  use edr_evm::trace::AfterMessage;
2
2
  use napi::{
3
- bindgen_prelude::{BigInt, Buffer, Either3},
4
- Either, Env, JsBuffer, JsBufferValue,
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<edr_evm::SuccessReason> for SuccessReason {
23
- fn from(eval: edr_evm::SuccessReason) -> Self {
22
+ impl From<edr_eth::result::SuccessReason> for SuccessReason {
23
+ fn from(eval: edr_eth::result::SuccessReason) -> Self {
24
24
  match eval {
25
- edr_evm::SuccessReason::Stop => Self::Stop,
26
- edr_evm::SuccessReason::Return => Self::Return,
27
- edr_evm::SuccessReason::SelfDestruct => Self::SelfDestruct,
28
- edr_evm::SuccessReason::EofReturnContract => Self::EofReturnContract,
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 edr_evm::SuccessReason {
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: JsBuffer,
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: JsBuffer,
53
+ pub return_value: Uint8Array,
54
54
  /// Optionally, a 160-bit address
55
- pub address: Option<Buffer>,
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: JsBuffer,
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
- EOFFunctionStackOverflow,
108
+ SubRoutineStackOverflow,
109
109
  /// Check for target address validity is only done inside subcall.
110
110
  InvalidEXTCALLTarget,
111
111
  }
112
112
 
113
- impl From<edr_evm::HaltReason> for ExceptionalHalt {
114
- fn from(halt: edr_evm::HaltReason) -> Self {
113
+ impl From<edr_eth::l1::HaltReason> for ExceptionalHalt {
114
+ fn from(halt: edr_eth::l1::HaltReason) -> Self {
115
115
  match halt {
116
- edr_evm::HaltReason::OutOfGas(..) => ExceptionalHalt::OutOfGas,
117
- edr_evm::HaltReason::OpcodeNotFound => ExceptionalHalt::OpcodeNotFound,
118
- edr_evm::HaltReason::InvalidFEOpcode => ExceptionalHalt::InvalidFEOpcode,
119
- edr_evm::HaltReason::InvalidJump => ExceptionalHalt::InvalidJump,
120
- edr_evm::HaltReason::NotActivated => ExceptionalHalt::NotActivated,
121
- edr_evm::HaltReason::StackUnderflow => ExceptionalHalt::StackUnderflow,
122
- edr_evm::HaltReason::StackOverflow => ExceptionalHalt::StackOverflow,
123
- edr_evm::HaltReason::OutOfOffset => ExceptionalHalt::OutOfOffset,
124
- edr_evm::HaltReason::CreateCollision => ExceptionalHalt::CreateCollision,
125
- edr_evm::HaltReason::PrecompileError => ExceptionalHalt::PrecompileError,
126
- edr_evm::HaltReason::NonceOverflow => ExceptionalHalt::NonceOverflow,
127
- edr_evm::HaltReason::CreateContractSizeLimit => {
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
- edr_evm::HaltReason::CreateContractStartingWithEF => {
130
+ edr_eth::l1::HaltReason::CreateContractStartingWithEF => {
131
131
  ExceptionalHalt::CreateContractStartingWithEF
132
132
  }
133
- edr_evm::HaltReason::CreateInitCodeSizeLimit => {
133
+ edr_eth::l1::HaltReason::CreateInitCodeSizeLimit => {
134
134
  ExceptionalHalt::CreateInitCodeSizeLimit
135
135
  }
136
- edr_evm::HaltReason::EofAuxDataOverflow => ExceptionalHalt::EofAuxDataOverflow,
137
- edr_evm::HaltReason::EofAuxDataTooSmall => ExceptionalHalt::EofAuxDataTooSmall,
138
- edr_evm::HaltReason::EOFFunctionStackOverflow => {
139
- ExceptionalHalt::EOFFunctionStackOverflow
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
- edr_evm::HaltReason::InvalidEXTCALLTarget => ExceptionalHalt::InvalidEXTCALLTarget,
142
- edr_evm::HaltReason::OverflowPayment
143
- | edr_evm::HaltReason::StateChangeDuringStaticCall
144
- | edr_evm::HaltReason::CallNotAllowedInsideStatic
145
- | edr_evm::HaltReason::OutOfFunds
146
- | edr_evm::HaltReason::CallTooDeep => {
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 edr_evm::HaltReason {
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(edr_evm::OutOfGasError::Basic),
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::EOFFunctionStackOverflow => Self::EOFFunctionStackOverflow,
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<Buffer>,
194
+ pub contract_address: Option<Uint8Array>,
195
195
  }
196
196
 
197
- impl ExecutionResult {
198
- pub fn new(env: &Env, message: &AfterMessage) -> napi::Result<Self> {
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
- } = message;
202
+ } = value;
203
203
 
204
204
  let result = match execution_result {
205
- edr_evm::ExecutionResult::Success {
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
- edr_evm::Output::Call(return_value) => {
224
- let return_value = env
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
- edr_evm::Output::Create(return_value, address) => {
231
- let return_value = env
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(|address| Buffer::from(address.as_slice())),
230
+ address: address.as_ref().map(Uint8Array::with_data_copied),
238
231
  })
239
232
  }
240
233
  },
241
234
  })
242
235
  }
243
- edr_evm::ExecutionResult::Revert { gas_used, output } => {
244
- let output = env
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
- edr_evm::ExecutionResult::Halt { reason, gas_used } => Either3::C(HaltResult {
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(|address| Buffer::from(address.as_slice()));
250
+ let contract_address = contract_address.as_ref().map(Uint8Array::with_data_copied);
260
251
 
261
- Ok(Self {
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 edr_provider::ProviderRequest;
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
- #[derive(Clone, Debug, Serialize)]
11
- struct ScenarioConfig {
12
- provider_config: edr_scenarios::ScenarioProviderConfig,
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
- provider_config: provider_config.clone().into(),
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
- pub(crate) async fn write_request(
49
- scenario_file: &Mutex<File>,
50
- request: &ProviderRequest,
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
+ }