EthereumErc20 {
let ret = self.provider.call(request, Some(block_id)).await?;
let balance = U256::try_from_be_slice(&ret)
- .ok_or_else(|| KakarotError::from(ExecutionError::Other("failed to deserialize balance".to_string())))?;
+ .ok_or_else(|| ExecutionError::Other("failed to deserialize balance".to_string()))?;
Ok(balance)
}
diff --git a/src/eth_provider/database/ethereum.rs b/src/eth_provider/database/ethereum.rs
index cc4ada8d6..287fdd0ef 100644
--- a/src/eth_provider/database/ethereum.rs
+++ b/src/eth_provider/database/ethereum.rs
@@ -1,10 +1,14 @@
+use alloy_rlp::Encodable;
use async_trait::async_trait;
-use reth_primitives::B256;
-use reth_rpc_types::Transaction;
+use mongodb::bson::doc;
+use reth_primitives::constants::EMPTY_ROOT_HASH;
+use reth_primitives::{TransactionSigned, B256, U256};
+use reth_rpc_types::{Block, BlockHashOrNumber, BlockTransactions, Header, RichBlock, Transaction};
-use crate::eth_provider::error::EthApiError;
+use crate::eth_provider::error::{EthApiError, EthereumDataFormatError};
-use super::filter;
+use super::types::header::StoredHeader;
+use super::{filter, FindOpts};
use super::{
filter::EthDatabaseFilterBuilder,
types::transaction::{StoredPendingTransaction, StoredTransaction},
@@ -12,12 +16,18 @@ use super::{
};
/// Trait for interacting with a database that stores Ethereum typed
-/// data.
+/// transaction data.
#[async_trait]
-pub trait EthereumDatabase {
- /// Returns the transaction with the given hash.
+pub trait EthereumTransactionStore {
+ /// Returns the transaction with the given hash. Returns None if the
+ /// transaction is not found.
async fn transaction(&self, hash: &B256) -> Result, EthApiError>;
- /// Returns the pending transaction with the given hash.
+ /// Returns all transactions for the given block hash or number.
+ async fn transactions(&self, block_hash_or_number: BlockHashOrNumber) -> Result, EthApiError>;
+ /// Returns all transactions hashes for the given block hash or number.
+ async fn transaction_hashes(&self, block_hash_or_number: BlockHashOrNumber) -> Result, EthApiError>;
+ /// Returns the pending transaction with the given hash. Returns None if the
+ /// transaction is not found.
async fn pending_transaction(&self, hash: &B256) -> Result, EthApiError>;
/// Returns the pending transaction's retries with the given hash.
/// Returns 0 if the transaction is not found.
@@ -29,12 +39,33 @@ pub trait EthereumDatabase {
}
#[async_trait]
-impl EthereumDatabase for Database {
+impl EthereumTransactionStore for Database {
async fn transaction(&self, hash: &B256) -> Result , EthApiError> {
let filter = EthDatabaseFilterBuilder::::default().with_tx_hash(hash).build();
Ok(self.get_one::(filter, None).await?.map(Into::into))
}
+ async fn transactions(&self, block_hash_or_number: BlockHashOrNumber) -> Result, EthApiError> {
+ let filter = EthDatabaseFilterBuilder::::default()
+ .with_block_hash_or_number(block_hash_or_number)
+ .build();
+
+ Ok(self.get::(filter, None).await?.into_iter().map(Into::into).collect())
+ }
+
+ async fn transaction_hashes(&self, block_hash_or_number: BlockHashOrNumber) -> Result, EthApiError> {
+ let filter = EthDatabaseFilterBuilder::::default()
+ .with_block_hash_or_number(block_hash_or_number)
+ .build();
+
+ Ok(self
+ .get::(filter, FindOpts::default().with_projection(doc! {"tx.hash": 1}))
+ .await?
+ .into_iter()
+ .map(|tx| tx.tx.hash)
+ .collect())
+ }
+
async fn pending_transaction(&self, hash: &B256) -> Result, EthApiError> {
let filter = EthDatabaseFilterBuilder::::default().with_tx_hash(hash).build();
Ok(self.get_one::(filter, None).await?.map(Into::into))
@@ -64,3 +95,107 @@ impl EthereumDatabase for Database {
Ok(self.update_one(StoredPendingTransaction::new(transaction, retries), filter, true).await?)
}
}
+
+/// Trait for interacting with a database that stores Ethereum typed
+/// blocks.
+#[async_trait]
+pub trait EthereumBlockStore {
+ /// Returns the header for the given hash or number. Returns None if the
+ /// header is not found.
+ async fn header(&self, block_hash_or_number: BlockHashOrNumber) -> Result, EthApiError>;
+ /// Returns the block for the given hash or number. Returns None if the
+ /// block is not found.
+ async fn block(
+ &self,
+ block_hash_or_number: BlockHashOrNumber,
+ full: bool,
+ ) -> Result , EthApiError>;
+ /// Returns true if the block with the given hash or number exists.
+ async fn block_exists(&self, block_hash_or_number: BlockHashOrNumber) -> Result {
+ self.header(block_hash_or_number).await.map(|header| header.is_some())
+ }
+ /// Returns the transaction count for the given block hash or number. Returns None if the
+ /// block is not found.
+ async fn transaction_count(&self, block_hash_or_number: BlockHashOrNumber) -> Result, EthApiError>;
+}
+
+#[async_trait]
+impl EthereumBlockStore for Database {
+ async fn header(&self, block_hash_or_number: BlockHashOrNumber) -> Result , EthApiError> {
+ let filter = EthDatabaseFilterBuilder::::default()
+ .with_block_hash_or_number(block_hash_or_number)
+ .build();
+ Ok(self
+ .get_one::(filter, None)
+ .await
+ .inspect_err(|err| tracing::error!("internal error: {:?}", err))
+ .map_err(|_| EthApiError::UnknownBlock(block_hash_or_number))?
+ .map(|sh| sh.header))
+ }
+
+ async fn block(
+ &self,
+ block_hash_or_number: BlockHashOrNumber,
+ full: bool,
+ ) -> Result, EthApiError> {
+ let maybe_header = self.header(block_hash_or_number).await?;
+ if maybe_header.is_none() {
+ return Ok(None);
+ }
+ let header = maybe_header.unwrap();
+
+ // The withdrawals are not supported, hence the withdrawals_root should always be empty.
+ if let Some(withdrawals_root) = header.withdrawals_root {
+ if withdrawals_root != EMPTY_ROOT_HASH {
+ return Err(EthApiError::Unsupported("withdrawals"));
+ }
+ }
+
+ let transactions = self.transactions(block_hash_or_number).await?;
+ let block_transactions = if full {
+ BlockTransactions::Full(transactions.clone())
+ } else {
+ BlockTransactions::Hashes(transactions.iter().map(|tx| tx.hash).collect())
+ };
+
+ let signed_transactions = transactions
+ .into_iter()
+ .map(|tx| TransactionSigned::try_from(tx).map_err(|_| EthereumDataFormatError::TransactionConversion))
+ .collect::, _>>()?;
+
+ let block = reth_primitives::Block {
+ body: signed_transactions,
+ header: reth_primitives::Header::try_from(header.clone())
+ .map_err(|_| EthereumDataFormatError::Primitive)?,
+ withdrawals: Some(Default::default()),
+ ..Default::default()
+ };
+
+ // This is how Reth computes the block size.
+ // `https://github.com/paradigmxyz/reth/blob/v0.2.0-beta.5/crates/rpc/rpc-types-compat/src/block.rs#L66`
+ let size = block.length();
+
+ Ok(Some(
+ Block {
+ header,
+ transactions: block_transactions,
+ size: Some(U256::from(size)),
+ withdrawals: Some(Default::default()),
+ ..Default::default()
+ }
+ .into(),
+ ))
+ }
+
+ async fn transaction_count(&self, block_hash_or_number: BlockHashOrNumber) -> Result, EthApiError> {
+ if !self.block_exists(block_hash_or_number).await? {
+ return Ok(None);
+ }
+
+ let filter = EthDatabaseFilterBuilder::::default()
+ .with_block_hash_or_number(block_hash_or_number)
+ .build();
+ let count = self.count::(filter).await?;
+ Ok(Some(U256::from(count)))
+ }
+}
diff --git a/src/eth_provider/database/filter.rs b/src/eth_provider/database/filter.rs
index 4276ca877..e062a0470 100644
--- a/src/eth_provider/database/filter.rs
+++ b/src/eth_provider/database/filter.rs
@@ -2,7 +2,7 @@ use std::fmt::{Display, LowerHex};
use mongodb::bson::{doc, Document};
use reth_primitives::{Address, B256};
-use reth_rpc_types::{Index, Topic};
+use reth_rpc_types::{BlockHashOrNumber, Index, Topic};
use crate::eth_provider::constant::{
ADDRESS_HEX_STRING_LEN, BLOCK_NUMBER_HEX_STRING_LEN, HASH_HEX_STRING_LEN, LOGS_TOPICS_HEX_STRING_LEN,
@@ -165,6 +165,15 @@ impl EthDatabaseFilterBuilder {
self.filter.insert(key, format_hex(number, BLOCK_NUMBER_HEX_STRING_LEN));
self
}
+
+ /// Adds a filter on the block hash or number.
+ #[must_use]
+ pub fn with_block_hash_or_number(self, block_hash_or_number: BlockHashOrNumber) -> Self {
+ match block_hash_or_number {
+ BlockHashOrNumber::Hash(hash) => self.with_block_hash(&hash),
+ BlockHashOrNumber::Number(number) => self.with_block_number(number),
+ }
+ }
}
impl EthDatabaseFilterBuilder {
diff --git a/src/eth_provider/database/mod.rs b/src/eth_provider/database/mod.rs
index 1d6e27a8f..248b7f383 100644
--- a/src/eth_provider/database/mod.rs
+++ b/src/eth_provider/database/mod.rs
@@ -76,11 +76,12 @@ impl Database {
pub async fn get(
&self,
filter: impl Into>,
- find_options: Option,
+ find_options: impl Into>,
) -> DatabaseResult>
where
T: DeserializeOwned + CollectionName,
{
+ let find_options = find_options.into();
Ok(self.collection::().find(filter, find_options.unwrap_or_default().build()).await?.try_collect().await?)
}
diff --git a/src/eth_provider/error.rs b/src/eth_provider/error.rs
index 19d61db01..351e33874 100644
--- a/src/eth_provider/error.rs
+++ b/src/eth_provider/error.rs
@@ -1,4 +1,4 @@
-use alloy_sol_types::SolType;
+use alloy_sol_types::decode_revert_reason;
use jsonrpsee::types::ErrorObject;
use reth_primitives::{Bytes, B256};
use reth_rpc_types::BlockHashOrNumber;
@@ -26,8 +26,8 @@ pub enum EthRpcErrorCode {
JsonRpcVersionUnsupported = -32006,
}
-impl From for EthRpcErrorCode {
- fn from(error: EthApiError) -> Self {
+impl From<&EthApiError> for EthRpcErrorCode {
+ fn from(error: &EthApiError) -> Self {
match error {
EthApiError::UnknownBlock(_) | EthApiError::UnknownBlockNumber(_) | EthApiError::TransactionNotFound(_) => {
Self::ResourceNotFound
@@ -36,52 +36,56 @@ impl From for EthRpcErrorCode {
| EthApiError::EthereumDataFormat(_)
| EthApiError::CalldataExceededLimit(_, _) => Self::InvalidParams,
EthApiError::Transaction(err) => err.into(),
- EthApiError::Unsupported(_) => Self::InternalError,
- EthApiError::Kakarot(err) => err.into(),
+ EthApiError::Unsupported(_) | EthApiError::Kakarot(_) => Self::InternalError,
+ EthApiError::Execution(_) => Self::ExecutionError,
}
}
}
/// Error that can occur when interacting with the ETH Api.
-#[derive(Error)]
+#[derive(Debug, Error)]
pub enum EthApiError {
/// When a block is not found
- #[error("unknown block {0}")]
UnknownBlock(BlockHashOrNumber),
/// When an unknown block number is encountered
- #[error("unknown block number {0:?}")]
UnknownBlockNumber(Option),
/// When a transaction is not found
- #[error("transaction not found {0}")]
TransactionNotFound(B256),
/// Error related to transaction
- #[error("transaction error: {0}")]
Transaction(#[from] TransactionError),
/// Error related to signing
- #[error("signature error: {0}")]
Signature(#[from] SignatureError),
/// Unsupported feature
- #[error("unsupported: {0}")]
Unsupported(&'static str),
/// Ethereum data format error
- #[error("ethereum data format error: {0}")]
EthereumDataFormat(#[from] EthereumDataFormatError),
- /// Other Kakarot error
- #[error("kakarot error: {0}")]
+ /// Execution error
+ Execution(#[from] ExecutionError),
+ /// Kakarot related error (database, ...)
Kakarot(KakarotError),
/// Error related to transaction calldata being too large.
- #[error("calldata exceeded limit of {0}: {1}")]
CalldataExceededLimit(usize, usize),
}
-impl std::fmt::Debug for EthApiError {
+impl std::fmt::Display for EthApiError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
+ Self::UnknownBlock(block) => write!(f, "unknown block {block}"),
+ Self::UnknownBlockNumber(block) => write!(f, "unknown block number {block:?}"),
+ Self::TransactionNotFound(tx) => write!(f, "transaction not found {tx}"),
+ Self::Transaction(err) => write!(f, "{err}"),
+ Self::Signature(err) => write!(f, "{err}"),
+ Self::Unsupported(feature) => write!(f, "unsupported: {feature}"),
+ Self::EthereumDataFormat(err) => write!(f, "ethereum data format error: {err}"),
+ Self::Execution(err) => write!(f, "{err}"),
Self::Kakarot(KakarotError::Provider(err)) => {
+ // We use Debug here otherwise we risk losing some information on contract error
write!(f, "starknet provider error: {err:?}")
}
- Self::Kakarot(KakarotError::Execution(err)) => write!(f, "execution reverted: {err:?}"),
- _ => write!(f, "{self}"),
+ Self::Kakarot(err) => write!(f, "kakarot error: {err}"),
+ Self::CalldataExceededLimit(limit, actual) => {
+ write!(f, "calldata exceeded limit of {limit}: {actual}")
+ }
}
}
}
@@ -89,8 +93,13 @@ impl std::fmt::Debug for EthApiError {
/// Constructs a JSON-RPC error object, consisting of `code` and `message`.
impl From for ErrorObject<'static> {
fn from(value: EthApiError) -> Self {
- let msg = format!("{value:?}");
- ErrorObject::owned(EthRpcErrorCode::from(value) as i32, msg, None::<()>)
+ let msg = format!("{value}");
+ let code = EthRpcErrorCode::from(&value);
+ let data = match value {
+ EthApiError::Execution(ExecutionError::Evm(EvmError::Other(ref b))) => Some(b),
+ _ => None,
+ };
+ ErrorObject::owned(code as i32, msg, data)
}
}
@@ -108,19 +117,6 @@ pub enum KakarotError {
/// Error related to the database deserialization.
#[error(transparent)]
DatabaseDeserialization(#[from] mongodb::bson::de::Error),
- /// Error related to execution.
- #[error(transparent)]
- Execution(#[from] ExecutionError),
-}
-
-impl From for KakarotError {
- fn from(error: cainome::cairo_serde::Error) -> Self {
- let error = error.to_string();
- if error.contains("RunResources has no remaining steps.") {
- return ExecutionError::from(CairoError::VmOutOfResources).into();
- }
- ExecutionError::Other(error).into()
- }
}
impl From for EthApiError {
@@ -129,28 +125,46 @@ impl From for EthApiError {
}
}
-impl From for EthRpcErrorCode {
- fn from(value: KakarotError) -> Self {
- match value {
- KakarotError::Execution(_) => Self::ExecutionError,
- _ => Self::InternalError,
- }
- }
-}
-
/// Error related to execution errors, by the EVM or Cairo vm.
#[derive(Debug, Error)]
pub enum ExecutionError {
/// Error related to the EVM execution failures.
- #[error(transparent)]
Evm(#[from] EvmError),
/// Error related to the Cairo vm execution failures.
- #[error(transparent)]
CairoVm(#[from] CairoError),
- #[error("{0}")]
+ /// Other execution error.
Other(String),
}
+impl From for ExecutionError {
+ fn from(error: cainome::cairo_serde::Error) -> Self {
+ let error = error.to_string();
+ if error.contains("RunResources has no remaining steps.") {
+ return Self::CairoVm(CairoError::VmOutOfResources);
+ }
+ Self::Other(error)
+ }
+}
+
+impl std::fmt::Display for ExecutionError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str("execution reverted")?;
+ match self {
+ Self::Evm(err) => match err {
+ EvmError::Other(b) => {
+ if let Some(reason) = decode_revert_reason(b.as_ref()) {
+ write!(f, ": {reason}")?;
+ }
+ Ok(())
+ }
+ _ => write!(f, ": {err}"),
+ },
+ Self::CairoVm(err) => write!(f, ": {err}"),
+ Self::Other(err) => write!(f, ": {err}"),
+ }
+ }
+}
+
/// Error related to the Cairo vm execution failures.
#[derive(Debug, Error)]
pub enum CairoError {
@@ -162,17 +176,17 @@ pub enum CairoError {
#[derive(Debug, Error)]
pub enum EvmError {
#[error("validation failed")]
- ValidationError,
+ Validation,
#[error("state modification error")]
- StateModificationError,
+ StateModification,
#[error("unknown opcode")]
UnknownOpcode,
#[error("invalid jump dest")]
InvalidJumpDest,
- #[error("invalid caller")]
+ #[error("caller is not a Kakarot EOA")]
NotKakarotEoaCaller,
- #[error("view function error")]
- ViewFunctionError,
+ #[error("function limited to view call")]
+ ViewFunction,
#[error("stack overflow")]
StackOverflow,
#[error("stack underflow")]
@@ -187,24 +201,18 @@ pub enum EvmError {
NotImplementedPrecompile(String),
#[error("invalid cairo selector")]
InvalidCairoSelector,
- #[error("precompile input error")]
- PrecompileInputError,
+ #[error("precompile wrong input length")]
+ PrecompileInputLength,
#[error("precompile flag error")]
- PrecompileFlagError,
- #[error("balance error")]
- BalanceError,
+ PrecompileFlag,
+ #[error("transfer amount exceeds balance")]
+ Balance,
#[error("address collision")]
AddressCollision,
#[error("out of gas")]
OutOfGas,
#[error("{0}")]
- Other(String),
-}
-
-impl From for KakarotError {
- fn from(value: EvmError) -> Self {
- Self::Execution(ExecutionError::Evm(value))
- }
+ Other(Bytes),
}
impl From> for EvmError {
@@ -212,18 +220,18 @@ impl From> for EvmError {
let bytes = value.into_iter().filter_map(|x| u8::try_from(x).ok()).collect::>();
let maybe_revert_reason = String::from_utf8(bytes.clone());
if maybe_revert_reason.is_err() {
- return Self::Other(decode_err(&bytes));
+ return Self::Other(bytes.into());
}
let revert_reason = maybe_revert_reason.unwrap(); // safe unwrap
let trimmed = revert_reason.trim_start_matches("Kakarot: ").trim_start_matches("Precompile: ");
match trimmed {
- "eth validation failed" => Self::ValidationError,
- "StateModificationError" => Self::StateModificationError,
+ "eth validation failed" => Self::Validation,
+ "StateModificationError" => Self::StateModification,
"UnknownOpcode" => Self::UnknownOpcode,
"invalidJumpDestError" => Self::InvalidJumpDest,
"caller contract is not a Kakarot account" => Self::NotKakarotEoaCaller,
- "entrypoint should only be called in view mode" => Self::ViewFunctionError,
+ "entrypoint should only be called in view mode" => Self::ViewFunction,
"StackOverflow" => Self::StackOverflow,
"StackUnderflow" => Self::StackUnderflow,
"OutOfBoundsRead" => Self::OutOfBoundsRead,
@@ -235,23 +243,16 @@ impl From> for EvmError {
Self::NotImplementedPrecompile(s.trim_start_matches("NotImplementedPrecompile ").to_string())
}
"invalidCairoSelector" => Self::InvalidCairoSelector,
- "wrong input_len" => Self::PrecompileInputError,
- "flag error" => Self::PrecompileFlagError,
- "transfer amount exceeds balance" => Self::BalanceError,
+ "wrong input_length" => Self::PrecompileInputLength,
+ "flag error" => Self::PrecompileFlag,
+ "transfer amount exceeds balance" => Self::Balance,
"addressCollision" => Self::AddressCollision,
s if s.contains("outOfGas") => Self::OutOfGas,
- _ => Self::Other(decode_err(&bytes)),
+ _ => Self::Other(bytes.into()),
}
}
}
-fn decode_err(bytes: &[u8]) -> String {
- // Skip the first 4 bytes which is the function selector
- let msg = &bytes.get(4..);
- let maybe_decoded_msg = msg.and_then(|msg| alloy_sol_types::sol_data::String::abi_decode(msg, true).ok());
- maybe_decoded_msg.map_or_else(|| format!("{}", bytes.iter().collect::()), |s| s)
-}
-
/// Error related to a transaction.
#[derive(Debug, Error)]
pub enum TransactionError {
@@ -273,8 +274,8 @@ pub enum TransactionError {
Tracing(Box),
}
-impl From for EthRpcErrorCode {
- fn from(error: TransactionError) -> Self {
+impl From<&TransactionError> for EthRpcErrorCode {
+ fn from(error: &TransactionError) -> Self {
match error {
TransactionError::InvalidChainId | TransactionError::InvalidTransactionType => Self::InvalidInput,
TransactionError::GasOverflow => Self::TransactionRejected,
@@ -288,10 +289,10 @@ impl From for EthRpcErrorCode {
pub enum SignatureError {
/// Thrown when signer recovery fails.
#[error("could not recover signer")]
- RecoveryError,
+ Recovery,
/// Thrown when signing fails.
- #[error("failed to sign")]
- SignError,
+ #[error("failed to sign transaction")]
+ SigningFailure,
/// Thrown when signature is missing.
#[error("missing signature")]
MissingSignature,
@@ -305,16 +306,16 @@ pub enum SignatureError {
pub enum EthereumDataFormatError {
/// Error related to conversion in header.
#[error("header conversion error")]
- HeaderConversionError,
+ HeaderConversion,
/// Error related to conversion in receipt.
#[error("header conversion error")]
- ReceiptConversionError,
+ ReceiptConversion,
/// Error related to conversion in transaction.
#[error("transaction conversion error")]
- TransactionConversionError,
+ TransactionConversion,
/// Error related to starknet to eth conversion or vice versa.
#[error("primitive conversion error")]
- PrimitiveError,
+ Primitive,
}
#[cfg(test)]
@@ -339,41 +340,81 @@ mod tests {
}
#[test]
- fn test_decode_evm_error() {
+ fn test_decode_revert_message() {
// Given
- let bytes: Vec<_> = vec![
+ let b: Vec<_> = vec![
0x08u8, 0xc3, 0x79, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x46, 0x61, 0x75,
0x63, 0x65, 0x74, 0x3a, 0x20, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x20, 0x74, 0x6f, 0x6f, 0x20, 0x73, 0x6f, 0x6f,
0x6e, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ];
+ let bytes = b.clone().into_iter().map(FieldElement::from).collect::>();
+
+ // When
+ let evm_err: EvmError = bytes.into();
+ let json_rpsee_error: ErrorObject<'static> = EthApiError::Execution(ExecutionError::Evm(evm_err)).into();
+
+ // Then
+ assert_eq!(json_rpsee_error.message(), "execution reverted: revert: Faucet: Claim too soon.");
+ assert_eq!(json_rpsee_error.code(), 3);
+ assert_eq!(format!("{}", json_rpsee_error.data().unwrap()), format!("\"{}\"", Bytes::from(b)));
+ }
+
+ #[test]
+ fn test_decode_undecodable_message() {
+ // Given
+ let b = vec![
+ 0x6cu8, 0xa7, 0xb8, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71,
+ 0x52, 0xe0, 0x85, 0x5b, 0xab, 0x82, 0xb8, 0xe1, 0x0b, 0x86, 0x92, 0xe5, 0x84, 0xad, 0x03, 0x4b, 0xd2, 0x29,
+ 0x12,
+ ];
+ let bytes = b.clone().into_iter().map(FieldElement::from).collect::>();
+
+ // When
+ let evm_err: EvmError = bytes.into();
+ let json_rpsee_error: ErrorObject<'static> = EthApiError::Execution(ExecutionError::Evm(evm_err)).into();
+
+ // Then
+ assert_eq!(json_rpsee_error.message(), "execution reverted");
+ assert_eq!(json_rpsee_error.code(), 3);
+ assert_eq!(format!("{}", json_rpsee_error.data().unwrap()), format!("\"{}\"", Bytes::from(b)));
+ }
+
+ #[test]
+ fn test_decode_kakarot_evm_error() {
+ // Given
+ let bytes = vec![
+ 0x4bu8, 0x61, 0x6b, 0x61, 0x72, 0x6f, 0x74, 0x3a, 0x20, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69,
+ 0x6e, 0x74, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x62, 0x65, 0x20,
+ 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x69, 0x65, 0x77, 0x20, 0x6d, 0x6f, 0x64,
+ 0x65,
]
.into_iter()
.map(FieldElement::from)
- .collect();
+ .collect::>();
// When
let evm_err: EvmError = bytes.into();
+ let json_rpsee_error: ErrorObject<'static> = EthApiError::Execution(ExecutionError::Evm(evm_err)).into();
// Then
- if let EvmError::Other(err) = evm_err {
- assert_eq!(err, "Faucet: Claim too soon.");
- } else {
- panic!("Expected EvmError::Other, got {evm_err:?}");
- }
+ assert_eq!(json_rpsee_error.message(), "execution reverted: function limited to view call");
+ assert_eq!(json_rpsee_error.code(), 3);
+ assert!(json_rpsee_error.data().is_none());
}
#[test]
fn test_display_execution_error() {
// Given
- let err = EthApiError::Kakarot(KakarotError::Execution(ExecutionError::Evm(EvmError::BalanceError)));
+ let err = EthApiError::Execution(ExecutionError::Evm(EvmError::Balance));
// When
- let display = format!("{err:?}");
+ let display = format!("{err}");
// Then
- assert_eq!(display, "execution reverted: Evm(BalanceError)");
+ assert_eq!(display, "execution reverted: transfer amount exceeds balance");
}
#[test]
@@ -418,10 +459,10 @@ mod tests {
));
// When
- let eth_err: KakarotError = err.into();
- let display = format!("{eth_err:?}");
+ let eth_err: ExecutionError = err.into();
+ let display = format!("{eth_err}");
// Then
- assert_eq!(display, "Execution(CairoVm(VmOutOfResources))");
+ assert_eq!(display, "execution reverted: cairo vm out of resources");
}
}
diff --git a/src/eth_provider/provider.rs b/src/eth_provider/provider.rs
index 10c4bb834..2a01f7587 100644
--- a/src/eth_provider/provider.rs
+++ b/src/eth_provider/provider.rs
@@ -1,22 +1,21 @@
use crate::eth_provider::database::filter::format_hex;
use crate::eth_provider::database::FindOpts;
-use crate::eth_provider::database::{ethereum::EthereumDatabase, filter};
-use alloy_rlp::{Decodable, Encodable};
+use crate::eth_provider::database::{ethereum::EthereumTransactionStore, filter};
+use alloy_rlp::Decodable;
use async_trait::async_trait;
use auto_impl::auto_impl;
use cainome::cairo_serde::CairoArrayLegacy;
use eyre::{eyre, Result};
use itertools::Itertools;
use mongodb::bson::doc;
-use reth_primitives::constants::EMPTY_ROOT_HASH;
use reth_primitives::{
Address, BlockId, BlockNumberOrTag, Bytes, TransactionSigned, TransactionSignedEcRecovered, TxKind, B256, U256, U64,
};
use reth_rpc_types::serde_helpers::JsonStorageKey;
use reth_rpc_types::txpool::TxpoolContent;
use reth_rpc_types::{
- Block, BlockHashOrNumber, BlockTransactions, FeeHistory, Filter, FilterChanges, Header, Index, RichBlock,
- Transaction, TransactionReceipt, TransactionRequest,
+ BlockHashOrNumber, FeeHistory, Filter, FilterChanges, Header, Index, RichBlock, Transaction, TransactionReceipt,
+ TransactionRequest,
};
use reth_rpc_types::{SyncInfo, SyncStatus};
use reth_rpc_types_compat::transaction::from_recovered;
@@ -27,13 +26,16 @@ use starknet_crypto::FieldElement;
use super::constant::{
BLOCK_NUMBER_HEX_STRING_LEN, CALL_REQUEST_GAS_LIMIT, HASH_HEX_STRING_LEN, MAX_LOGS, TRANSACTION_MAX_RETRIES,
};
+use super::database::ethereum::EthereumBlockStore;
use super::database::filter::EthDatabaseFilterBuilder;
use super::database::types::{
header::StoredHeader, log::StoredLog, receipt::StoredTransactionReceipt, transaction::StoredPendingTransaction,
- transaction::StoredTransaction, transaction::StoredTransactionHash,
+ transaction::StoredTransaction,
};
use super::database::{CollectionName, Database};
-use super::error::{EthApiError, EthereumDataFormatError, EvmError, KakarotError, SignatureError, TransactionError};
+use super::error::{
+ EthApiError, EthereumDataFormatError, EvmError, ExecutionError, KakarotError, SignatureError, TransactionError,
+};
use super::starknet::kakarot_core::{
self,
account_contract::AccountContractReader,
@@ -162,12 +164,8 @@ where
SP: starknet::providers::Provider + Send + Sync,
{
async fn header(&self, block_id: &BlockId) -> EthProviderResult> {
- let block = match block_id {
- BlockId::Hash(hash) => BlockHashOrNumber::Hash((*hash).into()),
- BlockId::Number(number_or_tag) => self.tag_into_block_number(*number_or_tag).await?.to::().into(),
- };
-
- Ok(self.header(block).await?.map(|h| h.header))
+ let block_hash_or_number = self.block_id_into_block_number_or_hash(*block_id).await?;
+ Ok(self.database.header(block_hash_or_number).await?)
}
async fn block_number(&self) -> EthProviderResult {
@@ -201,7 +199,7 @@ where
}
async fn block_by_hash(&self, hash: B256, full: bool) -> EthProviderResult> {
- Ok(self.block(hash.into(), full).await?)
+ Ok(self.database.block(hash.into(), full).await?)
}
async fn block_by_number(
@@ -210,16 +208,11 @@ where
full: bool,
) -> EthProviderResult > {
let block_number = self.tag_into_block_number(number_or_tag).await?;
- Ok(self.block(block_number.into(), full).await?)
+ Ok(self.database.block(block_number.into(), full).await?)
}
async fn block_transaction_count_by_hash(&self, hash: B256) -> EthProviderResult > {
- Ok(if self.block_exists(hash.into()).await? {
- let filter = EthDatabaseFilterBuilder::::default().with_block_hash(&hash).build();
- Some(U256::from(self.database.count::(filter).await?))
- } else {
- None
- })
+ self.database.transaction_count(hash.into()).await
}
async fn block_transaction_count_by_number(
@@ -227,15 +220,7 @@ where
number_or_tag: BlockNumberOrTag,
) -> EthProviderResult> {
let block_number = self.tag_into_block_number(number_or_tag).await?;
- let block_exists = self.block_exists(block_number.into()).await?;
- if !block_exists {
- return Ok(None);
- }
-
- let filter =
- EthDatabaseFilterBuilder::::default().with_block_number(block_number.to()).build();
- let count = self.database.count::(filter).await?;
- Ok(Some(U256::from(count)))
+ self.database.transaction_count(block_number.into()).await
}
async fn transaction_by_hash(&self, hash: B256) -> EthProviderResult> {
@@ -289,7 +274,7 @@ where
number_or_tag: BlockNumberOrTag,
index: Index,
) -> EthProviderResult > {
- let block_number = self.tag_into_block_number(number_or_tag).await?.to();
+ let block_number = self.tag_into_block_number(number_or_tag).await?;
let filter = EthDatabaseFilterBuilder::::default()
.with_block_number(block_number)
.with_tx_index(&index)
@@ -318,8 +303,8 @@ where
if contract_not_found(&res) {
return Ok(Default::default());
}
- // Otherwise, extract the balance from the result, converting any errors to KakarotError
- let balance = res.map_err(KakarotError::from)?.balance;
+ // Otherwise, extract the balance from the result, converting any errors to ExecutionError
+ let balance = res.map_err(ExecutionError::from)?.balance;
// Convert the low and high parts of the balance to U256
let low: U256 = into_via_wrapper!(balance.low);
@@ -349,7 +334,7 @@ where
return Ok(U256::ZERO.into());
}
- let storage = maybe_storage.map_err(KakarotError::from)?.value;
+ let storage = maybe_storage.map_err(ExecutionError::from)?.value;
let low: U256 = into_via_wrapper!(storage.low);
let high: U256 = into_via_wrapper!(storage.high);
let storage: U256 = low + (high << 128);
@@ -367,7 +352,7 @@ where
if contract_not_found(&maybe_nonce) || entrypoint_not_found(&maybe_nonce) {
return Ok(U256::ZERO);
}
- let nonce = maybe_nonce.map_err(KakarotError::from)?.nonce;
+ let nonce = maybe_nonce.map_err(ExecutionError::from)?.nonce;
// Get the protocol nonce as well, in edge cases where the protocol nonce is higher than the account nonce.
// This can happen when an underlying Starknet transaction reverts => Account storage changes are reverted,
@@ -389,7 +374,7 @@ where
return Ok(Bytes::default());
}
- let bytecode = bytecode.map_err(KakarotError::from)?.bytecode.0;
+ let bytecode = bytecode.map_err(ExecutionError::from)?.bytecode.0;
Ok(Bytes::from(bytecode.into_iter().filter_map(|x| x.try_into().ok()).collect::>()))
}
@@ -470,7 +455,6 @@ where
}
let end_block = self.tag_into_block_number(newest_block).await?;
- let end_block = end_block.to::();
let end_block_plus_one = end_block.saturating_add(1);
// 0 <= start_block <= end_block
@@ -516,7 +500,7 @@ where
async fn send_raw_transaction(&self, transaction: Bytes) -> EthProviderResult {
// Decode the transaction data
let transaction_signed = TransactionSigned::decode(&mut transaction.0.as_ref())
- .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::TransactionConversionError))?;
+ .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::TransactionConversion))?;
let chain_id: u64 =
self.chain_id().await?.unwrap_or_default().try_into().map_err(|_| TransactionError::InvalidChainId)?;
@@ -525,7 +509,7 @@ where
validate_transaction(&transaction_signed, chain_id)?;
// Recover the signer from the transaction
- let signer = transaction_signed.recover_signer().ok_or(SignatureError::RecoveryError)?;
+ let signer = transaction_signed.recover_signer().ok_or(SignatureError::Recovery)?;
// Get the number of retries for the transaction
let retries = self.database.pending_transaction_retries(&transaction_signed.hash).await?;
@@ -535,13 +519,8 @@ where
from_recovered(TransactionSignedEcRecovered::from_signed_transaction(transaction_signed.clone(), signer));
self.database.upsert_pending_transaction(transaction, retries).await?;
- // The max fee is always set to 0. This means that no fee is perceived by the
- // Starknet sequencer, which is the intended behavior has fee perception is
- // handled by the Kakarot execution layer through EVM gas accounting.
- let max_fee = 0;
-
// Convert the Ethereum transaction to a Starknet transaction
- let starknet_transaction = to_starknet_transaction(&transaction_signed, signer, max_fee, retries)?;
+ let starknet_transaction = to_starknet_transaction(&transaction_signed, signer, retries)?;
// Deploy EVM transaction signer if Hive feature is enabled
#[cfg(feature = "hive")]
@@ -567,25 +546,25 @@ where
async fn gas_price(&self) -> EthProviderResult {
let kakarot_contract = KakarotCoreReader::new(*KAKAROT_ADDRESS, &self.starknet_provider);
- let gas_price = kakarot_contract.get_base_fee().call().await.map_err(KakarotError::from)?.base_fee;
+ let gas_price = kakarot_contract.get_base_fee().call().await.map_err(ExecutionError::from)?.base_fee;
Ok(into_via_wrapper!(gas_price))
}
async fn block_receipts(&self, block_id: Option) -> EthProviderResult>> {
match block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)) {
- BlockId::Number(maybe_number) => {
- let block_number = self.tag_into_block_number(maybe_number).await?;
- if !self.block_exists(block_number.into()).await? {
+ BlockId::Number(number_or_tag) => {
+ let block_number = self.tag_into_block_number(number_or_tag).await?;
+ if !self.database.block_exists(block_number.into()).await? {
return Ok(None);
}
let filter =
- EthDatabaseFilterBuilder::::default().with_block_number(block_number.to()).build();
+ EthDatabaseFilterBuilder::::default().with_block_number(block_number).build();
let tx: Vec = self.database.get(filter, None).await?;
Ok(Some(tx.into_iter().map(Into::into).collect()))
}
BlockId::Hash(hash) => {
- if !self.block_exists(hash.block_hash.into()).await? {
+ if !self.database.block_exists(hash.block_hash.into()).await? {
return Ok(None);
}
let filter =
@@ -599,18 +578,14 @@ where
&self,
block_id: Option,
) -> EthProviderResult>> {
- let block_id = match block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)) {
- BlockId::Number(maybe_number) => self.tag_into_block_number(maybe_number).await?.to::().into(),
- BlockId::Hash(hash) => hash.block_hash.into(),
- };
- if !self.block_exists(block_id).await? {
+ let block_hash_or_number = self
+ .block_id_into_block_number_or_hash(block_id.unwrap_or(BlockId::Number(BlockNumberOrTag::Latest)))
+ .await?;
+ if !self.database.block_exists(block_hash_or_number).await? {
return Ok(None);
}
- match self.transactions(block_id, true).await? {
- BlockTransactions::Full(transactions) => Ok(Some(transactions)),
- _ => Err(TransactionError::ExpectedFullTransactions.into()),
- }
+ Ok(Some(self.database.transactions(block_hash_or_number).await?))
}
async fn txpool_transactions(&self) -> EthProviderResult> {
@@ -720,11 +695,11 @@ where
.block_id(starknet_block_id)
.call()
.await
- .map_err(KakarotError::from)?;
+ .map_err(ExecutionError::from)?;
let return_data = call_output.return_data;
if call_output.success == FieldElement::ZERO {
- return Err(KakarotError::from(EvmError::from(return_data.0)).into());
+ return Err(ExecutionError::from(EvmError::from(return_data.0)).into());
}
Ok(return_data)
}
@@ -755,97 +730,16 @@ where
.block_id(starknet_block_id)
.call()
.await
- .map_err(KakarotError::from)?;
+ .map_err(ExecutionError::from)?;
let return_data = estimate_gas_output.return_data;
if estimate_gas_output.success == FieldElement::ZERO {
- return Err(KakarotError::from(EvmError::from(return_data.0)).into());
+ return Err(ExecutionError::from(EvmError::from(return_data.0)).into());
}
let required_gas = estimate_gas_output.required_gas.try_into().map_err(|_| TransactionError::GasOverflow)?;
Ok(required_gas)
}
- /// Check if a block exists in the database.
- async fn block_exists(&self, block_id: BlockHashOrNumber) -> EthProviderResult {
- Ok(self.header(block_id).await?.is_some())
- }
-
- /// Get a header from the database based on the filter.
- async fn header(&self, id: BlockHashOrNumber) -> EthProviderResult> {
- let builder = EthDatabaseFilterBuilder::::default();
- let filter = match id {
- BlockHashOrNumber::Hash(hash) => builder.with_block_hash(&hash),
- BlockHashOrNumber::Number(number) => builder.with_block_number(number),
- }
- .build();
- self.database
- .get_one(filter, None)
- .await
- .inspect_err(|err| tracing::error!("internal error: {:?}", err))
- .map_err(|_| EthApiError::UnknownBlock(id))
- }
-
- /// Return the transactions given a block id.
- pub(crate) async fn transactions(
- &self,
- block_id: BlockHashOrNumber,
- full: bool,
- ) -> EthProviderResult {
- let builder = EthDatabaseFilterBuilder::::default();
- let filter = match block_id {
- BlockHashOrNumber::Hash(hash) => builder.with_block_hash(&hash),
- BlockHashOrNumber::Number(number) => builder.with_block_number(number),
- }
- .build();
- let block_transactions = if full {
- BlockTransactions::Full(self.database.get_and_map_to::<_, StoredTransaction>(filter, None).await?)
- } else {
- BlockTransactions::Hashes(
- self.database
- .get_and_map_to::<_, StoredTransactionHash>(
- filter,
- Some(FindOpts::default().with_projection(doc! {"tx.hash": 1})),
- )
- .await?,
- )
- };
-
- Ok(block_transactions)
- }
-
- /// Get a block from the database based on a block hash or number.
- /// If full is true, the block will contain the full transactions, otherwise just the hashes
- async fn block(&self, block_id: BlockHashOrNumber, full: bool) -> EthProviderResult> {
- let header = match self.header(block_id).await? {
- Some(h) => h.header,
- None => return Ok(None),
- };
-
- // The withdrawals are not supported, hence the withdrawals_root should always be empty.
- if let Some(withdrawals_root) = header.withdrawals_root {
- if withdrawals_root != EMPTY_ROOT_HASH {
- return Err(EthApiError::Unsupported("withdrawals"));
- }
- }
-
- // This is how reth computes the block size.
- // `https://github.com/paradigmxyz/reth/blob/v0.2.0-beta.5/crates/rpc/rpc-types-compat/src/block.rs#L66`
- let size = reth_primitives::Header::try_from(header.clone())
- .map_err(|_| EthereumDataFormatError::PrimitiveError)?
- .length();
- Ok(Some(
- Block {
- header,
- transactions: self.transactions(block_id, full).await?,
- uncles: Default::default(),
- size: Some(U256::from(size)),
- withdrawals: Some(Default::default()),
- other: Default::default(),
- }
- .into(),
- ))
- }
-
/// Convert the given block id into a Starknet block id
pub async fn to_starknet_block_id(
&self,
@@ -863,10 +757,13 @@ where
// 3. The block number is not found, then we return an error
match number_or_tag {
BlockNumberOrTag::Number(number) => {
- let header =
- self.header(number.into()).await?.ok_or(EthApiError::UnknownBlockNumber(Some(number)))?;
+ let header = self
+ .database
+ .header(number.into())
+ .await?
+ .ok_or(EthApiError::UnknownBlockNumber(Some(number)))?;
// If the block hash is zero, then the block corresponds to a Starknet pending block
- if header.header.hash.ok_or(EthApiError::UnknownBlock(number.into()))?.is_zero() {
+ if header.hash.ok_or(EthApiError::UnknownBlock(number.into()))?.is_zero() {
Ok(starknet::core::types::BlockId::Tag(starknet::core::types::BlockTag::Pending))
} else {
Ok(starknet::core::types::BlockId::Number(number))
@@ -880,18 +777,26 @@ where
}
/// Converts the given [`BlockNumberOrTag`] into a block number.
- async fn tag_into_block_number(&self, tag: BlockNumberOrTag) -> EthProviderResult {
+ async fn tag_into_block_number(&self, tag: BlockNumberOrTag) -> EthProviderResult {
match tag {
// Converts the tag representing the earliest block into block number 0.
- BlockNumberOrTag::Earliest => Ok(U64::ZERO),
+ BlockNumberOrTag::Earliest => Ok(0),
// Converts the tag containing a specific block number into a `U64`.
- BlockNumberOrTag::Number(number) => Ok(U64::from(number)),
+ BlockNumberOrTag::Number(number) => Ok(number),
// Returns `self.block_number()` which is the block number of the latest finalized block.
BlockNumberOrTag::Latest | BlockNumberOrTag::Finalized | BlockNumberOrTag::Safe => {
- self.block_number().await
+ self.block_number().await.map(|x| x.to())
}
// Adds 1 to the block number of the latest finalized block.
- BlockNumberOrTag::Pending => Ok(self.block_number().await?.saturating_add(U64::from(1))),
+ BlockNumberOrTag::Pending => Ok(self.block_number().await?.to::().saturating_add(1)),
+ }
+ }
+
+ /// Converts the given [`BlockId`] into a [`BlockHashOrNumber`].
+ async fn block_id_into_block_number_or_hash(&self, block_id: BlockId) -> EthProviderResult {
+ match block_id {
+ BlockId::Hash(hash) => Ok(BlockHashOrNumber::Hash(hash.into())),
+ BlockId::Number(number_or_tag) => Ok(self.tag_into_block_number(number_or_tag).await?.into()),
}
}
}
@@ -934,10 +839,10 @@ where
.nonce(current_nonce)
.max_fee(u64::MAX.into())
.prepared()
- .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::TransactionConversionError))?
+ .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::TransactionConversion))?
.get_invoke_request(false)
.await
- .map_err(|_| SignatureError::SignError)?;
+ .map_err(|_| SignatureError::SigningFailure)?;
self.starknet_provider.add_invoke_transaction(tx).await.map_err(KakarotError::from)?;
*nonce += 1u8.into();
diff --git a/src/eth_provider/starknet/kakarot_core.rs b/src/eth_provider/starknet/kakarot_core.rs
index bd1e4274a..2703fea4c 100644
--- a/src/eth_provider/starknet/kakarot_core.rs
+++ b/src/eth_provider/starknet/kakarot_core.rs
@@ -93,7 +93,6 @@ pub fn starknet_address(address: Address) -> FieldElement {
pub fn to_starknet_transaction(
transaction: &TransactionSigned,
signer: Address,
- max_fee: u64,
retries: u8,
) -> EthProviderResult {
let sender_address = starknet_address(signer);
@@ -104,8 +103,11 @@ pub fn to_starknet_transaction(
// Transform the transaction's data to Starknet calldata
let calldata = transaction_data_to_starknet_calldata(transaction, retries)?;
+ // The max fee is always set to 0. This means that no fee is perceived by the
+ // Starknet sequencer, which is the intended behavior as fee perception is
+ // handled by the Kakarot execution layer through EVM gas accounting.
Ok(BroadcastedInvokeTransaction::V1(BroadcastedInvokeTransactionV1 {
- max_fee: max_fee.into(),
+ max_fee: FieldElement::ZERO,
signature,
nonce: transaction.nonce().into(),
sender_address,
@@ -131,7 +133,6 @@ mod tests {
&transaction,
Address::from_str("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266").unwrap(),
0,
- 0,
)
.unwrap()
{
@@ -194,7 +195,7 @@ mod tests {
}
#[test]
- #[should_panic(expected = "calldata exceeded limit of 22500: 30008")]
+ #[should_panic(expected = "CalldataExceededLimit(22500, 30008)")]
fn to_starknet_transaction_too_large_calldata_test() {
// Test that an example create transaction from goerli decodes properly
let tx_bytes = hex!("b901f202f901ee05228459682f008459682f11830209bf8080b90195608060405234801561001057600080fd5b50610175806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80630c49c36c14610030575b600080fd5b61003861004e565b604051610045919061011d565b60405180910390f35b60606020600052600f6020527f68656c6c6f2073746174656d696e64000000000000000000000000000000000060405260406000f35b600081519050919050565b600082825260208201905092915050565b60005b838110156100be5780820151818401526020810190506100a3565b838111156100cd576000848401525b50505050565b6000601f19601f8301169050919050565b60006100ef82610084565b6100f9818561008f565b93506101098185602086016100a0565b610112816100d3565b840191505092915050565b6000602082019050818103600083015261013781846100e4565b90509291505056fea264697066735822122051449585839a4ea5ac23cae4552ef8a96b64ff59d0668f76bfac3796b2bdbb3664736f6c63430008090033c080a0136ebffaa8fc8b9fda9124de9ccb0b1f64e90fbd44251b4c4ac2501e60b104f9a07eb2999eec6d185ef57e91ed099afb0a926c5b536f0155dd67e537c7476e1471");
@@ -212,6 +213,6 @@ mod tests {
transaction.transaction.set_input(vec![0; 30000 * 31].into());
// Attempt to convert the transaction into a Starknet transaction
- to_starknet_transaction(&transaction, transaction.recover_signer().unwrap(), 100_000_000, 0).unwrap();
+ to_starknet_transaction(&transaction, transaction.recover_signer().unwrap(), 0).unwrap();
}
}
diff --git a/src/eth_rpc/servers/alchemy_rpc.rs b/src/eth_rpc/servers/alchemy_rpc.rs
index 0f1e33849..a3855989e 100644
--- a/src/eth_rpc/servers/alchemy_rpc.rs
+++ b/src/eth_rpc/servers/alchemy_rpc.rs
@@ -1,10 +1,11 @@
use futures::future::join_all;
-use jsonrpsee::core::{async_trait, RpcResult as Result};
+use jsonrpsee::core::{async_trait, RpcResult};
use reth_primitives::{Address, BlockId, BlockNumberOrTag};
use crate::eth_provider::contracts::erc20::EthereumErc20;
+use crate::eth_provider::error::EthApiError;
use crate::eth_rpc::api::alchemy_api::AlchemyApiServer;
-use crate::models::balance::FutureTokenBalance;
+use crate::models::balance::TokenBalanceFuture;
use crate::{eth_provider::provider::EthereumProvider, models::balance::TokenBalances};
/// The RPC module for the Ethereum protocol required by Kakarot.
@@ -22,16 +23,18 @@ impl AlchemyRpc {
#[async_trait]
impl AlchemyApiServer for AlchemyRpc {
#[tracing::instrument(skip_all, ret, fields(address = %address, token_addresses = ?token_addresses))]
- async fn token_balances(&self, address: Address, token_addresses: Vec
) -> Result {
+ async fn token_balances(&self, address: Address, token_addresses: Vec) -> RpcResult {
+ tracing::info!("Serving alchemy_getTokenBalances");
+
let block_id = BlockId::Number(BlockNumberOrTag::Latest);
let handles = token_addresses.into_iter().map(|token_addr| {
let token = EthereumErc20::new(token_addr, &self.eth_provider);
let balance = token.balance_of(address, block_id);
- FutureTokenBalance::new(Box::pin(balance), token_addr)
+ TokenBalanceFuture::new(Box::pin(balance), token_addr)
});
- let token_balances = join_all(handles).await;
+ let token_balances = join_all(handles).await.into_iter().collect::, EthApiError>>()?;
Ok(TokenBalances { address, token_balances })
}
diff --git a/src/eth_rpc/servers/debug_rpc.rs b/src/eth_rpc/servers/debug_rpc.rs
index 970f33bd7..b154e1897 100644
--- a/src/eth_rpc/servers/debug_rpc.rs
+++ b/src/eth_rpc/servers/debug_rpc.rs
@@ -29,6 +29,8 @@ impl DebugApiServer for DebugRpc
/// Returns an RLP-encoded header.
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
async fn raw_header(&self, block_id: BlockId) -> Result {
+ tracing::info!("Serving debug_getRawHeader");
+
let mut res = Vec::new();
if let Some(header) = self
.eth_provider
@@ -36,7 +38,7 @@ impl DebugApiServer for DebugRpc
.await?
.map(Header::try_from)
.transpose()
- .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::HeaderConversionError))?
+ .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::HeaderConversion))?
{
header.encode(&mut res);
}
@@ -47,6 +49,8 @@ impl DebugApiServer for DebugRpc
/// Returns an RLP-encoded block.
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
async fn raw_block(&self, block_id: BlockId) -> Result {
+ tracing::info!("Serving debug_getRawBlock");
+
let block = match block_id {
BlockId::Hash(hash) => self.eth_provider.block_by_hash(hash.into(), true).await?,
BlockId::Number(number) => self.eth_provider.block_by_number(number, true).await?,
@@ -54,7 +58,7 @@ impl DebugApiServer for DebugRpc
let mut raw_block = Vec::new();
if let Some(block) = block {
let block =
- Block::try_from(block.inner).map_err(|_| EthApiError::from(EthereumDataFormatError::PrimitiveError))?;
+ Block::try_from(block.inner).map_err(|_| EthApiError::from(EthereumDataFormatError::Primitive))?;
block.encode(&mut raw_block);
}
Ok(raw_block.into())
@@ -65,12 +69,13 @@ impl DebugApiServer for DebugRpc
/// If this is a pooled EIP-4844 transaction, the blob sidecar is included.
#[tracing::instrument(skip(self), err, fields(hash = ?hash))]
async fn raw_transaction(&self, hash: B256) -> Result> {
+ tracing::info!("Serving debug_getRawTransaction");
+
let transaction = self.eth_provider.transaction_by_hash(hash).await?;
if let Some(tx) = transaction {
let signature = tx.signature.ok_or_else(|| EthApiError::from(SignatureError::MissingSignature))?;
- let tx =
- tx.try_into().map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::PrimitiveError))?;
+ let tx = tx.try_into().map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::Primitive))?;
let bytes = TransactionSigned::from_transaction_and_signature(
tx,
reth_primitives::Signature {
@@ -89,13 +94,14 @@ impl DebugApiServer for DebugRpc
/// Returns an array of EIP-2718 binary-encoded transactions for the given [BlockId].
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
async fn raw_transactions(&self, block_id: BlockId) -> Result> {
+ tracing::info!("Serving debug_getRawTransactions");
+
let transactions = self.eth_provider.block_transactions(Some(block_id)).await?.unwrap_or_default();
let mut raw_transactions = Vec::with_capacity(transactions.len());
for t in transactions {
let signature = t.signature.ok_or_else(|| EthApiError::from(SignatureError::MissingSignature))?;
- let tx =
- t.try_into().map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::PrimitiveError))?;
+ let tx = t.try_into().map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::Primitive))?;
let bytes = TransactionSigned::from_transaction_and_signature(
tx,
reth_primitives::Signature {
@@ -114,6 +120,8 @@ impl DebugApiServer for DebugRpc
/// Returns an array of EIP-2718 binary-encoded receipts.
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
async fn raw_receipts(&self, block_id: BlockId) -> Result> {
+ tracing::info!("Serving debug_getRawReceipts");
+
let receipts = self.eth_provider.block_receipts(Some(block_id)).await?.unwrap_or_default();
// Initializes an empty vector to store the raw receipts
@@ -124,11 +132,11 @@ impl DebugApiServer for DebugRpc
// Converts the transaction type to a u8 and then tries to convert it into TxType
let tx_type = Into::::into(receipt.transaction_type())
.try_into()
- .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::ReceiptConversionError))?;
+ .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::ReceiptConversion))?;
// Tries to convert the cumulative gas used to u64
let cumulative_gas_used = TryInto::::try_into(receipt.inner.cumulative_gas_used())
- .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::ReceiptConversionError))?;
+ .map_err(|_| EthApiError::EthereumDataFormat(EthereumDataFormatError::ReceiptConversion))?;
// Creates a ReceiptWithBloom from the receipt data
raw_receipts.push(
@@ -161,6 +169,8 @@ impl DebugApiServer for DebugRpc
block_number: BlockNumberOrTag,
opts: Option,
) -> Result> {
+ tracing::info!("Serving debug_traceBlockByNumber");
+
let provider = Arc::new(&self.eth_provider);
let tracer = TracerBuilder::new(provider)
.await?
@@ -179,6 +189,8 @@ impl DebugApiServer for DebugRpc
block_hash: B256,
opts: Option,
) -> Result> {
+ tracing::info!("Serving debug_traceBlockByHash");
+
let tracer = TracerBuilder::new(Arc::new(&self.eth_provider))
.await?
.with_block_id(BlockId::Hash(block_hash.into()))
@@ -196,6 +208,8 @@ impl DebugApiServer for DebugRpc
transaction_hash: B256,
opts: Option,
) -> Result {
+ tracing::info!("Serving debug_traceTransaction");
+
let tracer = TracerBuilder::new(Arc::new(&self.eth_provider))
.await?
.with_transaction_hash(transaction_hash)
diff --git a/src/eth_rpc/servers/eth_rpc.rs b/src/eth_rpc/servers/eth_rpc.rs
index 5e2d36d94..c5dcc61f0 100644
--- a/src/eth_rpc/servers/eth_rpc.rs
+++ b/src/eth_rpc/servers/eth_rpc.rs
@@ -39,11 +39,13 @@ where
{
#[tracing::instrument(skip_all, ret, err)]
async fn block_number(&self) -> Result {
+ tracing::info!("Serving eth_blockNumber");
Ok(self.eth_provider.block_number().await?)
}
#[tracing::instrument(skip_all, ret, err)]
async fn syncing(&self) -> Result {
+ tracing::info!("Serving eth_syncing");
Ok(self.eth_provider.syncing().await?)
}
@@ -53,31 +55,37 @@ where
#[tracing::instrument(skip_all, ret, err)]
async fn accounts(&self) -> Result> {
+ tracing::info!("Serving eth_accounts");
Ok(Vec::new())
}
#[tracing::instrument(skip_all, ret, err)]
async fn chain_id(&self) -> Result> {
+ tracing::info!("Serving eth_chainId");
Ok(self.eth_provider.chain_id().await?)
}
#[tracing::instrument(skip(self), ret, err, fields(hash = %hash))]
async fn block_by_hash(&self, hash: B256, full: bool) -> Result > {
+ tracing::info!("Serving eth_getBlockByHash");
Ok(self.eth_provider.block_by_hash(hash, full).await?)
}
#[tracing::instrument(skip(self), err, fields(number = %number, full = full))]
async fn block_by_number(&self, number: BlockNumberOrTag, full: bool) -> Result > {
+ tracing::info!("Serving eth_getBlockByNumber");
Ok(self.eth_provider.block_by_number(number, full).await?)
}
#[tracing::instrument(skip(self), ret, err, fields(hash = %hash))]
async fn block_transaction_count_by_hash(&self, hash: B256) -> Result > {
+ tracing::info!("Serving eth_getBlockTransactionCountByHash");
Ok(self.eth_provider.block_transaction_count_by_hash(hash).await?)
}
#[tracing::instrument(skip(self), ret, err, fields(number = %number))]
async fn block_transaction_count_by_number(&self, number: BlockNumberOrTag) -> Result > {
+ tracing::info!("Serving eth_getBlockTransactionCountByNumber");
Ok(self.eth_provider.block_transaction_count_by_number(number).await?)
}
@@ -111,11 +119,13 @@ where
#[tracing::instrument(skip(self), ret, err, fields(hash = %hash))]
async fn transaction_by_hash(&self, hash: B256) -> Result > {
+ tracing::info!("Serving eth_getTransactionByHash");
Ok(self.eth_provider.transaction_by_hash(hash).await?)
}
#[tracing::instrument(skip(self), ret, err, fields(hash = %hash, index = ?index))]
async fn transaction_by_block_hash_and_index(&self, hash: B256, index: Index) -> Result > {
+ tracing::info!("Serving eth_getTransactionByBlockHashAndIndex");
Ok(self.eth_provider.transaction_by_block_hash_and_index(hash, index).await?)
}
@@ -125,41 +135,49 @@ where
number: BlockNumberOrTag,
index: Index,
) -> Result > {
+ tracing::info!("Serving eth_getTransactionByBlockNumberAndIndex");
Ok(self.eth_provider.transaction_by_block_number_and_index(number, index).await?)
}
#[tracing::instrument(skip(self), ret, err, fields(hash = %hash))]
async fn transaction_receipt(&self, hash: B256) -> Result > {
+ tracing::info!("Serving eth_getTransactionReceipt");
Ok(self.eth_provider.transaction_receipt(hash).await?)
}
#[tracing::instrument(skip(self), ret, err, fields(address = %address, block_id = ?block_id))]
async fn balance(&self, address: Address, block_id: Option) -> Result {
+ tracing::info!("Serving eth_getBalance");
Ok(self.eth_provider.balance(address, block_id).await?)
}
#[tracing::instrument(skip(self), ret, err, fields(address = %address, index = ?index, block_id = ?block_id))]
async fn storage_at(&self, address: Address, index: JsonStorageKey, block_id: Option) -> Result {
+ tracing::info!("Serving eth_getStorageAt");
Ok(self.eth_provider.storage_at(address, index, block_id).await?)
}
#[tracing::instrument(skip(self), ret, err, fields(address = %address, block_id = ?block_id))]
async fn transaction_count(&self, address: Address, block_id: Option) -> Result {
+ tracing::info!("Serving eth_getTransactionCount");
Ok(self.eth_provider.transaction_count(address, block_id).await?)
}
#[tracing::instrument(skip(self), err, fields(address = %address, block_id = ?block_id))]
async fn get_code(&self, address: Address, block_id: Option) -> Result {
+ tracing::info!("Serving eth_getCode");
Ok(self.eth_provider.get_code(address, block_id).await?)
}
#[tracing::instrument(skip(self), err, fields(filter = ?filter))]
async fn get_logs(&self, filter: Filter) -> Result {
+ tracing::info!("Serving eth_getLogs");
Ok(self.eth_provider.get_logs(filter).await?)
}
#[tracing::instrument(skip(self, request), err, fields(block_id = ?block_id, gas_limit = request.gas))]
async fn call(&self, request: TransactionRequest, block_id: Option) -> Result {
+ tracing::info!("Serving eth_call");
Ok(self.eth_provider.call(request, block_id).await?)
}
@@ -173,11 +191,13 @@ where
#[tracing::instrument(skip(self, request), err, fields(block_id = ?block_id, gas_limit = request.gas))]
async fn estimate_gas(&self, request: TransactionRequest, block_id: Option) -> Result {
+ tracing::info!("Serving eth_estimateGas");
Ok(self.eth_provider.estimate_gas(request, block_id).await?)
}
#[tracing::instrument(skip_all, ret, err)]
async fn gas_price(&self) -> Result {
+ tracing::info!("Serving eth_gasPrice");
Ok(self.eth_provider.gas_price().await?)
}
@@ -188,11 +208,13 @@ where
newest_block: BlockNumberOrTag,
reward_percentiles: Option>,
) -> Result {
+ tracing::info!("Serving eth_feeHistory");
Ok(self.eth_provider.fee_history(block_count, newest_block, reward_percentiles).await?)
}
#[tracing::instrument(skip_all, ret, err)]
async fn max_priority_fee_per_gas(&self) -> Result {
+ tracing::info!("Serving eth_maxPriorityFeePerGas");
Ok(U256::from(*MAX_PRIORITY_FEE_PER_GAS))
}
@@ -229,6 +251,7 @@ where
#[tracing::instrument(skip_all, ret, err)]
async fn send_raw_transaction(&self, bytes: Bytes) -> Result {
+ tracing::info!("Serving eth_sendRawTransaction");
Ok(self.eth_provider.send_raw_transaction(bytes).await?)
}
diff --git a/src/eth_rpc/servers/trace_rpc.rs b/src/eth_rpc/servers/trace_rpc.rs
index f38fa95e4..7747c952c 100644
--- a/src/eth_rpc/servers/trace_rpc.rs
+++ b/src/eth_rpc/servers/trace_rpc.rs
@@ -26,6 +26,7 @@ impl TraceApiServer for TraceRpc
#[allow(clippy::blocks_in_conditions)]
#[tracing::instrument(skip(self), err, fields(block_id = ?block_id))]
async fn trace_block(&self, block_id: BlockId) -> Result>> {
+ tracing::info!("Serving debug_traceBlock");
let tracer = TracerBuilder::new(Arc::new(&self.eth_provider))
.await?
.with_block_id(block_id)
diff --git a/src/lib.rs b/src/lib.rs
index 21e840077..320955de1 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,6 @@
+#![cfg_attr(not(any(test, feature = "testing")), warn(unused_crate_dependencies))]
+use tracing_subscriber as _;
+
pub mod config;
pub mod eth_provider;
pub mod eth_rpc;
diff --git a/src/main.rs b/src/main.rs
index 46d7e5795..3816a6515 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,7 @@ use std::sync::Arc;
use dotenvy::dotenv;
use eyre::Result;
-use kakarot_rpc::config::{JsonRpcClientBuilder, KakarotRpcConfig, Network, SequencerGatewayProviderBuilder};
+use kakarot_rpc::config::KakarotRpcConfig;
use kakarot_rpc::eth_provider::database::Database;
use kakarot_rpc::eth_provider::pending_pool::start_retry_service;
use kakarot_rpc::eth_provider::provider::EthDataProvider;
@@ -12,38 +12,25 @@ use kakarot_rpc::eth_rpc::rpc::KakarotRpcModuleBuilder;
use kakarot_rpc::eth_rpc::run_server;
use mongodb::options::{DatabaseOptions, ReadConcern, WriteConcern};
use starknet::providers::jsonrpc::HttpTransport;
-use starknet::providers::{JsonRpcClient, SequencerGatewayProvider};
+use starknet::providers::JsonRpcClient;
use tracing_subscriber::{filter, util::SubscriberInitExt};
-enum StarknetProvider {
- JsonRpcClient(JsonRpcClient),
- SequencerGatewayProvider(SequencerGatewayProvider),
-}
-
#[tokio::main]
async fn main() -> Result<()> {
- dotenv().ok();
// Environment variables are safe to use after this
+ dotenv().ok();
+
let filter = format!("kakarot_rpc={}", std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()));
let filter = filter::EnvFilter::new(filter);
tracing_subscriber::FmtSubscriber::builder().with_env_filter(filter).finish().try_init()?;
+ // Load the configuration
let starknet_config = KakarotRpcConfig::from_env()?;
-
let rpc_config = RPCConfig::from_env()?;
- let starknet_provider = match &starknet_config.network {
- Network::Madara | Network::Katana | Network::Sharingan => {
- StarknetProvider::JsonRpcClient(JsonRpcClientBuilder::with_http(&starknet_config).unwrap().build())
- }
- Network::JsonRpcProvider(url) => {
- StarknetProvider::JsonRpcClient(JsonRpcClientBuilder::new(HttpTransport::new(url.clone())).build())
- }
- _ => StarknetProvider::SequencerGatewayProvider(
- SequencerGatewayProviderBuilder::new(&starknet_config.network).build(),
- ),
- };
+ let starknet_provider = JsonRpcClient::new(HttpTransport::new(starknet_config.network_url));
+ // Setup the database
let db_client =
mongodb::Client::with_uri_str(var("MONGO_CONNECTION_STRING").expect("Missing MONGO_CONNECTION_STRING .env"))
.await?;
@@ -52,42 +39,16 @@ async fn main() -> Result<()> {
DatabaseOptions::builder().read_concern(ReadConcern::MAJORITY).write_concern(WriteConcern::MAJORITY).build(),
));
- // Get the deployer nonce and set the value in the DEPLOY_WALLET_NONCE
+ // Setup hive
#[cfg(feature = "hive")]
- {
- use kakarot_rpc::eth_provider::constant::{CHAIN_ID, DEPLOY_WALLET, DEPLOY_WALLET_NONCE};
- use starknet::accounts::ConnectedAccount;
- use starknet::providers::Provider as _;
- use starknet_crypto::FieldElement;
-
- let provider = JsonRpcClient::new(HttpTransport::new(
- starknet_config.network.provider_url().expect("Incorrect provider URL"),
- ));
- let chain_id = provider.chain_id().await?;
- let chain_id: u64 = (FieldElement::from(u64::MAX) & chain_id).try_into()?;
-
- CHAIN_ID.set(chain_id.into()).expect("Failed to set chain id");
-
- let deployer_nonce = DEPLOY_WALLET.get_nonce().await?;
- let mut nonce = DEPLOY_WALLET_NONCE.lock().await;
- *nonce = deployer_nonce;
- }
-
- let kakarot_rpc_module = match starknet_provider {
- StarknetProvider::JsonRpcClient(starknet_provider) => {
- let starknet_provider = Arc::new(starknet_provider);
- let eth_provider = EthDataProvider::new(db.clone(), starknet_provider).await?;
- tokio::spawn(start_retry_service(eth_provider.clone()));
- KakarotRpcModuleBuilder::new(eth_provider).rpc_module()?
- }
- StarknetProvider::SequencerGatewayProvider(starknet_provider) => {
- let starknet_provider = Arc::new(starknet_provider);
- let eth_provider = EthDataProvider::new(db.clone(), starknet_provider).await?;
- tokio::spawn(start_retry_service(eth_provider.clone()));
- KakarotRpcModuleBuilder::new(eth_provider).rpc_module()?
- }
- };
+ setup_hive(&starknet_provider).await?;
+
+ // Setup the retry service
+ let eth_provider = EthDataProvider::new(db, Arc::new(starknet_provider)).await?;
+ tokio::spawn(start_retry_service(eth_provider.clone()));
+ let kakarot_rpc_module = KakarotRpcModuleBuilder::new(eth_provider).rpc_module()?;
+ // Start the RPC server
let (socket_addr, server_handle) = run_server(kakarot_rpc_module, rpc_config).await?;
let url = format!("http://{socket_addr}");
@@ -98,3 +59,23 @@ async fn main() -> Result<()> {
Ok(())
}
+
+#[allow(clippy::significant_drop_tightening)]
+#[cfg(feature = "hive")]
+async fn setup_hive(starknet_provider: &JsonRpcClient) -> Result<()> {
+ use kakarot_rpc::eth_provider::constant::{CHAIN_ID, DEPLOY_WALLET, DEPLOY_WALLET_NONCE};
+ use starknet::accounts::ConnectedAccount;
+ use starknet::providers::Provider as _;
+ use starknet_crypto::FieldElement;
+
+ let chain_id = starknet_provider.chain_id().await?;
+ let chain_id: u64 = (FieldElement::from(u64::MAX) & chain_id).try_into()?;
+
+ CHAIN_ID.set(chain_id.into()).expect("Failed to set chain id");
+
+ let deployer_nonce = DEPLOY_WALLET.get_nonce().await?;
+ let mut nonce = DEPLOY_WALLET_NONCE.lock().await;
+ *nonce = deployer_nonce;
+
+ Ok(())
+}
diff --git a/src/models/balance.rs b/src/models/balance.rs
index 5aecfeadd..5f97552b8 100644
--- a/src/models/balance.rs
+++ b/src/models/balance.rs
@@ -1,17 +1,17 @@
+use std::fmt;
use std::pin::Pin;
use std::task::Poll;
-use futures::{Future, FutureExt};
+use futures::{future::BoxFuture, Future, FutureExt};
use reth_primitives::{Address, U256};
use serde::{Deserialize, Serialize};
-use crate::eth_provider::error::EthApiError;
+use crate::eth_provider::provider::EthProviderResult;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TokenBalance {
pub token_address: Address,
- pub token_balance: Option,
- pub error: Option,
+ pub token_balance: U256,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -20,22 +20,31 @@ pub struct TokenBalances {
pub token_balances: Vec,
}
-type BalanceOfResult = Result;
-
-#[derive(Debug)]
-pub struct FutureTokenBalance> {
- pub balance: F,
+pub struct TokenBalanceFuture<'a> {
+ pub balance: BoxFuture<'a, EthProviderResult>,
pub token_address: Address,
}
-impl> FutureTokenBalance {
- pub const fn new(balance: F, token_address: Address) -> Self {
- Self { balance, token_address }
+impl<'a> fmt::Debug for TokenBalanceFuture<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("TokenBalanceFuture")
+ .field("balance", &"...")
+ .field("token_address", &self.token_address)
+ .finish()
+ }
+}
+
+impl<'a> TokenBalanceFuture<'a> {
+ pub fn new(balance: F, token_address: Address) -> Self
+ where
+ F: Future> + Send + 'a,
+ {
+ Self { balance: Box::pin(balance), token_address }
}
}
-impl + Unpin> Future for FutureTokenBalance {
- type Output = TokenBalance;
+impl<'a> Future for TokenBalanceFuture<'a> {
+ type Output = EthProviderResult;
fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll {
let balance = self.balance.poll_unpin(cx);
@@ -43,10 +52,8 @@ impl + Unpin> Future for FutureTokenBalance<
match balance {
Poll::Ready(output) => match output {
- Ok(balance) => Poll::Ready(TokenBalance { token_address, token_balance: Some(balance), error: None }),
- Err(error) => {
- Poll::Ready(TokenBalance { token_address, token_balance: None, error: Some(error.to_string()) })
- }
+ Ok(token_balance) => Poll::Ready(Ok(TokenBalance { token_address, token_balance })),
+ Err(err) => Poll::Ready(Err(err)),
},
Poll::Pending => Poll::Pending,
}
diff --git a/src/models/felt.rs b/src/models/felt.rs
index 992a19779..721f7e099 100644
--- a/src/models/felt.rs
+++ b/src/models/felt.rs
@@ -51,7 +51,7 @@ impl TryFrom for Address {
fn try_from(felt: Felt252Wrapper) -> Result {
EthAddress::from_felt(&felt)
.map(|eth_address| Self::from_slice(eth_address.as_bytes()))
- .map_err(|_| EthereumDataFormatError::PrimitiveError)
+ .map_err(|_| EthereumDataFormatError::Primitive)
}
}
@@ -59,7 +59,7 @@ impl TryFrom for Felt252Wrapper {
type Error = EthereumDataFormatError;
fn try_from(value: B256) -> Result {
- Ok(Self(FieldElement::from_bytes_be(value.as_ref()).map_err(|_| EthereumDataFormatError::PrimitiveError)?))
+ Ok(Self(FieldElement::from_bytes_be(value.as_ref()).map_err(|_| EthereumDataFormatError::Primitive)?))
}
}
@@ -67,7 +67,7 @@ impl TryFrom for Felt252Wrapper {
type Error = EthereumDataFormatError;
fn try_from(u256: U256) -> Result {
- Ok(Self(FieldElement::from_bytes_be(&u256.to_be_bytes()).map_err(|_| EthereumDataFormatError::PrimitiveError)?))
+ Ok(Self(FieldElement::from_bytes_be(&u256.to_be_bytes()).map_err(|_| EthereumDataFormatError::Primitive)?))
}
}
@@ -110,7 +110,7 @@ macro_rules! into_via_try_wrapper {
($val: expr) => {{
let intermediate: Result<_, $crate::eth_provider::error::EthereumDataFormatError> =
TryInto::<$crate::models::felt::Felt252Wrapper>::try_into($val)
- .map_err(|_| $crate::eth_provider::error::EthereumDataFormatError::PrimitiveError)
+ .map_err(|_| $crate::eth_provider::error::EthereumDataFormatError::Primitive)
.map(Into::into);
intermediate
}};
@@ -146,7 +146,7 @@ mod tests {
}
#[test]
- #[should_panic(expected = "PrimitiveError")]
+ #[should_panic(expected = "Primitive")]
fn test_address_try_from_felt_should_fail() {
// Given
let address: Felt252Wrapper = FieldElement::from_hex_be(OVERFLOW_ADDRESS).unwrap().into();
@@ -169,7 +169,7 @@ mod tests {
}
#[test]
- #[should_panic(expected = "PrimitiveError")]
+ #[should_panic(expected = "Primitive")]
fn test_felt_try_from_b256_should_fail() {
// Given
let hash = B256::from_str(OVERFLOW_FELT).unwrap();
@@ -192,7 +192,7 @@ mod tests {
}
#[test]
- #[should_panic(expected = "PrimitiveError")]
+ #[should_panic(expected = "Primitive")]
fn test_felt_try_from_u256_should_fail() {
// Given
let hash = U256::from_str_radix(OVERFLOW_FELT, 16).unwrap();
diff --git a/src/models/transaction.rs b/src/models/transaction.rs
index 31dd40019..a9e8fb74a 100644
--- a/src/models/transaction.rs
+++ b/src/models/transaction.rs
@@ -28,7 +28,7 @@ pub(crate) fn validate_transaction(transaction_signed: &TransactionSigned, chain
}
// Recover the signer from the transaction
- let _ = transaction_signed.recover_signer().ok_or(SignatureError::RecoveryError)?;
+ let _ = transaction_signed.recover_signer().ok_or(SignatureError::Recovery)?;
// Assert the chain is correct
let maybe_chain_id = transaction_signed.chain_id();
diff --git a/src/test_utils/katana/mod.rs b/src/test_utils/katana/mod.rs
index 4e6da749b..6c9b5e8d7 100644
--- a/src/test_utils/katana/mod.rs
+++ b/src/test_utils/katana/mod.rs
@@ -17,7 +17,7 @@ use reth_rpc_types::Log;
use starknet::providers::jsonrpc::HttpTransport;
use starknet::providers::JsonRpcClient;
-use crate::eth_provider::database::ethereum::EthereumDatabase;
+use crate::eth_provider::database::ethereum::EthereumTransactionStore;
use crate::eth_provider::database::filter;
use crate::eth_provider::database::filter::format_hex;
use crate::eth_provider::database::filter::EthDatabaseFilterBuilder;
@@ -64,7 +64,7 @@ pub fn katana_config() -> StarknetConfig {
/// Returns a `TestSequencer` configured for Kakarot.
#[cfg(any(test, feature = "arbitrary", feature = "testing"))]
async fn katana_sequencer() -> TestSequencer {
- TestSequencer::start(SequencerConfig { no_mining: false, block_time: None, messaging: None }, katana_config()).await
+ TestSequencer::start(SequencerConfig { no_mining: false, block_time: None }, katana_config()).await
}
/// Represents the Katana test environment.
diff --git a/src/tracing/database.rs b/src/tracing/database.rs
index c53b340f7..7faf54ee0 100644
--- a/src/tracing/database.rs
+++ b/src/tracing/database.rs
@@ -95,7 +95,7 @@ impl Database for EthDatabaseSnapshot {
return Ok(*hash);
}
- let block_number = number.try_into().map_err(|_| EthereumDataFormatError::PrimitiveError)?;
+ let block_number = number.try_into().map_err(|_| EthereumDataFormatError::Primitive)?;
let hash = Handle::current().block_on(async {
let hash = cache
.db
diff --git a/src/tracing/mod.rs b/src/tracing/mod.rs
index 6caea7778..bebc8882b 100644
--- a/src/tracing/mod.rs
+++ b/src/tracing/mod.rs
@@ -271,7 +271,7 @@ impl Tracer {
/// Returns the environment with the transaction env updated to the given transaction.
fn env_with_tx(env: &EnvWithHandlerCfg, tx: reth_rpc_types::Transaction) -> TracerResult {
// Convert the transaction to an ec recovered transaction and update the env with it.
- let tx_ec_recovered = tx.try_into().map_err(|_| EthereumDataFormatError::TransactionConversionError)?;
+ let tx_ec_recovered = tx.try_into().map_err(|_| EthereumDataFormatError::TransactionConversion)?;
let tx_env = tx_env_with_recovered(&tx_ec_recovered);
Ok(EnvWithHandlerCfg {
diff --git a/tests/tests/alchemy_api.rs b/tests/tests/alchemy_api.rs
index 7d01cd9e7..cdac4baed 100644
--- a/tests/tests/alchemy_api.rs
+++ b/tests/tests/alchemy_api.rs
@@ -54,7 +54,7 @@ async fn test_token_balances(#[future] erc20: (Katana, KakarotEvmContract), _set
let raw: Value = serde_json::from_str(&response).expect("Failed to deserialize response body");
let balances: TokenBalances =
serde_json::from_value(raw.get("result").cloned().unwrap()).expect("Failed to deserialize response body");
- let erc20_balance = balances.token_balances[0].token_balance.expect("Failed to get ERC20 balance");
+ let erc20_balance = balances.token_balances[0].token_balance;
assert_eq!(amount, erc20_balance);
drop(server_handle);
diff --git a/tests/tests/eth_provider.rs b/tests/tests/eth_provider.rs
index 6d0f41053..2df400e45 100644
--- a/tests/tests/eth_provider.rs
+++ b/tests/tests/eth_provider.rs
@@ -4,7 +4,7 @@ use std::str::FromStr;
use std::sync::Arc;
use kakarot_rpc::eth_provider::constant::{MAX_LOGS, STARKNET_MODULUS, TRANSACTION_MAX_RETRIES};
-use kakarot_rpc::eth_provider::database::ethereum::EthereumDatabase;
+use kakarot_rpc::eth_provider::database::ethereum::EthereumTransactionStore;
use kakarot_rpc::eth_provider::database::types::transaction::StoredPendingTransaction;
use kakarot_rpc::eth_provider::provider::EthereumProvider;
use kakarot_rpc::models::felt::Felt252Wrapper;