Repository: keep-starknet-strange/beerus
Pull request #307 (2023-03-16 22:27:19)

dev: error mapping
Map JSON-RPC errors to platform specific codes/messages as follows
```    
#[error("Failed to write transaction")]
FailedToReceiveTxn = 1,
#[error("Contract not found")]
ContractNotFound = 20,
#[error("Invalid message selector")]
InvalidMessageSelector = 21,
#[error("Invalid call data")]
InvalidCallData = 22,
#[error("Block not found")]
BlockNotFound = 24,
#[error("Transaction hash not found")]
TxnHashNotFound = 25,
#[error("Invalid transaction index in a block")]
InvalidTxnIndex = 27,
#[error("Class hash not found")]
ClassHashNotFound = 28,
#[error("Requested page size is too big")]
PageSizeTooBig = 31,
#[error("There are no blocks")]
NoBlocks = 32,
#[error("The supplied continuation token is invalid or unknown")]
InvalidContinuationToken = 33,
#[error("Contract error")]
ContractError = 40,
#[error("Invalid contract class")]
InvalidContractClass = 50,
#[error("Too many storage keys requested")]
ProofLimitExceeded = 10000,
#[error("Too many keys provided in a filter")]
TooManyKeysInFilter = 34,
#[error("Internal server error")]
InternalServerError = 500,
```

Also makes the `beerus_rpc_address` configurable via environment variable or config file

# Pull Request type

Please check the type of change your PR introduces:

- [ ] Bugfix
- [x] Feature
- [ ] Code style update (formatting, renaming)
- [ ] Refactoring (no functional changes, no API changes)
- [ ] Build-related changes
- [ ] Documentation content changes
- [ ] Testing
- [ ] Other (please describe):

# What is the current behavior?

all errors default to the `UNKNOWN_ERROR` provided by the jsonprcee package

Issue Number: #276 

# What is the new behavior?

respond with platform specific errors

# Does this introduce a breaking change?

- [x] Yes
- [ ] No

If the caller was handling the error codes, they will be mapped differently 


drspacemn: Hurl Test w/ bad block number:
```
POST http://0.0.0.0:3030
Content-Type: application/json
{
    "jsonrpc":"2.0","method":"starknet_getBlockWithTxHashes","params":[
        "number", "1000000000000"
    ],"id":1
}
```

output:
```
{"jsonrpc":"2.0","error":{"code":24,"message":"Block not found"},"id":1}
```

Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 34cd35e..6c31664 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -488,7 +488,6 @@ dependencies = [
  "serde_json",
  "serial_test",
  "starknet",
- "temp-env",
  "tokio",
  "toml 0.7.3",
  "url",
@@ -5082,15 +5081,6 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
 
-[[package]]
-name = "temp-env"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c0c07fe42fe15dcf97c394cb8350483a6e5095affb3a99e0dfd5f664be15fc3"
-dependencies = [
- "once_cell",
-]
-
 [[package]]
 name = "tempfile"
 version = "3.4.0"
diff --git a/README.md b/README.md
index e7c9d1a..3f81d71 100644
--- a/README.md
+++ b/README.md
@@ -131,9 +131,9 @@ Beerus is configurable via a config toml. If you have set the env var
 variables and take configuration from values defined herein.
 Also the the cli can be directed via `berrus --config <path/to/config>`
 
-[goerli.toml](./crates/beerus-core/tests/data/goerli.toml)
+[goerli.toml](./crates/beerus-core/tests/common/data/goerli.toml)
 
-[mainnet.toml](./crates/beerus-core/tests/data/mainnet.toml)
+[mainnet.toml](./crates/beerus-core/tests/common/data/mainnet.toml)
 
 ##### Environment Variables
 
diff --git a/crates/beerus-core/Cargo.toml b/crates/beerus-core/Cargo.toml
index 0e48b35..77b43aa 100644
--- a/crates/beerus-core/Cargo.toml
+++ b/crates/beerus-core/Cargo.toml
@@ -24,7 +24,6 @@ toml = "0.7.3"
 
 [dev-dependencies]
 httpmock = "0.6.7"
-temp-env = "0.3.1"
 serial_test = "1.0.0"
 
 [dev-dependencies.cargo-husky]
diff --git a/crates/beerus-core/src/config.rs b/crates/beerus-core/src/config.rs
index df98883..67f8b17 100644
--- a/crates/beerus-core/src/config.rs
+++ b/crates/beerus-core/src/config.rs
@@ -5,6 +5,7 @@ use log::{error, info};
 use serde::Deserialize;
 use std::env;
 use std::fs;
+use std::net::SocketAddr;
 use std::path::PathBuf;
 use std::str::FromStr;
 
@@ -13,6 +14,7 @@ pub const STARKNET_GOERLI_CC_ADDRESS: &str = "0xde29d060D45901Fb19ED6C6e959EB22d
 pub const DEFAULT_ETHEREUM_NETWORK: &str = "goerli";
 pub const DEFAULT_DATA_DIR: &str = "~/.beerus/tmp";
 pub const DEFAULT_POLL_INTERVAL_SECS: u64 = 5;
+pub const DEFAULT_BEERUS_RPC_ADDR: &str = "0.0.0.0:3030";
 
 /// Global configuration.
 #[derive(Clone, PartialEq, Deserialize, Debug)]
@@ -25,6 +27,7 @@ pub struct Config {
     pub starknet_core_contract_address: Address,
     pub data_dir: PathBuf,
     pub poll_interval_secs: Option<u64>,
+    pub beerus_rpc_address: Option<SocketAddr>,
 }
 
 impl Config {
@@ -57,13 +60,15 @@ impl Config {
         config.ethereum_consensus_rpc = string_env_or_die("ETHEREUM_CONSENSUS_RPC_URL");
         config.ethereum_execution_rpc = string_env_or_die("ETHEREUM_EXECUTION_RPC_URL");
         config.starknet_rpc = string_env_or_die("STARKNET_RPC_URL");
-        config.data_dir = match std::env::var("DATA_DIR") {
-            Ok(dir) => PathBuf::from(dir),
-            Err(e) => {
-                error! {"DATA_DIR: {e}"};
-                panic!();
+        if let Ok(dir) = std::env::var("DATA_DIR") {
+            config.data_dir = PathBuf::from(dir);
+        }
+
+        if let Ok(raw_addr) = std::env::var("BEERUS_RPC_ADDR") {
+            if let Ok(addr) = SocketAddr::from_str(raw_addr.as_str()) {
+                config.beerus_rpc_address = Some(addr);
             }
-        };
+        }
 
         config
     }
@@ -99,6 +104,11 @@ impl Config {
             config.poll_interval_secs = Some(DEFAULT_POLL_INTERVAL_SECS);
         }
 
+        if config.beerus_rpc_address.is_none() {
+            config.beerus_rpc_address =
+                Some(SocketAddr::from_str(DEFAULT_BEERUS_RPC_ADDR).unwrap());
+        }
+
         config
     }
 
@@ -145,6 +155,7 @@ impl Config {
         env::remove_var("ETHEREUM_EXECUTION_RPC_URL");
         env::remove_var("STARKNET_RPC_URL");
         env::remove_var("DATA_DIR");
+        env::remove_var("BEERUS_RPC_ADDR");
     }
 }
 
@@ -168,6 +179,7 @@ impl Default for Config {
             starknet_core_contract_address: Address::from_str(STARKNET_GOERLI_CC_ADDRESS).unwrap(),
             data_dir: PathBuf::from(DEFAULT_DATA_DIR),
             poll_interval_secs: Some(DEFAULT_POLL_INTERVAL_SECS),
+            beerus_rpc_address: Some(SocketAddr::from_str(DEFAULT_BEERUS_RPC_ADDR).unwrap()),
         }
     }
 }
