Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,519 changes: 3,256 additions & 3,263 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion crates/anvil-polkadot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ path = "bin/main.rs"
[dependencies]
# foundry internal
substrate-runtime = { path = "substrate-runtime" }
polkadot-sdk = { version = "2507.1.0", default-features = false, features = [
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk.git", branch = "acihodaru/foundry-polkadot", default-features = false, features = [
"sc-allocator",
"sc-basic-authorship",
"sc-block-builder",
Expand Down Expand Up @@ -118,6 +118,7 @@ tempfile.workspace = true
itertools.workspace = true
rand.workspace = true
eyre.workspace = true
indexmap = "2.0"

# cli
clap = { version = "4", features = [
Expand All @@ -132,6 +133,7 @@ parity-scale-codec = "3.7.5"
subxt = "0.41.0"
subxt-signer = "0.41.0"
tokio-stream = "0.1.17"
sqlx = "0.8.6"

[dev-dependencies]
alloy-provider = { workspace = true, features = ["txpool-api"] }
Expand Down
11 changes: 8 additions & 3 deletions crates/anvil-polkadot/src/api_server/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::substrate_node::mining_engine::MiningError;
use anvil_rpc::{error::RpcError, response::ResponseResult};
use polkadot_sdk::pallet_revive_eth_rpc::{client::ClientError, EthRpcError};
use serde::Serialize;

#[derive(Debug, thiserror::Error)]
Expand All @@ -10,16 +11,21 @@ pub enum Error {
RpcUnimplemented,
#[error("Invalid params: {0}")]
InvalidParams(String),
#[error("Revive call failed: {0:?}")]
Revive(ClientError),
#[error("ETH RPC ERROR {0:?}")]
EthRpc(EthRpcError),
}

pub type Result<T> = std::result::Result<T, Error>;

/// Helper trait to easily convert results to rpc results
pub(crate) trait ToRpcResponseResult {
fn to_rpc_result(self) -> ResponseResult;
}

/// Converts a serializable value into a `ResponseResult`
pub fn to_rpc_result<T: Serialize>(val: T) -> ResponseResult {
/// Converts a serializable value into a `ResponseResult`.
fn to_rpc_result<T: Serialize>(val: T) -> ResponseResult {
match serde_json::to_value(val) {
Ok(success) => ResponseResult::Success(success),
Err(err) => {
Expand All @@ -33,7 +39,6 @@ impl<T: Serialize> ToRpcResponseResult for Result<T> {
fn to_rpc_result(self) -> ResponseResult {
match self {
Ok(val) => to_rpc_result(val),
Err(Error::InvalidParams(msg)) => RpcError::invalid_params(msg).into(),
Err(err) => RpcError::internal_error_with(err.to_string()).into(),
}
}
Expand Down
12 changes: 9 additions & 3 deletions crates/anvil-polkadot/src/api_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use futures::channel::{mpsc, oneshot};
use server::ApiServer;

mod error;
pub mod revive_conversions;
mod server;

pub type ApiHandle = mpsc::Sender<ApiRequest>;
Expand All @@ -17,10 +18,15 @@ pub struct ApiRequest {
pub fn spawn(substrate_service: &Service, logging_manager: LoggingManager) -> ApiHandle {
let (api_handle, receiver) = mpsc::channel(100);

let api_server = ApiServer::new(substrate_service, receiver, logging_manager);

let spawn_handle = substrate_service.task_manager.spawn_essential_handle();
spawn_handle.spawn("anvil-api-server", "anvil", api_server.run());
let rpc_handlers = substrate_service.rpc_handlers.clone();
let mining_engine = substrate_service.mining_engine.clone();
let tx_pool = substrate_service.tx_pool.clone();
spawn_handle.spawn("anvil-api-server", "anvil", async move {
let api_server =
ApiServer::new(mining_engine, rpc_handlers, receiver, logging_manager, tx_pool).await;
api_server.run().await;
});

api_handle
}
192 changes: 192 additions & 0 deletions crates/anvil-polkadot/src/api_server/revive_conversions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
use alloy_eips::{BlockId, BlockNumberOrTag};
use alloy_primitives::Address;
use alloy_rpc_types::{AccessList, TransactionRequest};
use polkadot_sdk::{
pallet_revive::evm::{
AccessListEntry, BlockNumberOrTagOrHash, BlockTag, Byte, Bytes, GenericTransaction,
InputOrData,
},
sp_core,
};
use subxt::utils::{H160, H256};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AlloyU256(alloy_primitives::U256);

impl From<polkadot_sdk::sp_core::U256> for AlloyU256 {
fn from(value: polkadot_sdk::sp_core::U256) -> Self {
let mut bytes = [0u8; 32];
value.write_as_big_endian(&mut bytes);
Self(alloy_primitives::U256::from_be_bytes(bytes))
}
}

impl AlloyU256 {
pub fn inner(&self) -> alloy_primitives::U256 {
self.0
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SubstrateU256(sp_core::U256);

impl From<alloy_primitives::U256> for SubstrateU256 {
fn from(value: alloy_primitives::U256) -> Self {
Self(sp_core::U256::from_big_endian(&value.to_be_bytes::<32>()))
}
}

impl SubstrateU256 {
pub fn inner(&self) -> sp_core::U256 {
self.0
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReviveAddress(H160);

impl ReviveAddress {
pub fn new(addr: H160) -> Self {
Self(addr)
}

pub fn inner(&self) -> H160 {
self.0
}
}

impl From<Address> for ReviveAddress {
fn from(addr: Address) -> Self {
Self(H160::from_slice(addr.0.as_ref()))
}
}

impl From<ReviveAddress> for Address {
fn from(value: ReviveAddress) -> Self {
Self(alloy_primitives::U160::from_be_bytes(*value.0.as_fixed_bytes()).into())
}
}

#[derive(Debug, Clone)]
pub struct ReviveBlockId(BlockNumberOrTagOrHash);

impl ReviveBlockId {
pub fn inner(self) -> BlockNumberOrTagOrHash {
self.0
}
}

impl From<Option<BlockId>> for ReviveBlockId {
fn from(block_id: Option<BlockId>) -> Self {
Self(block_id.map_or(
BlockNumberOrTagOrHash::BlockTag(BlockTag::Latest),
|b_id| match b_id {
BlockId::Hash(rpc_hash) => BlockNumberOrTagOrHash::BlockHash(H256::from_slice(
rpc_hash.block_hash.as_slice(),
)),
BlockId::Number(number_or_tag) => match number_or_tag {
BlockNumberOrTag::Number(num) => BlockNumberOrTagOrHash::BlockNumber(
polkadot_sdk::pallet_revive::U256::from(num),
),
BlockNumberOrTag::Latest => BlockNumberOrTagOrHash::BlockTag(BlockTag::Latest),
BlockNumberOrTag::Earliest => {
BlockNumberOrTagOrHash::BlockTag(BlockTag::Earliest)
}
BlockNumberOrTag::Pending => {
BlockNumberOrTagOrHash::BlockTag(BlockTag::Pending)
}
BlockNumberOrTag::Safe => BlockNumberOrTagOrHash::BlockTag(BlockTag::Safe),
BlockNumberOrTag::Finalized => {
BlockNumberOrTagOrHash::BlockTag(BlockTag::Finalized)
}
},
},
))
}
}

#[derive(Debug, Clone)]
pub struct ReviveAccessList(Vec<AccessListEntry>);

impl ReviveAccessList {
pub fn inner(self) -> Vec<AccessListEntry> {
self.0
}
}

impl From<AccessList> for ReviveAccessList {
fn from(value: AccessList) -> Self {
Self(
value
.0
.into_iter()
.map(|access_list_entry| AccessListEntry {
address: ReviveAddress::from(access_list_entry.address).inner(),
storage_keys: access_list_entry
.storage_keys
.into_iter()
.map(|key| H256::from_slice(key.as_ref()))
.collect(),
})
.collect(),
)
}
}

#[derive(Debug, Clone)]
pub struct ReviveBytes(Bytes);

impl From<alloy_primitives::Bytes> for ReviveBytes {
fn from(value: alloy_primitives::Bytes) -> Self {
Self(Bytes::from(value.to_vec()))
}
}

impl ReviveBytes {
pub fn inner(self) -> Bytes {
self.0
}
}

pub(crate) fn convert_to_generic_transaction(
transaction_request: TransactionRequest,
) -> GenericTransaction {
GenericTransaction {
access_list: transaction_request
.access_list
.map(|access_list| ReviveAccessList::from(access_list).inner()),
blob_versioned_hashes: transaction_request
.blob_versioned_hashes
.unwrap_or_default()
.into_iter()
.map(|b256| H256::from_slice(b256.as_ref()))
.collect(),
//replace
blobs: transaction_request
.sidecar
.unwrap_or_default()
.blobs
.into_iter()
.map(|blob| Bytes::from(blob.0.to_vec()))
.collect(),
chain_id: transaction_request.chain_id.map(sp_core::U256::from),
from: transaction_request.from.map(|addr| ReviveAddress::from(addr).inner()),
gas: transaction_request.gas.map(sp_core::U256::from),
gas_price: transaction_request.gas_price.map(sp_core::U256::from),
input: InputOrData::from(
ReviveBytes::from(transaction_request.input.into_input().unwrap_or_default()).inner(),
),
max_fee_per_blob_gas: transaction_request.max_fee_per_blob_gas.map(sp_core::U256::from),
max_fee_per_gas: transaction_request.max_fee_per_gas.map(sp_core::U256::from),
max_priority_fee_per_gas: transaction_request
.max_priority_fee_per_gas
.map(sp_core::U256::from),
nonce: transaction_request.nonce.map(sp_core::U256::from),
to: transaction_request
.to
.and_then(|tx_kind| tx_kind.into_to())
.map(|addr| ReviveAddress::from(addr).inner()),
r#type: transaction_request.transaction_type.map(Byte::from),
value: transaction_request.value.map(|value| SubstrateU256::from(value).inner()),
}
}
Loading