diff --git a/crates/beerus-rpc/Cargo.toml b/crates/beerus-rpc/Cargo.toml
index bf6a6e1..8b4b117 100644
--- a/crates/beerus-rpc/Cargo.toml
+++ b/crates/beerus-rpc/Cargo.toml
@@ -20,6 +20,8 @@ tokio = { version = "1.8.1", features = ["full"] }
 serde_json = "1.0.64"
 env_logger = "0.10.0"
 anyhow = "1.0.69"
-wiremock = "0.5.17"
 serde = "1.0.152"
 reqwest = "0.11.13"
+
+[dev-dependencies]
+wiremock = "0.5.17"
diff --git a/crates/beerus-rpc/src/api.rs b/crates/beerus-rpc/src/api.rs
new file mode 100644
index 0000000..e18b290
--- /dev/null
+++ b/crates/beerus-rpc/src/api.rs
@@ -0,0 +1,145 @@
+use jsonrpsee::{
+    core::Error,
+    proc_macros::rpc,
+    types::error::{CallError, ErrorObject},
+};
+
+use ethers::types::U256;
+use starknet::{
+    core::types::FieldElement,
+    providers::jsonrpc::models::{
+        BlockHashAndNumber, ContractClass, MaybePendingBlockWithTxHashes, MaybePendingBlockWithTxs,
+        MaybePendingTransactionReceipt, StateUpdate, SyncStatusType, Transaction,
+    },
+};
+
+#[derive(thiserror::Error, Clone, Copy, Debug)]
+pub enum BeerusApiError {
+    #[error("Failed to write transaction")]
+    FailedToReceiveTxn = 1,
+    #[error("Contract not found")]
+    ContractNotFound = 20,
+    #[error("Invalid message selector")]
+    InvalidMessageSelector = 21,
+    #[error("Invalid call data")]
+    InvalidCallData = 22,
+    #[error("Block not found")]
+    BlockNotFound = 24,
+    #[error("Transaction hash not found")]
+    TxnHashNotFound = 25,
+    #[error("Invalid transaction index in a block")]
+    InvalidTxnIndex = 27,
+    #[error("Class hash not found")]
+    ClassHashNotFound = 28,
+    #[error("Requested page size is too big")]
+    PageSizeTooBig = 31,
+    #[error("There are no blocks")]
+    NoBlocks = 32,
+    #[error("The supplied continuation token is invalid or unknown")]
+    InvalidContinuationToken = 33,
+    #[error("Contract error")]
+    ContractError = 40,
+    #[error("Invalid contract class")]
+    InvalidContractClass = 50,
+    #[error("Too many storage keys requested")]
+    ProofLimitExceeded = 10000,
+    #[error("Too many keys provided in a filter")]
+    TooManyKeysInFilter = 34,
+    #[error("Internal server error")]
+    InternalServerError = 500,
+}
+
+impl From<BeerusApiError> for Error {
+    fn from(err: BeerusApiError) -> Self {
+        Error::Call(CallError::Custom(ErrorObject::owned(
+            err as i32,
+            err.to_string(),
+            None::<()>,
+        )))
+    }
+}
+
+#[rpc(server, client, namespace = "starknet")]
+pub trait BeerusApi {
+    #[method(name = "l2_to_l1_messages")]
+    async fn l2_to_l1_messages(&self, msg_hash: U256) -> Result<U256, Error>;
+
+    #[method(name = "chainId")]
+    async fn chain_id(&self) -> Result<String, Error>;
+
+    #[method(name = "blockNumber")]
+    async fn block_number(&self) -> Result<u64, Error>;
+
+    #[method(name = "getBlockTransactionCount")]
+    async fn get_block_transaction_count(
+        &self,
+        block_id_type: String,
+        block_id: String,
+    ) -> Result<u64, Error>;
+
+    #[method(name = "getClassAt")]
+    async fn get_class_at(
+        &self,
+        block_id_type: String,
+        block_id: String,
+        contract_address: String,
+    ) -> Result<ContractClass, Error>;
+
+    #[method(name = "blockHashAndNumber")]
+    async fn block_hash_and_number(&self) -> Result<BlockHashAndNumber, Error>;
+
+    #[method(name = "getBlockWithTxHashes")]
+    async fn get_block_with_tx_hashes(
+        &self,
+        block_id_type: String,
+        block_id: String,
+    ) -> Result<MaybePendingBlockWithTxHashes, Error>;
+
+    #[method(name = "getTransactionByBlockIdAndIndex")]
+    async fn get_transaction_by_block_id_and_index(
+        &self,
+        block_id_type: &str,
+        block_id: &str,
+        index: &str,
+    ) -> Result<Transaction, Error>;
+
+    #[method(name = "getBlockWithTxs")]
+    async fn get_block_with_txs(
+        &self,
+        block_id_type: &str,
+        block_id: &str,
+    ) -> Result<MaybePendingBlockWithTxs, Error>;
+
+    #[method(name = "getStateUpdate")]
+    async fn get_state_update(
+        &self,
+        block_id_type: String,
+        block_id: String,
+    ) -> Result<StateUpdate, Error>;
+
+    #[method(name = "syncing")]
+    async fn syncing(&self) -> Result<SyncStatusType, Error>;
+
+    #[method(name = "l1_to_l2_messages")]
+    async fn l1_to_l2_messages(&self, msg_hash: U256) -> Result<U256, Error>;
+
+    #[method(name = "l1_to_l2_message_nonce")]
+    async fn l1_to_l2_message_nonce(&self) -> Result<U256, Error>;
+
+    #[method(name = "l1_to_l2_message_cancellations")]
+    async fn l1_to_l2_message_cancellations(&self, msg_hash: U256) -> Result<U256, Error>;
+
+    #[method(name = "getTransactionReceipt")]
+    async fn get_transaction_receipt(
+        &self,
+        tx_hash: String,
+    ) -> Result<MaybePendingTransactionReceipt, Error>;
+
+    #[method(name = "getClassHash")]
+    async fn get_class_hash(
+        &self,
+        block_id_type: String,
+        block_id: String,
+        contract_address: String,
+    ) -> Result<FieldElement, Error>;
+}
diff --git a/crates/beerus-rpc/src/lib.rs b/crates/beerus-rpc/src/lib.rs
index fc53220..5b7eeee 100644
--- a/crates/beerus-rpc/src/lib.rs
+++ b/crates/beerus-rpc/src/lib.rs
@@ -1,31 +1,241 @@
-use eyre::Result;
-use jsonrpsee::server::{ServerBuilder, ServerHandle};
-use std::net::{AddrParseError, SocketAddr};
-use thiserror::Error;
-pub mod server;
+pub mod api;
+use crate::api::{BeerusApiError, BeerusApiServer};
+use jsonrpsee::{
+    core::{async_trait, Error},
+    server::{ServerBuilder, ServerHandle},
+    types::error::CallError,
+};
+
 use beerus_core::lightclient::beerus::BeerusLightClient;
-use server::{BeerusApiServer, BeerusRpc};
-
-#[derive(Error, Debug)]
-pub enum RpcError {
-    #[error(transparent)]
-    JsonRpcServerError(#[from] jsonrpsee::core::Error),
-    #[error(transparent)]
-    ParseError(#[from] AddrParseError),
+use beerus_core::starknet_helper::block_id_string_to_block_id_type;
+use ethers::types::U256;
+use starknet::{
+    core::types::FieldElement,
+    providers::jsonrpc::models::{
+        BlockHashAndNumber, ContractClass, MaybePendingBlockWithTxHashes, MaybePendingBlockWithTxs,
+        MaybePendingTransactionReceipt, StateUpdate, SyncStatusType, Transaction,
+    },
+};
+use std::net::SocketAddr;
+use std::str::FromStr;
+
+pub struct BeerusRpc {
+    beerus: BeerusLightClient,
+}
+
+impl BeerusRpc {
+    pub fn new(beerus: BeerusLightClient) -> Self {
+        Self { beerus }
+    }
+
+    pub async fn run(self) -> Result<(SocketAddr, ServerHandle), Error> {
+        let server = ServerBuilder::default()
+            .build(self.beerus.config.beerus_rpc_address.unwrap())
+            .await
+            .map_err(|_| Error::from(BeerusApiError::InternalServerError))?;
+
+        let addr = server.local_addr()?;
+        let handle = server.start(self.into_rpc())?;
+
+        Ok((addr, handle))
+    }
 }
 
-pub async fn run_server(beerus: BeerusLightClient) -> Result<(SocketAddr, ServerHandle), RpcError> {
-    let socket_addr = format!(
-        "0.0.0.0:{}",
-        std::env::var("PORT").unwrap_or_else(|_| "3030".to_owned())
-    )
-    .parse::<SocketAddr>()
-    .unwrap();
+#[async_trait]
+impl BeerusApiServer for BeerusRpc {
+    async fn l2_to_l1_messages(&self, msg_hash: U256) -> Result<U256, Error> {
+        Ok(self
+            .beerus
+            .starknet_l2_to_l1_messages(msg_hash)
+            .await
+            .unwrap())
+    }
+
+    async fn chain_id(&self) -> Result<String, Error> {
+        let chain_id = self
+            .beerus
+            .starknet_lightclient
+            .chain_id()
+            .await
+            .unwrap()
+            .to_string();
+
+        Ok(chain_id)
+    }
+
+    async fn block_number(&self) -> Result<u64, Error> {
+        let block_number = self
+            .beerus
+            .starknet_lightclient
+            .block_number()
+            .await
+            .unwrap();
+
+        Ok(block_number)
+    }
+
+    async fn get_block_transaction_count(
+        &self,
+        block_id_type: String,
+        block_id: String,
+    ) -> Result<u64, Error> {
+        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
+        let block_transaction_count = self
+            .beerus
+            .starknet_lightclient
+            .get_block_transaction_count(&block_id)
+            .await
+            .unwrap();
+
+        Ok(block_transaction_count)
+    }
+
+    async fn block_hash_and_number(&self) -> Result<BlockHashAndNumber, Error> {
+        Ok(self
+            .beerus
+            .starknet_lightclient
+            .block_hash_and_number()
+            .await
+            .unwrap())
+    }
+
+    async fn get_class_at(
+        &self,
+        block_id_type: String,
+        block_id: String,
+        contract_address: String,
+    ) -> Result<ContractClass, Error> {
+        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
+        let contract_address = FieldElement::from_str(&contract_address).unwrap();
+        Ok(self
+            .beerus
+            .starknet_lightclient
+            .get_class_at(&block_id, contract_address)
+            .await
+            .unwrap())
+    }
+
+    async fn get_block_with_tx_hashes(
+        &self,
+        block_id_type: String,
+        block_id: String,
+    ) -> Result<MaybePendingBlockWithTxHashes, Error> {
+        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
+        self.beerus
+            .starknet_lightclient
+            .get_block_with_tx_hashes(&block_id)
+            .await
+            .map_err(|_| Error::from(BeerusApiError::BlockNotFound))
+    }
+
+    async fn get_transaction_by_block_id_and_index(
+        &self,
+        block_id_type: &str,
+        block_id: &str,
+        index: &str,
+    ) -> Result<Transaction, Error> {
+        let block_id =
+            beerus_core::starknet_helper::block_id_string_to_block_id_type(block_id_type, block_id)
+                .map_err(|e| {
+                    Error::Call(CallError::InvalidParams(anyhow::anyhow!(e.to_string())))
+                })?;
+        let index = u64::from_str(index)
+            .map_err(|e| Error::Call(CallError::InvalidParams(anyhow::anyhow!(e.to_string()))))?;
+        let result = self
+            .beerus
+            .starknet_lightclient
+            .get_transaction_by_block_id_and_index(&block_id, index)
+            .await
+            .map_err(|e| Error::Call(CallError::Failed(anyhow::anyhow!(e.to_string()))))?;
+        Ok(result)
+    }
+
+    async fn get_block_with_txs(
+        &self,
+        block_id_type: &str,
+        block_id: &str,
+    ) -> Result<MaybePendingBlockWithTxs, Error> {
+        let block_id =
+            beerus_core::starknet_helper::block_id_string_to_block_id_type(block_id_type, block_id)
+                .map_err(|e| {
+                    Error::Call(CallError::InvalidParams(anyhow::anyhow!(e.to_string())))
+                })?;
+        let result = self
+            .beerus
+            .starknet_lightclient
+            .get_block_with_txs(&block_id)
+            .await
+            .map_err(|e| Error::Call(CallError::Failed(anyhow::anyhow!(e.to_string()))))?;
+        Ok(result)
+    }
+
+    async fn get_state_update(
+        &self,
+        block_id_type: String,
+        block_id: String,
+    ) -> Result<StateUpdate, Error> {
+        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
+        Ok(self
+            .beerus
+            .starknet_lightclient
+            .get_state_update(&block_id)
+            .await
+            .unwrap())
+    }
+
+    async fn syncing(&self) -> Result<SyncStatusType, Error> {
+        let sync_status_type = self.beerus.starknet_lightclient.syncing().await.unwrap();
+        Ok(sync_status_type)
+    }
+
+    async fn l1_to_l2_messages(&self, msg_hash: U256) -> Result<U256, Error> {
+        Ok(self
+            .beerus
+            .starknet_l1_to_l2_messages(msg_hash)
+            .await
+            .unwrap())
+    }
+
+    async fn l1_to_l2_message_nonce(&self) -> Result<U256, Error> {
+        let nonce = self.beerus.starknet_l1_to_l2_message_nonce().await.unwrap();
+        Ok(nonce)
+    }
+
+    async fn l1_to_l2_message_cancellations(&self, msg_hash: U256) -> Result<U256, Error> {
+        Ok(self
+            .beerus
+            .starknet_l1_to_l2_message_cancellations(msg_hash)
+            .await
+            .unwrap())
+    }
+
+    async fn get_transaction_receipt(
+        &self,
+        tx_hash: String,
+    ) -> Result<MaybePendingTransactionReceipt, Error> {
+        let tx_hash_felt = FieldElement::from_hex_be(&tx_hash).unwrap();
+        Ok(self
+            .beerus
+            .starknet_lightclient
+            .get_transaction_receipt(tx_hash_felt)
+            .await
+            .unwrap())
+    }
 
-    let server = ServerBuilder::default().build(socket_addr).await?;
-    let addr = server.local_addr()?;
-    let rpc_calls = BeerusRpc::new(beerus);
-    let handle = server.start(rpc_calls.into_rpc()).unwrap();
+    async fn get_class_hash(
+        &self,
+        block_id_type: String,
+        block_id: String,
+        contract_address: String,
+    ) -> Result<FieldElement, Error> {
+        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
+        let contract_address = FieldElement::from_str(&contract_address).unwrap();
 
-    Ok((addr, handle))
+        Ok(self
+            .beerus
+            .starknet_lightclient
+            .get_class_hash_at(&block_id, contract_address)
+            .await
+            .unwrap())
+    }
 }
diff --git a/crates/beerus-rpc/src/main.rs b/crates/beerus-rpc/src/main.rs
index 63e1068..f4724bd 100644
--- a/crates/beerus-rpc/src/main.rs
+++ b/crates/beerus-rpc/src/main.rs
@@ -5,7 +5,7 @@ use beerus_core::{
         starknet::StarkNetLightClientImpl,
     },
 };
-use beerus_rpc::run_server;
+use beerus_rpc::BeerusRpc;
 use env_logger::Env;
 use log::{error, info};
 use std::process::exit;
@@ -48,7 +48,7 @@ async fn main() {
     };
 
     info!("starting beerus rpc server...");
-    match run_server(beerus).await {
+    match BeerusRpc::new(beerus).run().await {
         Ok((addr, server_handle)) => {
             info!("===================================================");
             info!("Beerus JSON-RPC Server started: http://{addr}");
diff --git a/crates/beerus-rpc/src/server.rs b/crates/beerus-rpc/src/server.rs
deleted file mode 100644
index 5c9129a..0000000
--- a/crates/beerus-rpc/src/server.rs
+++ /dev/null
@@ -1,333 +0,0 @@
-use std::str::FromStr;
-
-use beerus_core::lightclient::beerus::BeerusLightClient;
-/// The RPC module for the Ethereum protocol required by Kakarot.
-use jsonrpsee::{
-    core::{async_trait, RpcResult as Result},
-    proc_macros::rpc,
-    types::error::CallError,
-};
-
-use beerus_core::starknet_helper::block_id_string_to_block_id_type;
-use ethers::types::U256;
-use starknet::{
-    core::types::FieldElement,
-    providers::jsonrpc::models::{
-        BlockHashAndNumber, ContractClass, MaybePendingBlockWithTxHashes, MaybePendingBlockWithTxs,
-        MaybePendingTransactionReceipt, StateUpdate, SyncStatusType, Transaction,
-    },
-};
-
-pub struct BeerusRpc {
-    _beerus: BeerusLightClient,
-}
-
-#[rpc(server, client)]
-trait BeerusApi {
-    #[method(name = "hello_world")]
-    async fn hello_world(&self) -> Result<String>;
-
-    #[method(name = "starknet_l2_to_l1_messages")]
-    async fn starknet_l2_to_l1_messages(&self, msg_hash: U256) -> Result<U256>;
-
-    #[method(name = "starknet_chainId")]
-    async fn starknet_chain_id(&self) -> Result<String>;
-
-    #[method(name = "starknet_blockNumber")]
-    async fn starknet_block_number(&self) -> Result<u64>;
-
-    #[method(name = "starknet_getBlockTransactionCount")]
-    async fn starknet_get_block_transaction_count(
-        &self,
-        block_id_type: String,
-        block_id: String,
-    ) -> Result<u64>;
-
-    #[method(name = "starknet_getClassAt")]
-    async fn starknet_get_class_at(
-        &self,
-        block_id_type: String,
-        block_id: String,
-        contract_address: String,
-    ) -> Result<ContractClass>;
-
-    #[method(name = "starknet_blockHashAndNumber")]
-    async fn starknet_block_hash_and_number(&self) -> Result<BlockHashAndNumber>;
-
-    #[method(name = "starknet_getBlockWithTxHashes")]
-    async fn stark_get_block_with_tx_hashes(
-        &self,
-        block_id_type: String,
-        block_id: String,
-    ) -> Result<MaybePendingBlockWithTxHashes>;
-
-    #[method(name = "starknet_getTransactionByBlockIdAndIndex")]
-    async fn starknet_get_transaction_by_block_id_and_index(
-        &self,
-        block_id_type: &str,
-        block_id: &str,
-        index: &str,
-    ) -> Result<Transaction>;
-
-    #[method(name = "starknet_getBlockWithTxs")]
-    async fn starknet_get_block_with_txs(
-        &self,
-        block_id_type: &str,
-        block_id: &str,
-    ) -> Result<MaybePendingBlockWithTxs>;
-
-    #[method(name = "starknet_getStateUpdate")]
-    async fn starknet_get_state_update(
-        &self,
-        block_id_type: String,
-        block_id: String,
-    ) -> Result<StateUpdate>;
-
-    #[method(name = "starknet_syncing")]
-    async fn starknet_syncing(&self) -> Result<SyncStatusType>;
-
-    #[method(name = "starknet_l1_to_l2_messages")]
-    async fn starknet_l1_to_l2_messages(&self, msg_hash: U256) -> Result<U256>;
-
-    #[method(name = "starknet_l1_to_l2_message_nonce")]
-    async fn starknet_l1_to_l2_message_nonce(&self) -> Result<U256>;
-
-    #[method(name = "starknet_l1_to_l2_message_cancellations")]
-    async fn starknet_l1_to_l2_message_cancellations(&self, msg_hash: U256) -> Result<U256>;
-
-    #[method(name = "starknet_getTransactionReceipt")]
-    async fn starknet_get_transaction_receipt(
-        &self,
-        tx_hash: String,
-    ) -> Result<MaybePendingTransactionReceipt>;
-
-    #[method(name = "starknet_getClassHash")]
-    async fn starknet_get_class_hash(
-        &self,
-        block_id_type: String,
-        block_id: String,
-        contract_address: String,
-    ) -> Result<FieldElement>;
-}
-
-#[async_trait]
-impl BeerusApiServer for BeerusRpc {
-    async fn hello_world(&self) -> Result<String> {
-        Ok("Hello World!".to_string())
-    }
-
-    async fn starknet_l2_to_l1_messages(&self, msg_hash: U256) -> Result<U256> {
-        Ok(self
-            ._beerus
-            .starknet_l2_to_l1_messages(msg_hash)
-            .await
-            .unwrap())
-    }
-
-    async fn starknet_chain_id(&self) -> Result<String> {
-        let chain_id = self
-            ._beerus
-            .starknet_lightclient
-            .chain_id()
-            .await
-            .unwrap()
-            .to_string();
-
-        Ok(chain_id)
-    }
-
-    async fn starknet_block_number(&self) -> Result<u64> {
-        let block_number = self
-            ._beerus
-            .starknet_lightclient
-            .block_number()
-            .await
-            .unwrap();
-
-        Ok(block_number)
-    }
-
-    async fn starknet_get_block_transaction_count(
-        &self,
-        block_id_type: String,
-        block_id: String,
-    ) -> Result<u64> {
-        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
-        let block_transaction_count = self
-            ._beerus
-            .starknet_lightclient
-            .get_block_transaction_count(&block_id)
-            .await
-            .unwrap();
-
-        Ok(block_transaction_count)
-    }
-
-    async fn starknet_block_hash_and_number(&self) -> Result<BlockHashAndNumber> {
-        Ok(self
-            ._beerus
-            .starknet_lightclient
-            .block_hash_and_number()
-            .await
-            .unwrap())
-    }
-
-    async fn starknet_get_class_at(
-        &self,
-        block_id_type: String,
-        block_id: String,
-        contract_address: String,
-    ) -> Result<ContractClass> {
-        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
-        let contract_address = FieldElement::from_str(&contract_address).unwrap();
-        Ok(self
-            ._beerus
-            .starknet_lightclient
-            .get_class_at(&block_id, contract_address)
-            .await
-            .unwrap())
-    }
-
-    async fn stark_get_block_with_tx_hashes(
-        &self,
-        block_id_type: String,
-        block_id: String,
-    ) -> Result<MaybePendingBlockWithTxHashes> {
-        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
-        Ok(self
-            ._beerus
-            .starknet_lightclient
-            .get_block_with_tx_hashes(&block_id)
-            .await
-            .unwrap())
-    }
-
-    async fn starknet_get_transaction_by_block_id_and_index(
-        &self,
-        block_id_type: &str,
-        block_id: &str,
-        index: &str,
-    ) -> Result<Transaction> {
-        let block_id =
-            beerus_core::starknet_helper::block_id_string_to_block_id_type(block_id_type, block_id)
-                .map_err(|e| {
-                    jsonrpsee::core::Error::Call(CallError::InvalidParams(anyhow::anyhow!(
-                        e.to_string()
-                    )))
-                })?;
-        let index = u64::from_str(index).map_err(|e| {
-            jsonrpsee::core::Error::Call(CallError::InvalidParams(anyhow::anyhow!(e.to_string())))
-        })?;
-        let result = self
-            ._beerus
-            .starknet_lightclient
-            .get_transaction_by_block_id_and_index(&block_id, index)
-            .await
-            .map_err(|e| {
-                jsonrpsee::core::Error::Call(CallError::Failed(anyhow::anyhow!(e.to_string())))
-            })?;
-        Ok(result)
-    }
-    async fn starknet_get_block_with_txs(
-        &self,
-        block_id_type: &str,
-        block_id: &str,
-    ) -> Result<MaybePendingBlockWithTxs> {
-        let block_id =
-            beerus_core::starknet_helper::block_id_string_to_block_id_type(block_id_type, block_id)
-                .map_err(|e| {
-                    jsonrpsee::core::Error::Call(CallError::InvalidParams(anyhow::anyhow!(
-                        e.to_string()
-                    )))
-                })?;
-        let result = self
-            ._beerus
-            .starknet_lightclient
-            .get_block_with_txs(&block_id)
-            .await
-            .map_err(|e| {
-                jsonrpsee::core::Error::Call(CallError::Failed(anyhow::anyhow!(e.to_string())))
-            })?;
-        Ok(result)
-    }
-
-    async fn starknet_get_state_update(
-        &self,
-        block_id_type: String,
-        block_id: String,
-    ) -> Result<StateUpdate> {
-        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
-        Ok(self
-            ._beerus
-            .starknet_lightclient
-            .get_state_update(&block_id)
-            .await
-            .unwrap())
-    }
-
-    async fn starknet_syncing(&self) -> Result<SyncStatusType> {
-        let sync_status_type = self._beerus.starknet_lightclient.syncing().await.unwrap();
-        Ok(sync_status_type)
-    }
-
-    async fn starknet_l1_to_l2_messages(&self, msg_hash: U256) -> Result<U256> {
-        Ok(self
-            ._beerus
-            .starknet_l1_to_l2_messages(msg_hash)
-            .await
-            .unwrap())
-    }
-
-    async fn starknet_l1_to_l2_message_nonce(&self) -> Result<U256> {
-        let nonce = self
-            ._beerus
-            .starknet_l1_to_l2_message_nonce()
-            .await
-            .unwrap();
-        Ok(nonce)
-    }
-
-    async fn starknet_l1_to_l2_message_cancellations(&self, msg_hash: U256) -> Result<U256> {
-        Ok(self
-            ._beerus
-            .starknet_l1_to_l2_message_cancellations(msg_hash)
-            .await
-            .unwrap())
-    }
-
-    async fn starknet_get_transaction_receipt(
-        &self,
-        tx_hash: String,
-    ) -> Result<MaybePendingTransactionReceipt> {
-        let tx_hash_felt = FieldElement::from_hex_be(&tx_hash).unwrap();
-        Ok(self
-            ._beerus
-            .starknet_lightclient
-            .get_transaction_receipt(tx_hash_felt)
-            .await
-            .unwrap())
-    }
-
-    async fn starknet_get_class_hash(
-        &self,
-        block_id_type: String,
-        block_id: String,
-        contract_address: String,
-    ) -> Result<FieldElement> {
-        let block_id = block_id_string_to_block_id_type(&block_id_type, &block_id).unwrap();
-        let contract_address = FieldElement::from_str(&contract_address).unwrap();
-
-        Ok(self
-            ._beerus
-            .starknet_lightclient
-            .get_class_hash_at(&block_id, contract_address)
-            .await
-            .unwrap())
-    }
-}
-
-impl BeerusRpc {
-    pub fn new(beerus: BeerusLightClient) -> Self {
-        Self { _beerus: beerus }
-    }
-}


Test patch:
diff --git a/crates/beerus-core/tests/data/data.json b/crates/beerus-core/tests/common/data/data.json
similarity index 100%
rename from crates/beerus-core/tests/data/data.json
rename to crates/beerus-core/tests/common/data/data.json
diff --git a/crates/beerus-core/tests/data/goerli.toml b/crates/beerus-core/tests/common/data/goerli.toml
similarity index 87%
rename from crates/beerus-core/tests/data/goerli.toml
rename to crates/beerus-core/tests/common/data/goerli.toml
index 367c85a..ed193fb 100644
--- a/crates/beerus-core/tests/data/goerli.toml
+++ b/crates/beerus-core/tests/common/data/goerli.toml
@@ -2,4 +2,5 @@
     ethereum_consensus_rpc = "http://testing.prater.beacon-api.nimbus.team"
     ethereum_execution_rpc = "https://eth-goerli.g.alchemy.com/v2/XXXXX"
     starknet_rpc = "https://starknet-goerli.infura.io/v3/XXXXX"
-    data_dir = "~/.beerus/tmp"
\ No newline at end of file
+    data_dir = "~/.beerus/tmp"
+    
\ No newline at end of file
diff --git a/crates/beerus-core/tests/data/mainnet.toml b/crates/beerus-core/tests/common/data/mainnet.toml
similarity index 89%
rename from crates/beerus-core/tests/data/mainnet.toml
rename to crates/beerus-core/tests/common/data/mainnet.toml
index 88e959d..5ec90e1 100644
--- a/crates/beerus-core/tests/data/mainnet.toml
+++ b/crates/beerus-core/tests/common/data/mainnet.toml
@@ -3,4 +3,5 @@
     ethereum_execution_rpc = "https://eth-mainnet.g.alchemy.com/v2/XXXXX"
     starknet_rpc = "https://starknet-mainnet.infura.io/v3/XXXXX"
     data_dir = "~/.beerus/tmp"
-    poll_interval_secs = 10
\ No newline at end of file
+    poll_interval_secs = 10
+    
\ No newline at end of file
diff --git a/crates/beerus-core/tests/common/mod.rs b/crates/beerus-core/tests/common/mod.rs
index fa38916..187f9a6 100644
--- a/crates/beerus-core/tests/common/mod.rs
+++ b/crates/beerus-core/tests/common/mod.rs
@@ -1,5 +1,5 @@
 use beerus_core::{
-    config::Config,
+    config::{Config, DEFAULT_BEERUS_RPC_ADDR},
     lightclient::{
         ethereum::MockEthereumLightClient,
         starknet::{storage_proof::GetProofOutput, MockStarkNetLightClient},
@@ -10,7 +10,7 @@ use httpmock::{prelude::*, Mock};
 use serde::{Deserialize, Serialize};
 use serde_json::json;
 use std::fs;
-
+use std::net::SocketAddr;
 use std::path::PathBuf;
 use std::str::FromStr;
 
@@ -23,7 +23,7 @@ pub fn mock_clients() -> (Config, MockEthereumLightClient, MockStarkNetLightClie
 }
 
 pub fn mock_get_contract_storage_proof(server: &MockServer) -> (Mock, GetProofOutput) {
-    let path = "tests/data/data.json";
+    let path = "tests/common/data/data.json";
     let s = fs::read_to_string(path).unwrap();
 
     #[derive(Debug, Serialize, Deserialize)]
@@ -117,5 +117,6 @@ pub fn mock_server_config(server: &MockServer) -> Config {
         .unwrap(),
         data_dir: PathBuf::from("/tmp"),
         poll_interval_secs: Some(5),
+        beerus_rpc_address: Some(SocketAddr::from_str(DEFAULT_BEERUS_RPC_ADDR).unwrap()),
     }
 }
diff --git a/crates/beerus-core/tests/config.rs b/crates/beerus-core/tests/config.rs
index 4b0bcd2..eebb3cb 100644
--- a/crates/beerus-core/tests/config.rs
+++ b/crates/beerus-core/tests/config.rs
@@ -14,7 +14,7 @@ mod tests {
     #[test]
     fn mainnet_file_config_returns_correct_values() {
         let mainnet_file_config: Config =
-            Config::from_file(&PathBuf::from("tests/data/mainnet.toml"));
+            Config::from_file(&PathBuf::from("tests/common/data/mainnet.toml"));
 
         assert_eq!(mainnet_file_config.ethereum_network, "mainnet");
         assert_eq!(
@@ -33,7 +33,7 @@ mod tests {
     #[test]
     fn goerli_file_config_returns_correct_values() {
         let goerli_file_config: Config =
-            Config::from_file(&PathBuf::from("tests/data/goerli.toml"));
+            Config::from_file(&PathBuf::from("tests/common/data/goerli.toml"));
 
         assert_eq!(
             goerli_file_config.ethereum_network,
@@ -72,7 +72,6 @@ mod tests {
 
     /// Test `from_env` function.
     #[test]
-    #[serial]
     fn all_envs_set_returns_config() {
         Config::clean_env();
         env::set_var("ETHEREUM_NETWORK", "mainnet");
@@ -105,6 +104,24 @@ mod tests {
         assert_eq!(cfg.ethereum_network, "goerli");
     }
 
+    /// Env Var `BEERUS_CONFIG`
+    /// Should override the config parsing to the file
+    #[test]
+    #[serial]
+    fn beerus_config_env_var_should_override() {
+        env::set_var("BEERUS_CONFIG", "tests/common/data/goerli.toml");
+        let cfg = Config::from_env();
+
+        assert_eq!(
+            cfg.ethereum_consensus_rpc,
+            "http://testing.prater.beacon-api.nimbus.team"
+        );
+        assert_eq!(
+            cfg.ethereum_execution_rpc,
+            "https://eth-goerli.g.alchemy.com/v2/XXXXX"
+        );
+    }
+
     /// Test `from_env` function when `ETHEREUM_CONSENSUS_RPC_URL` is not set.
     /// It should return an error.
     #[test]
@@ -146,22 +163,4 @@ mod tests {
 
         let _cfg = Config::from_env();
     }
-
-    /// Env Var `BEERUS_CONFIG`
-    /// Should override the config parsing to the file
-    #[test]
-    #[serial]
-    fn beerus_config_env_var_should_override() {
-        env::set_var("BEERUS_CONFIG", "tests/data/goerli.toml");
-        let cfg = Config::from_env();
-
-        assert_eq!(
-            cfg.ethereum_consensus_rpc,
-            "http://testing.prater.beacon-api.nimbus.team"
-        );
-        assert_eq!(
-            cfg.ethereum_execution_rpc,
-            "https://eth-goerli.g.alchemy.com/v2/XXXXX"
-        );
-    }
 }
diff --git a/crates/beerus-core/tests/proofs.rs b/crates/beerus-core/tests/proofs.rs
index 2e81176..fa2bcf0 100644
--- a/crates/beerus-core/tests/proofs.rs
+++ b/crates/beerus-core/tests/proofs.rs
@@ -10,7 +10,7 @@ struct JsonOutput {
 
 #[test]
 fn non_membership() {
-    let path = "tests/data/data.json";
+    let path = "tests/common/data/data.json";
     let s = fs::read_to_string(path).unwrap();
 
     let state_root = FieldElement::from_hex_be(
diff --git a/crates/beerus-rpc/tests/common/data/test.toml b/crates/beerus-rpc/tests/common/data/test.toml
new file mode 100644
index 0000000..f8bea51
--- /dev/null
+++ b/crates/beerus-rpc/tests/common/data/test.toml
@@ -0,0 +1,5 @@
+    ethereum_network = "goerli"
+    ethereum_consensus_rpc = ""
+    ethereum_execution_rpc = ""
+    starknet_rpc = ""
+    data_dir = ""
diff --git a/crates/beerus-rpc/tests/common/mod.rs b/crates/beerus-rpc/tests/common/mod.rs
index 818ca23..e560e44 100644
--- a/crates/beerus-rpc/tests/common/mod.rs
+++ b/crates/beerus-rpc/tests/common/mod.rs
@@ -5,10 +5,11 @@ use beerus_core::{
         starknet::StarkNetLightClientImpl,
     },
 };
-use beerus_rpc::server::BeerusRpc;
+use beerus_rpc::BeerusRpc;
 use reqwest::StatusCode;
 use serde::{Deserialize, Serialize};
 use starknet::providers::jsonrpc::models::{BlockId, BlockTag};
+use std::path::PathBuf;
 use wiremock::{
     matchers::{body_json, method},
     Mock, MockServer, ResponseTemplate,
@@ -30,7 +31,7 @@ pub struct EthJsonRpcResponse<StarknetParams> {
 }
 
 impl<'a, StarknetParams> StarknetRpcBaseData<'a, StarknetParams> {
-    pub const fn stark_block_number(params: StarknetParams) -> Self {
+    pub const fn block_number(params: StarknetParams) -> Self {
         Self {
             id: 1,
             jsonrpc: "2.0",
@@ -57,9 +58,8 @@ pub async fn setup_wiremock() -> String {
 }
 
 pub async fn setup_beerus_rpc() -> BeerusRpc {
-    let mock_starknet_rpc = setup_wiremock().await;
-    set_mandatory_envs(mock_starknet_rpc);
-    let config = Config::from_env();
+    let mut config = Config::from_file(&PathBuf::from("tests/common/data/test.toml"));
+    config.starknet_rpc = setup_wiremock().await;
 
     let ethereum_lightclient = MockEthereumLightClient::new();
     let starknet_lightclient = StarkNetLightClientImpl::new(&config).unwrap();
@@ -72,17 +72,9 @@ pub async fn setup_beerus_rpc() -> BeerusRpc {
     BeerusRpc::new(beerus_client)
 }
 
-fn set_mandatory_envs(starknet_rpc: String) {
-    Config::clean_env();
-    std::env::set_var("ETHEREUM_CONSENSUS_RPC_URL", "");
-    std::env::set_var("ETHEREUM_EXECUTION_RPC_URL", "");
-    std::env::set_var("STARKNET_RPC_URL", starknet_rpc);
-    std::env::set_var("DATA_DIR", "");
-}
-
 fn mock_block_number() -> Mock {
     Mock::given(method("POST"))
-        .and(body_json(StarknetRpcBaseData::stark_block_number(())))
+        .and(body_json(StarknetRpcBaseData::block_number(())))
         .respond_with(response_template_with_status(StatusCode::OK).set_body_raw(
             include_str!("data/starknet_blockNumber.json"),
             "application/json",
diff --git a/crates/beerus-rpc/tests/rpc.rs b/crates/beerus-rpc/tests/rpc.rs
index da1c91c..a7f6885 100644
--- a/crates/beerus-rpc/tests/rpc.rs
+++ b/crates/beerus-rpc/tests/rpc.rs
@@ -3,24 +3,40 @@ mod common;
 #[cfg(test)]
 mod tests {
     use crate::common::setup_beerus_rpc;
-    use beerus_rpc::server::BeerusApiServer;
+    use beerus_rpc::api::{BeerusApiError, BeerusApiServer};
+    use jsonrpsee::types::error::ErrorObjectOwned;
 
     #[tokio::test]
-    async fn test_block_number_is_ok() {
+    async fn block_number_ok() {
         let beerus_rpc = setup_beerus_rpc().await;
-        let block_number = beerus_rpc.starknet_block_number().await.unwrap();
+        let block_number = beerus_rpc.block_number().await.unwrap();
         assert_eq!(block_number, 19640);
     }
 
     #[tokio::test]
-    async fn test_get_block_transaction_count_is_ok() {
+    async fn block_transaction_count_ok() {
         let beerus_rpc = setup_beerus_rpc().await;
-        let block_id_type = "tag".to_string();
-        let block_id = "latest".to_string();
         let transaction_count = beerus_rpc
-            .starknet_get_block_transaction_count(block_id_type, block_id)
+            .get_block_transaction_count("tag".to_string(), "latest".to_string())
             .await
             .unwrap();
+
         assert_eq!(transaction_count, 90);
     }
+
+    #[tokio::test]
+    async fn error_response_block_not_found() {
+        let beerus_rpc = setup_beerus_rpc().await;
+        let err = beerus_rpc
+            .get_block_with_tx_hashes("number".to_string(), "22050".to_string())
+            .await
+            .unwrap_err();
+
+        let beerus_rpc_err = ErrorObjectOwned::from(err);
+        assert_eq!(beerus_rpc_err.code(), BeerusApiError::BlockNotFound as i32);
+        assert_eq!(
+            beerus_rpc_err.message(),
+            BeerusApiError::BlockNotFound.to_string()
+        );
+    }
 }